From 66298a549a5eb5e5505bb240b8d12fd4e0ef137f Mon Sep 17 00:00:00 2001 From: Piotr Chabelski Date: Wed, 10 Jul 2024 10:31:42 +0200 Subject: [PATCH] Add a command line option & directive for enabling/disabling Scala Native multithreading (#3011) --- .../ScalaNativeUsingDirectiveTests.scala | 13 +++++ .../commands/shared/ScalaNativeOptions.scala | 7 ++- .../cli/commands/shared/SharedOptions.scala | 1 + .../directives/ScalaNative.scala | 4 +- .../RunScalaNativeTestDefinitions.scala | 56 +++++++++++++++++++ .../build/options/ScalaNativeOptions.scala | 7 ++- website/docs/reference/cli-options.md | 4 ++ .../reference/scala-command/cli-options.md | 6 ++ .../scala-command/runner-specification.md | 36 ++++++++++++ 9 files changed, 131 insertions(+), 3 deletions(-) diff --git a/modules/build/src/test/scala/scala/build/tests/ScalaNativeUsingDirectiveTests.scala b/modules/build/src/test/scala/scala/build/tests/ScalaNativeUsingDirectiveTests.scala index 2cd08b8a90..07d3c8bd99 100644 --- a/modules/build/src/test/scala/scala/build/tests/ScalaNativeUsingDirectiveTests.scala +++ b/modules/build/src/test/scala/scala/build/tests/ScalaNativeUsingDirectiveTests.scala @@ -236,4 +236,17 @@ class ScalaNativeUsingDirectiveTests extends TestUtil.ScalaCliBuildSuite { ) } } + + for { multithreadingDirective <- Seq("`native-multithreading`", "nativeMultithreading") } + test(s"ScalaNativeOptions for $multithreadingDirective") { + val inputs = TestInputs( + os.rel / "p.sc" -> + s"""//> using $multithreadingDirective + |def foo() = println("hello foo") + |""".stripMargin + ) + inputs.withLoadedBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) => + assert(maybeBuild.options.scalaNativeOptions.multithreading.get) + } + } } diff --git a/modules/cli/src/main/scala/scala/cli/commands/shared/ScalaNativeOptions.scala b/modules/cli/src/main/scala/scala/cli/commands/shared/ScalaNativeOptions.scala index 52395d05ef..92fd5bc234 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/shared/ScalaNativeOptions.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/shared/ScalaNativeOptions.scala @@ -70,7 +70,12 @@ final case class ScalaNativeOptions( @Group(HelpGroup.ScalaNative.toString) @HelpMessage("Embed resources into the Scala Native binary (can be read with the Java resources API)") @Tag(tags.should) - embedResources: Option[Boolean] = None + embedResources: Option[Boolean] = None, + + @Group(HelpGroup.ScalaNative.toString) + @HelpMessage("Enable/disable Scala Native multithreading support") + @Tag(tags.should) + nativeMultithreading: Option[Boolean] = None ) // format: on diff --git a/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala b/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala index 21299dd217..026c692347 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala @@ -285,6 +285,7 @@ final case class SharedOptions( compileDefaults = nativeCompileDefaults, embedResources = embedResources, buildTargetStr = nativeTarget, + multithreading = nativeMultithreading, maxDefaultNativeVersions = maxDefaultScalaNativeVersions ) } diff --git a/modules/directives/src/main/scala/scala/build/preprocessing/directives/ScalaNative.scala b/modules/directives/src/main/scala/scala/build/preprocessing/directives/ScalaNative.scala index 7fc94c9547..e9aae78f90 100644 --- a/modules/directives/src/main/scala/scala/build/preprocessing/directives/ScalaNative.scala +++ b/modules/directives/src/main/scala/scala/build/preprocessing/directives/ScalaNative.scala @@ -46,6 +46,7 @@ final case class ScalaNative( nativeClangPP: Option[String] = None, nativeEmbedResources: Option[Boolean] = None, nativeTarget: Option[String] = None, + nativeMultithreading: Option[Boolean] = None ) extends HasBuildOptions { // format: on def buildOptions: Either[BuildException, BuildOptions] = { @@ -59,7 +60,8 @@ final case class ScalaNative( clang = nativeClang, clangpp = nativeClangPP, embedResources = nativeEmbedResources, - buildTargetStr = nativeTarget + buildTargetStr = nativeTarget, + multithreading = nativeMultithreading ) val buildOpt = BuildOptions(scalaNativeOptions = nativeOptions) Right(buildOpt) diff --git a/modules/integration/src/test/scala/scala/cli/integration/RunScalaNativeTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/RunScalaNativeTestDefinitions.scala index 811f7b2ccb..142fa231fc 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/RunScalaNativeTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/RunScalaNativeTestDefinitions.scala @@ -401,4 +401,60 @@ trait RunScalaNativeTestDefinitions { _: RunTestDefinitions => } } } + + for { + expectedMultithreadingState <- Seq(true, false) + // multithreading should be enabled by Scala Native by default + setExplicitly <- if (expectedMultithreadingState) Seq(true, false) else Seq(true) + useDirective <- if (setExplicitly) Seq(true, false) else Seq(false) + directive = + if (useDirective && setExplicitly) + s"//> using nativeMultithreading $expectedMultithreadingState" + else "" + cliOptions = + if (!useDirective && setExplicitly) + Seq(s"--native-multithreading=$expectedMultithreadingState") + else Nil + testDescriptionString = useDirective -> setExplicitly match { + case (_, false) => "(implicitly)" + case (true, true) => "with directive" + case (false, true) => "with command line option" + } + } + test( + s"Scala Native multithreading set to $expectedMultithreadingState $testDescriptionString" + ) { + val fileName = "multithreading.sc" + val expectedOutput = "42" + val threadSleep = "100" + val threadAwait = "2.seconds" + val inputs = TestInputs( + os.rel / fileName -> + s"""$directive + |import scala.concurrent._ + |import scala.concurrent.duration._ + |import ExecutionContext.Implicits.global + |val promise = Promise[Int]() + |val thread = new Thread(new Runnable { + | def run(): Unit = { + | Thread.sleep($threadSleep) + | promise.success($expectedOutput) + | } + | }) + |thread.start() + |val result = Await.result(promise.future, $threadAwait) + |println(result) + |""".stripMargin + ) + inputs.fromRoot { root => + val r = os.proc(TestUtil.cli, extraOptions, fileName, "--native", cliOptions) + .call(cwd = root, stderr = os.Pipe, check = expectedMultithreadingState) + if (!expectedMultithreadingState) expect(r.exitCode == 1) + else { + expect(r.exitCode == 0) + expect(r.out.trim() == expectedOutput) + } + expect(r.err.trim().contains(s"multithreadingEnabled=$expectedMultithreadingState")) + } + } } diff --git a/modules/options/src/main/scala/scala/build/options/ScalaNativeOptions.scala b/modules/options/src/main/scala/scala/build/options/ScalaNativeOptions.scala index 0ca8d2479c..0857a0fd18 100644 --- a/modules/options/src/main/scala/scala/build/options/ScalaNativeOptions.scala +++ b/modules/options/src/main/scala/scala/build/options/ScalaNativeOptions.scala @@ -40,6 +40,7 @@ final case class ScalaNativeOptions( compileDefaults: Option[Boolean] = None, embedResources: Option[Boolean] = None, buildTargetStr: Option[String] = None, + multithreading: Option[Boolean] = None, maxDefaultNativeVersions: List[(String, String)] = Nil ) { @@ -122,6 +123,9 @@ final case class ScalaNativeOptions( } else Nil + private def multithreadingCliOption(): List[String] = + multithreading.toList.flatMap(m => List("--multithreading", m.toString)) + def platformSuffix: String = "native" + ScalaVersion.nativeBinary(finalVersion).getOrElse(finalVersion) @@ -174,7 +178,8 @@ final case class ScalaNativeOptions( linkingCliOptions() ++ compileCliOptions() ++ resourcesCliOptions(resourcesExist) ++ - targetCliOption() + targetCliOption() ++ + multithreadingCliOption() } diff --git a/website/docs/reference/cli-options.md b/website/docs/reference/cli-options.md index 90e45ec408..8c578ee95a 100644 --- a/website/docs/reference/cli-options.md +++ b/website/docs/reference/cli-options.md @@ -1396,6 +1396,10 @@ Build target type Embed resources into the Scala Native binary (can be read with the Java resources API) +### `--native-multithreading` + +Enable/disable Scala Native multithreading support + ## Scalac options Available in commands: diff --git a/website/docs/reference/scala-command/cli-options.md b/website/docs/reference/scala-command/cli-options.md index ea4fe08153..8c4fadd0cb 100644 --- a/website/docs/reference/scala-command/cli-options.md +++ b/website/docs/reference/scala-command/cli-options.md @@ -900,6 +900,12 @@ Build target type Embed resources into the Scala Native binary (can be read with the Java resources API) +### `--native-multithreading` + +`SHOULD have` per Scala Runner specification + +Enable/disable Scala Native multithreading support + ## Scalac options Available in commands: diff --git a/website/docs/reference/scala-command/runner-specification.md b/website/docs/reference/scala-command/runner-specification.md index 3056b233c0..918435f1f5 100644 --- a/website/docs/reference/scala-command/runner-specification.md +++ b/website/docs/reference/scala-command/runner-specification.md @@ -204,6 +204,10 @@ Build target type Embed resources into the Scala Native binary (can be read with the Java resources API) +**--native-multithreading** + +Enable/disable Scala Native multithreading support + **--repository** Add repositories for dependency resolution. @@ -967,6 +971,10 @@ Build target type Embed resources into the Scala Native binary (can be read with the Java resources API) +**--native-multithreading** + +Enable/disable Scala Native multithreading support + **--repository** Add repositories for dependency resolution. @@ -1532,6 +1540,10 @@ Build target type Embed resources into the Scala Native binary (can be read with the Java resources API) +**--native-multithreading** + +Enable/disable Scala Native multithreading support + **--repository** Add repositories for dependency resolution. @@ -2129,6 +2141,10 @@ Build target type Embed resources into the Scala Native binary (can be read with the Java resources API) +**--native-multithreading** + +Enable/disable Scala Native multithreading support + **--repository** Add repositories for dependency resolution. @@ -2739,6 +2755,10 @@ Build target type Embed resources into the Scala Native binary (can be read with the Java resources API) +**--native-multithreading** + +Enable/disable Scala Native multithreading support + **--repository** Add repositories for dependency resolution. @@ -3325,6 +3345,10 @@ Build target type Embed resources into the Scala Native binary (can be read with the Java resources API) +**--native-multithreading** + +Enable/disable Scala Native multithreading support + **--repository** Add repositories for dependency resolution. @@ -3948,6 +3972,10 @@ Build target type Embed resources into the Scala Native binary (can be read with the Java resources API) +**--native-multithreading** + +Enable/disable Scala Native multithreading support + **--repository** Add repositories for dependency resolution. @@ -4622,6 +4650,10 @@ Build target type Embed resources into the Scala Native binary (can be read with the Java resources API) +**--native-multithreading** + +Enable/disable Scala Native multithreading support + **--repository** Add repositories for dependency resolution. @@ -5537,6 +5569,10 @@ Build target type Embed resources into the Scala Native binary (can be read with the Java resources API) +**--native-multithreading** + +Enable/disable Scala Native multithreading support + **--repository** Add repositories for dependency resolution.