diff --git a/core/jvm/src/main/scala/cats/effect/IOApp.scala b/core/jvm/src/main/scala/cats/effect/IOApp.scala index 0dc1a06530e..c1cf74eb22e 100644 --- a/core/jvm/src/main/scala/cats/effect/IOApp.scala +++ b/core/jvm/src/main/scala/cats/effect/IOApp.scala @@ -393,7 +393,9 @@ trait IOApp { ) val (blocking, blockDown) = - IORuntime.createDefaultBlockingExecutionContext() + IORuntime.createDefaultBlockingExecutionContext( + reportFailure = t => reportFailure(t).unsafeRunAndForgetWithoutCallback()(runtime) + ) IORuntime( compute, diff --git a/core/jvm/src/main/scala/cats/effect/unsafe/IORuntimeCompanionPlatform.scala b/core/jvm/src/main/scala/cats/effect/unsafe/IORuntimeCompanionPlatform.scala index 9412fbdbfe0..0c990291f5f 100644 --- a/core/jvm/src/main/scala/cats/effect/unsafe/IORuntimeCompanionPlatform.scala +++ b/core/jvm/src/main/scala/cats/effect/unsafe/IORuntimeCompanionPlatform.scala @@ -174,7 +174,7 @@ private[unsafe] abstract class IORuntimeCompanionPlatform { this: IORuntime.type threadPrefix, blockerThreadPrefix, 60.seconds, - _.printStackTrace(), + t => self.compute.reportFailure(t), false ) @@ -186,7 +186,9 @@ private[unsafe] abstract class IORuntimeCompanionPlatform { this: IORuntime.type createDefaultComputeThreadPool(self(), threads, threadPrefix) def createDefaultBlockingExecutionContext( - threadPrefix: String = "io-blocking"): (ExecutionContext, () => Unit) = { + threadPrefix: String = "io-blocking", + reportFailure: Throwable => Unit = _.printStackTrace() + ): (ExecutionContext, () => Unit) = { val threadCount = new AtomicInteger(0) val executor = Executors.newCachedThreadPool { (r: Runnable) => val t = new Thread(r) @@ -194,7 +196,7 @@ private[unsafe] abstract class IORuntimeCompanionPlatform { this: IORuntime.type t.setDaemon(true) t } - (ExecutionContext.fromExecutor(executor), { () => executor.shutdown() }) + (ExecutionContext.fromExecutor(executor, reportFailure), { () => executor.shutdown() }) } def createDefaultScheduler(threadPrefix: String = "io-scheduler"): (Scheduler, () => Unit) = { diff --git a/core/shared/src/main/scala/cats/effect/IO.scala b/core/shared/src/main/scala/cats/effect/IO.scala index 7138a0b915f..64b897d63ad 100644 --- a/core/shared/src/main/scala/cats/effect/IO.scala +++ b/core/shared/src/main/scala/cats/effect/IO.scala @@ -962,7 +962,7 @@ sealed abstract class IO[+A] private () extends IOPlatform[A] { cb(Left(new CancellationException("The fiber was canceled"))), t => { if (!NonFatal(t)) { - t.printStackTrace() + runtime.compute.reportFailure(t) } cb(Left(t)) }, @@ -977,7 +977,7 @@ sealed abstract class IO[+A] private () extends IOPlatform[A] { cb(Outcome.canceled), t => { if (!NonFatal(t)) { - t.printStackTrace() + runtime.compute.reportFailure(t) } cb(Outcome.errored(t)) }, @@ -1002,7 +1002,7 @@ sealed abstract class IO[+A] private () extends IOPlatform[A] { if (NonFatal(t)) { if (runtime.config.reportUnhandledFiberErrors) runtime.compute.reportFailure(t) - } else { t.printStackTrace() } + } else { runtime.compute.reportFailure(t) } }, _ => ()) () diff --git a/std/shared/src/main/scala/cats/effect/std/Dispatcher.scala b/std/shared/src/main/scala/cats/effect/std/Dispatcher.scala index b96b20fd34a..c1cce2a1895 100644 --- a/std/shared/src/main/scala/cats/effect/std/Dispatcher.scala +++ b/std/shared/src/main/scala/cats/effect/std/Dispatcher.scala @@ -31,7 +31,6 @@ import cats.effect.kernel.{ Sync } import cats.effect.kernel.syntax.all._ -import cats.effect.std.Dispatcher.parasiticEC import cats.syntax.all._ import scala.annotation.tailrec @@ -88,22 +87,21 @@ trait Dispatcher[F[_]] extends DispatcherPlatform[F] { case _ => () }(parasiticEC) - protected def reportFailure(t: Throwable): Unit = - t.printStackTrace() + protected def reportFailure(t: Throwable): Unit // package-private because it's just an internal utility which supports specific implementations // anyone who needs this type of thing should use unsafeToFuture and then onComplete private[std] def unsafeRunAsync[A](fa: F[A])(cb: Either[Throwable, A] => Unit): Unit = unsafeToFuture(fa).onComplete(t => cb(t.toEither))(parasiticEC) -} - -object Dispatcher { private val parasiticEC: ExecutionContext = new ExecutionContext { def execute(runnable: Runnable) = runnable.run() - def reportFailure(t: Throwable) = t.printStackTrace() + def reportFailure(t: Throwable) = Dispatcher.this.reportFailure(t) } +} + +object Dispatcher { private[this] val Cpus: Int = Runtime.getRuntime().availableProcessors()