Skip to content

Commit 60a0934

Browse files
committed
Add setters for the failure and starvation reporters in IOApp
1 parent dddaae1 commit 60a0934

File tree

3 files changed

+159
-8
lines changed

3 files changed

+159
-8
lines changed

core/js/src/main/scala/cats/effect/IOApp.scala

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package cats.effect
1818

1919
import cats.effect.metrics.{CpuStarvationWarningMetrics, JsCpuStarvationMetrics}
20-
import cats.effect.std.Console
2120
import cats.effect.tracing.TracingConstants._
2221

2322
import scala.concurrent.CancellationException
@@ -147,6 +146,14 @@ trait IOApp {
147146

148147
private[this] var _runtime: unsafe.IORuntime = null
149148

149+
@volatile
150+
private[this] var _failureReporter: Throwable => IO[Unit] =
151+
err => IO.consoleForIO.printStackTrace(err)
152+
153+
@volatile
154+
private[this] var _onCpuStarvationReporter: CpuStarvationWarningMetrics => IO[Unit] =
155+
metrics => CpuStarvationCheck.logWarning(metrics)
156+
150157
/**
151158
* The runtime which will be used by `IOApp` to evaluate the [[IO]] produced by the `run`
152159
* method. This may be overridden by `IOApp` implementations which have extremely specialized
@@ -175,15 +182,57 @@ trait IOApp {
175182
* runtime. With that said, some care should be taken to avoid raising unhandled errors as a
176183
* result of handling unhandled errors, since that will result in the obvious chaos.
177184
*/
185+
@deprecatedOverriding("Use setFailureReporter instead", "3.6.0")
178186
protected def reportFailure(err: Throwable): IO[Unit] =
179-
Console[IO].printStackTrace(err)
187+
IO(_failureReporter).flatMap(_.apply(err))
188+
189+
/**
190+
* Configures the action to perform when unhandled errors are caught by the runtime.
191+
*
192+
* An unhandled error is an error that is raised (and not handled) on a Fiber that nobody is
193+
* joining.
194+
*
195+
* For example:
196+
*
197+
* {{{
198+
* import scala.concurrent.duration._
199+
* override def run: IO[Unit] = IO(throw new Exception("")).start *> IO.sleep(1.second)
200+
* }}}
201+
*
202+
* In this case, the exception is raised on a Fiber with no listeners. Nobody would be
203+
* notified about that error. Therefore it is unhandled, and it goes through the reportFailure
204+
* mechanism.
205+
*
206+
* By default, the runtime simply delegates to [[cats.effect.std.Console!.printStackTrace]].
207+
* It is safe to perform any `IO` action within this handler; it will not block the progress
208+
* of the runtime. With that said, some care should be taken to avoid raising unhandled errors
209+
* as a result of handling unhandled errors, since that will result in the obvious chaos.
210+
*/
211+
protected final def setFailureReporter(
212+
reporter: Throwable => IO[Unit]
213+
): IO[Unit] =
214+
IO {
215+
_failureReporter = reporter
216+
}
180217

181218
/**
182219
* Defines what to do when CpuStarvationCheck is triggered. Defaults to log a warning to
183220
* System.err.
184221
*/
222+
@deprecatedOverriding("Use setOnCpuStarvationReporter instead", "3.6.0")
185223
protected def onCpuStarvationWarn(metrics: CpuStarvationWarningMetrics): IO[Unit] =
186-
CpuStarvationCheck.logWarning(metrics)
224+
IO(_onCpuStarvationReporter).flatMap(_.apply(metrics))
225+
226+
/**
227+
* Configures what to do when CpuStarvationCheck is triggered. By default the runtime logs a
228+
* warning to System.err.
229+
*/
230+
protected final def setOnCpuStarvationReporter(
231+
reporter: CpuStarvationWarningMetrics => IO[Unit]
232+
): IO[Unit] =
233+
IO {
234+
_onCpuStarvationReporter = reporter
235+
}
187236

188237
/**
189238
* The entry point for your application. Will be called by the runtime when the process is

core/jvm/src/main/scala/cats/effect/IOApp.scala

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package cats.effect
1818

1919
import cats.effect.metrics.{CpuStarvationWarningMetrics, JvmCpuStarvationMetrics}
20-
import cats.effect.std.Console
2120
import cats.effect.tracing.TracingConstants._
2221
import cats.syntax.all._
2322

@@ -144,6 +143,14 @@ trait IOApp {
144143

145144
private[this] var _runtime: unsafe.IORuntime = null
146145

146+
@volatile
147+
private[this] var _failureReporter: Throwable => IO[Unit] =
148+
err => IO.consoleForIO.printStackTrace(err)
149+
150+
@volatile
151+
private[this] var _onCpuStarvationReporter: CpuStarvationWarningMetrics => IO[Unit] =
152+
metrics => CpuStarvationCheck.logWarning(metrics)
153+
147154
/**
148155
* The runtime which will be used by `IOApp` to evaluate the [[IO]] produced by the `run`
149156
* method. This may be overridden by `IOApp` implementations which have extremely specialized
@@ -233,7 +240,7 @@ trait IOApp {
233240
)
234241

235242
/**
236-
* Configures the action to perform when unhandled errors are caught by the runtime. An
243+
* Configures the action to perform when unhandled errors are caught by the runtime.An
237244
* unhandled error is an error that is raised (and not handled) on a Fiber that nobody is
238245
* joining.
239246
*
@@ -254,8 +261,38 @@ trait IOApp {
254261
* should be taken to avoid raising unhandled errors as a result of handling unhandled errors,
255262
* since that will result in the obvious chaos.
256263
*/
264+
@deprecatedOverriding("Use setFailureReporter instead", "3.6.0")
257265
protected def reportFailure(err: Throwable): IO[Unit] =
258-
Console[IO].printStackTrace(err)
266+
IO(_failureReporter).flatMap(_.apply(err))
267+
268+
/**
269+
* Configures the action to perform when unhandled errors are caught by the runtime.
270+
*
271+
* An unhandled error is an error that is raised (and not handled) on a Fiber that nobody is
272+
* joining.
273+
*
274+
* For example:
275+
*
276+
* {{{
277+
* import scala.concurrent.duration._
278+
* override def run: IO[Unit] = IO(throw new Exception("")).start *> IO.sleep(1.second)
279+
* }}}
280+
*
281+
* In this case, the exception is raised on a Fiber with no listeners. Nobody would be
282+
* notified about that error. Therefore it is unhandled, and it goes through the reportFailure
283+
* mechanism.
284+
*
285+
* By default, the runtime simply delegates to [[cats.effect.std.Console!.printStackTrace]].
286+
* It is safe to perform any `IO` action within this handler; it will not block the progress
287+
* of the runtime. With that said, some care should be taken to avoid raising unhandled errors
288+
* as a result of handling unhandled errors, since that will result in the obvious chaos.
289+
*/
290+
protected final def setFailureReporter(
291+
reporter: Throwable => IO[Unit]
292+
): IO[Unit] =
293+
IO {
294+
_failureReporter = reporter
295+
}
259296

260297
/**
261298
* Configures whether to enable blocked thread detection. This is relatively expensive so is
@@ -329,8 +366,20 @@ trait IOApp {
329366
* Defines what to do when CpuStarvationCheck is triggered. Defaults to log a warning to
330367
* System.err.
331368
*/
369+
@deprecatedOverriding("Use setOnCpuStarvationReporter instead", "3.6.0")
332370
protected def onCpuStarvationWarn(metrics: CpuStarvationWarningMetrics): IO[Unit] =
333-
CpuStarvationCheck.logWarning(metrics)
371+
IO(_onCpuStarvationReporter).flatMap(_.apply(metrics))
372+
373+
/**
374+
* Configures what to do when CpuStarvationCheck is triggered. By default the runtime logs a
375+
* warning to System.err.
376+
*/
377+
protected final def setOnCpuStarvationReporter(
378+
reporter: CpuStarvationWarningMetrics => IO[Unit]
379+
): IO[Unit] =
380+
IO {
381+
_onCpuStarvationReporter = reporter
382+
}
334383

335384
/**
336385
* Defines what to do when IOApp detects that `main` is being invoked on a `Thread` which

core/native/src/main/scala/cats/effect/IOApp.scala

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,14 @@ trait IOApp {
146146

147147
private[this] var _runtime: unsafe.IORuntime = null
148148

149+
@volatile
150+
private[this] var _failureReporter: Throwable => IO[Unit] =
151+
err => IO.consoleForIO.printStackTrace(err)
152+
153+
@volatile
154+
private[this] var _onCpuStarvationReporter: CpuStarvationWarningMetrics => IO[Unit] =
155+
metrics => CpuStarvationCheck.logWarning(metrics)
156+
149157
/**
150158
* The runtime which will be used by `IOApp` to evaluate the [[IO]] produced by the `run`
151159
* method. This may be overridden by `IOApp` implementations which have extremely specialized
@@ -167,12 +175,57 @@ trait IOApp {
167175
*/
168176
protected def runtimeConfig: unsafe.IORuntimeConfig = unsafe.IORuntimeConfig()
169177

178+
// Currently unused!
179+
private def reportFailure(err: Throwable): IO[Unit] =
180+
IO(_failureReporter).flatMap(_.apply(err))
181+
182+
/**
183+
* Configures the action to perform when unhandled errors are caught by the runtime.
184+
*
185+
* An unhandled error is an error that is raised (and not handled) on a Fiber that nobody is
186+
* joining.
187+
*
188+
* For example:
189+
*
190+
* {{{
191+
* import scala.concurrent.duration._
192+
* override def run: IO[Unit] = IO(throw new Exception("")).start *> IO.sleep(1.second)
193+
* }}}
194+
*
195+
* In this case, the exception is raised on a Fiber with no listeners. Nobody would be
196+
* notified about that error. Therefore it is unhandled, and it goes through the reportFailure
197+
* mechanism.
198+
*
199+
* By default, the runtime simply delegates to [[cats.effect.std.Console!.printStackTrace]].
200+
* It is safe to perform any `IO` action within this handler; it will not block the progress
201+
* of the runtime. With that said, some care should be taken to avoid raising unhandled errors
202+
* as a result of handling unhandled errors, since that will result in the obvious chaos.
203+
*/
204+
protected final def setFailureReporter(
205+
reporter: Throwable => IO[Unit]
206+
): IO[Unit] =
207+
IO {
208+
_failureReporter = reporter
209+
}
210+
170211
/**
171212
* Defines what to do when CpuStarvationCheck is triggered. Defaults to log a warning to
172213
* System.err.
173214
*/
215+
@deprecatedOverriding("Use setOnCpuStarvationReporter instead", "3.6.0")
174216
protected def onCpuStarvationWarn(metrics: CpuStarvationWarningMetrics): IO[Unit] =
175-
CpuStarvationCheck.logWarning(metrics)
217+
IO(_onCpuStarvationReporter).flatMap(_.apply(metrics))
218+
219+
/**
220+
* Configures what to do when CpuStarvationCheck is triggered. By default the runtime logs a
221+
* warning to System.err.
222+
*/
223+
protected final def setOnCpuStarvationReporter(
224+
reporter: CpuStarvationWarningMetrics => IO[Unit]
225+
): IO[Unit] =
226+
IO {
227+
_onCpuStarvationReporter = reporter
228+
}
176229

177230
/**
178231
* The [[unsafe.PollingSystem]] used by the [[runtime]] which will evaluate the [[IO]]

0 commit comments

Comments
 (0)