Skip to content

Commit

Permalink
Print warning when we detect a non-main-thread main
Browse files Browse the repository at this point in the history
This should help people not run facefirst into sbt/sbt#6242
It's a frequent question for new users
  • Loading branch information
Daenyth committed Aug 4, 2023
1 parent ccb8ff2 commit 94a7b60
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 1 deletion.
24 changes: 24 additions & 0 deletions core/jvm/src/main/scala/cats/effect/IOApp.scala
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,29 @@ 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`
*/
private def onNonMainThreadDetected(): Unit = {
val shouldPrint =
Option(System.getProperty("cats.effect.warnOnNonMainThreadDetected"))
.map(_.equalsIgnoreCase("true"))
.getOrElse(true)
if (shouldPrint)
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 `sbt run` with `fork := false`.
|Set `Compile / run / fork := true` in this project to resolve this.
|""".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
Expand All @@ -333,6 +356,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
Expand Down
3 changes: 2 additions & 1 deletion docs/core/io-runtime-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ This can be done for example with the [EnvironmentPlugin for Webpack](https://we
| `cats.effect.detectBlockedThreads` <br/> N/A | `Boolean` (`false`) | Whether or not we should detect blocked threads. |
| `cats.effect.logNonDaemonThreadsOnExit` <br/> N/A | `Boolean` (`true`) | Whether or not we should check for non-daemon threads on JVM exit. |
| `cats.effect.logNonDaemonThreads.sleepIntervalMillis` <br/> N/A | `Long` (`10000L`) | Time to sleep between checking for presence of non-daemon threads. |
| `cats.effect.cancelation.check.threshold ` <br/> `CATS_EFFECT_CANCELATION_CHECK_THRESHOLD` | `Int` (`512`) | Configure how often cancellation is checked. By default, every 512 iterations of the run loop. |
| `cats.effect.warnOnNonMainThreadDetected` <br/> N/A | `Boolean` (`true`) | Print a warning message when IOApp `main` runs on a non-main thread |
| `cats.effect.cancelation.check.threshold` <br/> `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` <br/> `CATS_EFFECT_AUTO_YIELD_THRESHOLD_MULTIPLIER` | `Int` (`2`) | `autoYieldThreshold = autoYieldThresholdMultiplier x cancelationCheckThreshold`. See [thread model](../thread-model.md). |
| `cats.effect.tracing.exceptions.enhanced` <br/> `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` <br/> `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. |
Expand Down

0 comments on commit 94a7b60

Please sign in to comment.