Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native signal handling and safe IOApp cancelation #3695

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions .cirrus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ jvm_highcore_task:
memory: 8G
matrix:
- name: JVM high-core-count 2.13
script: sbt '++ 2.13' testsJVM/test
script: sbt '++ 2.13' testsJVM/test ioAppTestsJVM/test
- name: JVM high-core-count 3
script: sbt '++ 3' testsJVM/test
script: sbt '++ 3' testsJVM/test ioAppTestsJVM/test

jvm_arm_highcore_task:
arm_container:
Expand All @@ -16,9 +16,9 @@ jvm_arm_highcore_task:
memory: 8G
matrix:
- name: JVM ARM high-core-count 2.13
script: sbt '++ 2.13' testsJVM/test
script: sbt '++ 2.13' testsJVM/test ioAppTestsJVM/test
- name: JVM ARM high-core-count 3
script: sbt '++ 3' testsJVM/test
script: sbt '++ 3' testsJVM/test ioAppTestsJVM/test

jvm_macos_highcore_task:
macos_instance:
Expand All @@ -27,7 +27,7 @@ jvm_macos_highcore_task:
- name: JVM Apple Silicon high-core-count 3
script:
- brew install sbt
- sbt '++ 3' testsJVM/test
- sbt '++ 3' testsJVM/test ioAppTestsJVM/test

native_arm_task:
arm_container:
Expand All @@ -36,7 +36,7 @@ native_arm_task:
memory: 8G
matrix:
- name: Native ARM 3
script: sbt '++ 3' testsNative/test
script: sbt '++ 3' testsNative/test ioAppTestsNative/test

native_macos_task:
macos_instance:
Expand All @@ -45,4 +45,4 @@ native_macos_task:
- name: Native Apple Silicon 3
script:
- brew install sbt
- sbt '++ 3' testsNative/test
- sbt '++ 3' testsNative/test ioAppTestsNative/test
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -547,5 +547,5 @@ jobs:
- name: Submit Dependencies
uses: scalacenter/sbt-dependency-submission@v2
with:
modules-ignore: cats-effect-benchmarks_3 cats-effect-benchmarks_2.12 cats-effect-benchmarks_2.13 cats-effect_3 cats-effect_2.12 cats-effect_2.13 cats-effect-stress-tests_3 cats-effect-stress-tests_2.12 cats-effect-stress-tests_2.13 cats-effect-example_sjs1_3 cats-effect-example_sjs1_2.12 cats-effect-example_sjs1_2.13 rootjs_3 rootjs_2.12 rootjs_2.13 cats-effect-graalvm-example_3 cats-effect-graalvm-example_2.12 cats-effect-graalvm-example_2.13 cats-effect-tests_sjs1_3 cats-effect-tests_sjs1_2.12 cats-effect-tests_sjs1_2.13 rootjvm_3 rootjvm_2.12 rootjvm_2.13 rootnative_3 rootnative_2.12 rootnative_2.13 cats-effect-example_native0.4_3 cats-effect-example_native0.4_2.12 cats-effect-example_native0.4_2.13 cats-effect-example_3 cats-effect-example_2.12 cats-effect-example_2.13 cats-effect-tests_3 cats-effect-tests_2.12 cats-effect-tests_2.13 cats-effect-tests_native0.4_3 cats-effect-tests_native0.4_2.12 cats-effect-tests_native0.4_2.13
modules-ignore: cats-effect-benchmarks_3 cats-effect-benchmarks_2.12 cats-effect-benchmarks_2.13 cats-effect_3 cats-effect_2.12 cats-effect_2.13 cats-effect-stress-tests_3 cats-effect-stress-tests_2.12 cats-effect-stress-tests_2.13 cats-effect-example_sjs1_3 cats-effect-example_sjs1_2.12 cats-effect-example_sjs1_2.13 rootjs_3 rootjs_2.12 rootjs_2.13 ioapptestsnative_3 ioapptestsnative_2.12 ioapptestsnative_2.13 cats-effect-graalvm-example_3 cats-effect-graalvm-example_2.12 cats-effect-graalvm-example_2.13 cats-effect-tests_sjs1_3 cats-effect-tests_sjs1_2.12 cats-effect-tests_sjs1_2.13 rootjvm_3 rootjvm_2.12 rootjvm_2.13 rootnative_3 rootnative_2.12 rootnative_2.13 cats-effect-example_native0.4_3 cats-effect-example_native0.4_2.12 cats-effect-example_native0.4_2.13 cats-effect-example_3 cats-effect-example_2.12 cats-effect-example_2.13 cats-effect-tests_3 cats-effect-tests_2.12 cats-effect-tests_2.13 ioapptestsjvm_3 ioapptestsjvm_2.12 ioapptestsjvm_2.13 ioapptestsjs_3 ioapptestsjs_2.12 ioapptestsjs_2.13 cats-effect-tests_native0.4_3 cats-effect-tests_native0.4_2.12 cats-effect-tests_native0.4_2.13
configs-ignore: test scala-tool scala-doc-tool test-internal
92 changes: 64 additions & 28 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -273,10 +273,6 @@ lazy val useJSEnv =
settingKey[JSEnv]("Use Node.js or a headless browser for running Scala.js tests")
Global / useJSEnv := NodeJS

lazy val testJSIOApp =
settingKey[Boolean]("Whether to test JVM (false) or Node.js (true) in IOAppSpec")
Global / testJSIOApp := false

ThisBuild / jsEnv := {
useJSEnv.value match {
case NodeJS => new NodeJSEnv(NodeJSEnv.Config().withSourceMap(true))
Expand Down Expand Up @@ -327,7 +323,16 @@ tlReplaceCommandAlias(
"; root/clean; +root/headerCreate; root/scalafixAll; scalafmtSbt; +root/scalafmtAll")

val jsProjects: Seq[ProjectReference] =
Seq(kernel.js, kernelTestkit.js, laws.js, core.js, testkit.js, testsJS, std.js, example.js)
Seq(
kernel.js,
kernelTestkit.js,
laws.js,
core.js,
testkit.js,
tests.js,
ioAppTestsJS,
std.js,
example.js)

val nativeProjects: Seq[ProjectReference] =
Seq(
Expand All @@ -337,6 +342,7 @@ val nativeProjects: Seq[ProjectReference] =
core.native,
testkit.native,
tests.native,
ioAppTestsNative,
std.native,
example.native)

Expand Down Expand Up @@ -369,7 +375,7 @@ lazy val rootJVM = project
laws.jvm,
core.jvm,
testkit.jvm,
testsJVM,
tests.jvm,
std.jvm,
example.jvm,
graalVMExample,
Expand Down Expand Up @@ -641,7 +647,10 @@ lazy val core = crossProject(JSPlatform, JVMPlatform, NativePlatform)
"cats.effect.IOFiberConstants.ContStateResult"),
// introduced by #3332, polling system
ProblemFilters.exclude[DirectMissingMethodProblem](
"cats.effect.unsafe.IORuntimeBuilder.this")
"cats.effect.unsafe.IORuntimeBuilder.this"),
// introduced by #3695, which enabled fiber dumps on native
ProblemFilters.exclude[MissingClassProblem](
"cats.effect.unsafe.FiberMonitorCompanionPlatform")
) ++ {
if (tlIsScala3.value) {
// Scala 3 specific exclusions
Expand Down Expand Up @@ -798,7 +807,10 @@ lazy val core = crossProject(JSPlatform, JVMPlatform, NativePlatform)
ProblemFilters.exclude[IncompatibleTemplateDefProblem]("cats.effect.CallbackStack"),
// introduced by #3642, which optimized the BatchingMacrotaskExecutor
ProblemFilters.exclude[MissingClassProblem](
"cats.effect.unsafe.BatchingMacrotaskExecutor$executeBatchTaskRunnable$")
"cats.effect.unsafe.BatchingMacrotaskExecutor$executeBatchTaskRunnable$"),
// introduced by #3695, which ported fiber monitoring to Native
// internal API change
ProblemFilters.exclude[MissingClassProblem]("cats.effect.unsafe.ES2021FiberMonitor")
)
},
mimaBinaryIssueFilters ++= {
Expand Down Expand Up @@ -860,7 +872,7 @@ lazy val testkit = crossProject(JSPlatform, JVMPlatform, NativePlatform)
lazy val tests: CrossProject = crossProject(JSPlatform, JVMPlatform, NativePlatform)
.in(file("tests"))
.dependsOn(core, laws % Test, kernelTestkit % Test, testkit % Test)
.enablePlugins(BuildInfoPlugin, NoPublishPlugin)
.enablePlugins(NoPublishPlugin)
.settings(
name := "cats-effect-tests",
libraryDependencies ++= Seq(
Expand All @@ -869,7 +881,6 @@ lazy val tests: CrossProject = crossProject(JSPlatform, JVMPlatform, NativePlatf
"org.typelevel" %%% "discipline-specs2" % DisciplineVersion % Test,
"org.typelevel" %%% "cats-kernel-laws" % CatsVersion % Test
),
buildInfoPackage := "catseffect",
githubWorkflowArtifactUpload := false
)
.jsSettings(
Expand All @@ -879,27 +890,52 @@ lazy val tests: CrossProject = crossProject(JSPlatform, JVMPlatform, NativePlatf
scalacOptions ~= { _.filterNot(_.startsWith("-P:scalajs:mapSourceURI")) }
)
.jvmSettings(
Test / fork := true,
Test / javaOptions += s"-Dsbt.classpath=${(Test / fullClasspath).value.map(_.data.getAbsolutePath).mkString(File.pathSeparator)}"
Test / fork := true
)

lazy val testsJS = tests.js
lazy val testsJVM = tests
.jvm
.enablePlugins(BuildInfoPlugin)
.settings(
Test / compile := {
if (testJSIOApp.value)
(Test / compile).dependsOn(testsJS / Compile / fastOptJS).value
else
(Test / compile).value
},
buildInfoPackage := "cats.effect",
buildInfoKeys += testJSIOApp,
buildInfoKeys +=
"jsRunner" -> (testsJS / Compile / fastOptJS / artifactPath).value
.nativeSettings(
Compile / mainClass := Some("catseffect.examples.NativeRunner")
)

def configureIOAppTests(p: Project): Project =
p.enablePlugins(NoPublishPlugin, BuildInfoPlugin)
.settings(
Test / unmanagedSourceDirectories += (LocalRootProject / baseDirectory).value / "ioapp-tests" / "src" / "test" / "scala",
libraryDependencies += "org.specs2" %%% "specs2-core" % Specs2Version % Test,
buildInfoPackage := "cats.effect",
buildInfoKeys ++= Seq(
"jsRunner" -> (tests.js / Compile / fastOptJS / artifactPath).value,
"nativeRunner" -> (tests.native / Compile / nativeLink / artifactPath).value
)
)

lazy val ioAppTestsJVM =
djspiewak marked this conversation as resolved.
Show resolved Hide resolved
project
.in(file("ioapp-tests/.jvm"))
.configure(configureIOAppTests)
.settings(
buildInfoKeys += "platform" -> "jvm",
Test / fork := true,
Test / javaOptions += s"-Dcatseffect.examples.classpath=${(tests.jvm / Compile / fullClasspath).value.map(_.data.getAbsolutePath).mkString(File.pathSeparator)}"
)

lazy val ioAppTestsJS =
project
.in(file("ioapp-tests/.js"))
.configure(configureIOAppTests)
.settings(
(Test / test) := (Test / test).dependsOn(tests.js / Compile / fastOptJS).value,
buildInfoKeys += "platform" -> "js"
)

lazy val ioAppTestsNative =
project
.in(file("ioapp-tests/.native"))
.configure(configureIOAppTests)
.settings(
(Test / test) := (Test / test).dependsOn(tests.native / Compile / nativeLink).value,
buildInfoKeys += "platform" -> "native"
)

/**
* Implementations lof standard functionality (e.g. Semaphore, Console, Queue) purely in terms
* of the typeclasses, with no dependency on IO. In most cases, the *tests* for these
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,12 @@
* limitations under the License.
*/

package cats.effect.unsafe
package cats.effect
package unsafe

import scala.concurrent.ExecutionContext

private[unsafe] trait FiberMonitorCompanionPlatform {
def apply(compute: ExecutionContext): FiberMonitor = {
val _ = compute
new NoOpFiberMonitor
}
/**
* An introspectable executor that runs fibers. Useful for fiber dumps.
*/
private[unsafe] trait FiberExecutor {
def liveTraces(): Map[IOFiber[_], Trace]
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@
package cats.effect
package unsafe

import scala.collection.mutable
import scala.concurrent.ExecutionContext
import scala.scalajs.{js, LinkingInfo}

private[effect] sealed abstract class FiberMonitor extends FiberMonitorShared {

/**
Expand All @@ -43,24 +39,21 @@ private[effect] sealed abstract class FiberMonitor extends FiberMonitorShared {
def liveFiberSnapshot(print: String => Unit): Unit
}

/**
* Relies on features *standardized* in ES2021, although already offered in many environments
*/
private final class ES2021FiberMonitor(
private final class FiberMonitorImpl(
// A reference to the compute pool of the `IORuntime` in which this suspended fiber bag
// operates. `null` if the compute pool of the `IORuntime` is not a `BatchingMacrotaskExecutor`.
private[this] val compute: BatchingMacrotaskExecutor
// operates. `null` if the compute pool of the `IORuntime` is not a `FiberExecutor`.
private[this] val compute: FiberExecutor
) extends FiberMonitor {
private[this] val bag = new WeakBag[IOFiber[_]]()

override def monitorSuspended(fiber: IOFiber[_]): WeakBag.Handle =
bag.insert(fiber)

def foreignTraces(): Map[IOFiber[_], Trace] = {
val foreign = mutable.Map.empty[IOFiber[Any], Trace]
private[this] def foreignTraces(): Map[IOFiber[_], Trace] = {
val foreign = Map.newBuilder[IOFiber[_], Trace]
bag.forEach(fiber =>
if (!fiber.isDone) foreign += (fiber.asInstanceOf[IOFiber[Any]] -> fiber.captureTrace()))
foreign.toMap
foreign.result()
}

def liveFiberSnapshot(print: String => Unit): Unit =
Expand All @@ -70,7 +63,7 @@ private final class ES2021FiberMonitor(

// We trust the sources of data in the following order, ordered from
// most trustworthy to least trustworthy.
// 1. Fibers from the macrotask executor
// 1. Fibers from the fiber executor
// 2. Fibers from the foreign fallback weak GC map

val allForeign = rawForeign -- queued.keys
Expand All @@ -92,33 +85,11 @@ private final class ES2021FiberMonitor(

/**
* A no-op implementation of an unordered bag used for tracking asynchronously suspended fiber
* instances on Scala.js. This is used as a fallback.
* instances on Scala Native. This is used as a fallback.
*/
private final class NoOpFiberMonitor extends FiberMonitor {
override def monitorSuspended(fiber: IOFiber[_]): WeakBag.Handle = () => ()
def liveFiberSnapshot(print: String => Unit): Unit = ()
}

private[effect] object FiberMonitor {
def apply(compute: ExecutionContext): FiberMonitor = {
if (LinkingInfo.developmentMode && weakRefsAvailable) {
if (compute.isInstanceOf[BatchingMacrotaskExecutor]) {
val bmec = compute.asInstanceOf[BatchingMacrotaskExecutor]
new ES2021FiberMonitor(bmec)
} else {
new ES2021FiberMonitor(null)
}
} else {
new NoOpFiberMonitor()
}
}

private[this] final val Undefined = "undefined"

/**
* Feature-tests for all the required, well, features :)
*/
private[unsafe] def weakRefsAvailable: Boolean =
js.typeOf(js.Dynamic.global.WeakRef) != Undefined &&
js.typeOf(js.Dynamic.global.FinalizationRegistry) != Undefined
}
private[effect] object FiberMonitor extends FiberMonitorPlatform
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ import scala.util.control.NonFatal
private[effect] final class BatchingMacrotaskExecutor(
batchSize: Int,
reportFailure0: Throwable => Unit
) extends ExecutionContextExecutor {
) extends ExecutionContextExecutor
with FiberExecutor {

private[this] val queueMicrotask: js.Function1[js.Function0[Any], Any] =
if (js.typeOf(js.Dynamic.global.queueMicrotask) == "function")
Expand Down

This file was deleted.

Loading