From bcc460dd8d1263ffcaab8d5d8f064d83f51705cb Mon Sep 17 00:00:00 2001 From: Diogo Canut Date: Sat, 9 Sep 2023 17:13:34 -0300 Subject: [PATCH 1/6] Add warnOnNonMainThreadDetected configuration to IOApp --- core/jvm/src/main/scala/cats/effect/IOApp.scala | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/core/jvm/src/main/scala/cats/effect/IOApp.scala b/core/jvm/src/main/scala/cats/effect/IOApp.scala index 816ad51053..32199bb9c4 100644 --- a/core/jvm/src/main/scala/cats/effect/IOApp.scala +++ b/core/jvm/src/main/scala/cats/effect/IOApp.scala @@ -317,6 +317,17 @@ trait IOApp { protected def onCpuStarvationWarn(metrics: CpuStarvationWarningMetrics): IO[Unit] = CpuStarvationCheck.logWarning(metrics) + /** + * Defines what to do when IOApp detects that `main` is being invoked on a `Thread` which + * isn't the main process thread. This condition can happen when we are running inside of an + * `sbt run` with `fork := false` + */ + def warnOnNonMainThreadDetected: Boolean = + Option(System.getProperty("cats.effect.warnOnNonMainThreadDetected")) + .map(_.equalsIgnoreCase("true")) + .getOrElse(true) + + /** * The entry point for your application. Will be called by the runtime when the process is * started. If the underlying runtime supports it, any arguments passed to the process will be From c564d0c80686cc46a659bae915f2dc3dda9d8a2a Mon Sep 17 00:00:00 2001 From: Diogo Canut Date: Sat, 9 Sep 2023 17:33:51 -0300 Subject: [PATCH 2/6] running prePR --- core/jvm/src/main/scala/cats/effect/IOApp.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/core/jvm/src/main/scala/cats/effect/IOApp.scala b/core/jvm/src/main/scala/cats/effect/IOApp.scala index 32199bb9c4..bc55646f6f 100644 --- a/core/jvm/src/main/scala/cats/effect/IOApp.scala +++ b/core/jvm/src/main/scala/cats/effect/IOApp.scala @@ -327,7 +327,6 @@ trait IOApp { .map(_.equalsIgnoreCase("true")) .getOrElse(true) - /** * The entry point for your application. Will be called by the runtime when the process is * started. If the underlying runtime supports it, any arguments passed to the process will be From 24ce2ca3d3069ceb13f563c1173c39ccc94ae2a7 Mon Sep 17 00:00:00 2001 From: Diogo Canut Date: Sat, 9 Sep 2023 17:42:05 -0300 Subject: [PATCH 3/6] adding onNonMainThreadDetected --- .../jvm/src/main/scala/cats/effect/IOApp.scala | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/core/jvm/src/main/scala/cats/effect/IOApp.scala b/core/jvm/src/main/scala/cats/effect/IOApp.scala index bc55646f6f..884495aaed 100644 --- a/core/jvm/src/main/scala/cats/effect/IOApp.scala +++ b/core/jvm/src/main/scala/cats/effect/IOApp.scala @@ -327,6 +327,23 @@ trait IOApp { .map(_.equalsIgnoreCase("true")) .getOrElse(true) + private def onNonMainThreadDetected(): Unit = { + if (warnOnNonMainThreadDetected) + System + .err + .println( + """|[WARNING] IOApp `main` is running on a thread other than the main thread. + |This may prevent correct resource cleanup after `main` completes. + |This condition could be caused by executing `run` in an interactive sbt session with `fork := false`. + |Set `Compile / run / fork := true` in this project to resolve this. + | + |To silence this warning set the system property: + |`-Dcats.effect.warnOnNonMainThreadDetected=false`. + |""".stripMargin + ) + else () + } + /** * The entry point for your application. Will be called by the runtime when the process is * started. If the underlying runtime supports it, any arguments passed to the process will be @@ -346,6 +363,7 @@ trait IOApp { final def main(args: Array[String]): Unit = { // checked in openjdk 8-17; this attempts to detect when we're running under artificial environments, like sbt val isForked = Thread.currentThread().getId() == 1 + if (!isForked) onNonMainThreadDetected() val installed = if (runtime == null) { import unsafe.IORuntime From a0c7ecf10ef467d1586ee561db58f7e8cca69624 Mon Sep 17 00:00:00 2001 From: Diogo Canut Date: Sat, 9 Sep 2023 17:50:13 -0300 Subject: [PATCH 4/6] adding warnOnNonMainThreadDetected to io-runtime-config --- docs/core/io-runtime-config.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/core/io-runtime-config.md b/docs/core/io-runtime-config.md index 0a48199b48..f107efc750 100644 --- a/docs/core/io-runtime-config.md +++ b/docs/core/io-runtime-config.md @@ -28,6 +28,7 @@ This can be done for example with the [EnvironmentPlugin for Webpack](https://we | `cats.effect.detectBlockedThreads`
N/A | `Boolean` (`false`) | Whether or not we should detect blocked threads. | | `cats.effect.logNonDaemonThreadsOnExit`
N/A | `Boolean` (`true`) | Whether or not we should check for non-daemon threads on JVM exit. | | `cats.effect.logNonDaemonThreads.sleepIntervalMillis`
N/A | `Long` (`10000L`) | Time to sleep between checking for presence of non-daemon threads. | +| `cats.effect.warnOnNonMainThreadDetected`
N/A | `Boolean` (`true`) | Print a warning message when IOApp `main` runs on a non-main thread | | `cats.effect.cancelation.check.threshold `
`CATS_EFFECT_CANCELATION_CHECK_THRESHOLD` | `Int` (`512`) | Configure how often cancellation is checked. By default, every 512 iterations of the run loop. | | `cats.effect.auto.yield.threshold.multiplier`
`CATS_EFFECT_AUTO_YIELD_THRESHOLD_MULTIPLIER` | `Int` (`2`) | `autoYieldThreshold = autoYieldThresholdMultiplier x cancelationCheckThreshold`. See [thread model](../thread-model.md). | | `cats.effect.tracing.exceptions.enhanced`
`CATS_EFFECT_TRACING_EXCEPTIONS_ENHANCED` | `Boolean` (`true`) | Augment the stack traces of caught exceptions to include frames from the asynchronous stack traces. See [tracing](../tracing.md). | From 6feeecf7d6eab812c2e1639d7dc6498bb3e3ca76 Mon Sep 17 00:00:00 2001 From: Diogo Canut Date: Mon, 25 Sep 2023 23:45:31 -0300 Subject: [PATCH 5/6] fix: merge --- docs/core/io-runtime-config.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/core/io-runtime-config.md b/docs/core/io-runtime-config.md index f107efc750..39828ad429 100644 --- a/docs/core/io-runtime-config.md +++ b/docs/core/io-runtime-config.md @@ -29,7 +29,7 @@ This can be done for example with the [EnvironmentPlugin for Webpack](https://we | `cats.effect.logNonDaemonThreadsOnExit`
N/A | `Boolean` (`true`) | Whether or not we should check for non-daemon threads on JVM exit. | | `cats.effect.logNonDaemonThreads.sleepIntervalMillis`
N/A | `Long` (`10000L`) | Time to sleep between checking for presence of non-daemon threads. | | `cats.effect.warnOnNonMainThreadDetected`
N/A | `Boolean` (`true`) | Print a warning message when IOApp `main` runs on a non-main thread | -| `cats.effect.cancelation.check.threshold `
`CATS_EFFECT_CANCELATION_CHECK_THRESHOLD` | `Int` (`512`) | Configure how often cancellation is checked. By default, every 512 iterations of the run loop. | +| `cats.effect.cancelation.check.threshold`
`CATS_EFFECT_CANCELATION_CHECK_THRESHOLD` | `Int` (`512`) | Configure how often cancellation is checked. By default, every 512 iterations of the run loop. | | `cats.effect.auto.yield.threshold.multiplier`
`CATS_EFFECT_AUTO_YIELD_THRESHOLD_MULTIPLIER` | `Int` (`2`) | `autoYieldThreshold = autoYieldThresholdMultiplier x cancelationCheckThreshold`. See [thread model](../thread-model.md). | | `cats.effect.tracing.exceptions.enhanced`
`CATS_EFFECT_TRACING_EXCEPTIONS_ENHANCED` | `Boolean` (`true`) | Augment the stack traces of caught exceptions to include frames from the asynchronous stack traces. See [tracing](../tracing.md). | | `cats.effect.tracing.buffer.size`
`CATS_EFFECT_TRACING_BUFFER_SIZE` | `Int` (`16`) | Number of stack frames retained in the tracing buffer. Will be rounded up to next power of two. | From f9bd7cb490edb4ffb44594aa664a684c36343481 Mon Sep 17 00:00:00 2001 From: Diogo Canut Date: Tue, 26 Sep 2023 08:12:14 -0300 Subject: [PATCH 6/6] remove stray space --- core/jvm/src/main/scala/cats/effect/IOApp.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/core/jvm/src/main/scala/cats/effect/IOApp.scala b/core/jvm/src/main/scala/cats/effect/IOApp.scala index 9a95bf9775..f9b8198701 100644 --- a/core/jvm/src/main/scala/cats/effect/IOApp.scala +++ b/core/jvm/src/main/scala/cats/effect/IOApp.scala @@ -337,7 +337,6 @@ trait IOApp { * isn't the main process thread. This condition can happen when we are running inside of an * `sbt run` with `fork := false` */ - def warnOnNonMainThreadDetected: Boolean = Option(System.getProperty("cats.effect.warnOnNonMainThreadDetected")) .map(_.equalsIgnoreCase("true"))