Skip to content

Commit

Permalink
Merge pull request #3811 from scott-thomson239/fix/3767
Browse files Browse the repository at this point in the history
Immediately surface fatal errors in `IO.raiseError`
  • Loading branch information
djspiewak authored Nov 23, 2023
2 parents 3589ff9 + febbf1f commit 74b2b74
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 3 deletions.
20 changes: 17 additions & 3 deletions core/shared/src/main/scala/cats/effect/IOFiber.scala
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,11 @@ private final class IOFiber[A](

case 1 =>
val cur = cur0.asInstanceOf[Error]
runLoop(failed(cur.t, 0), nextCancelation, nextAutoCede)
val ex = cur.t
if (!NonFatal(ex))
onFatalFailure(ex)

runLoop(failed(ex, 0), nextCancelation, nextAutoCede)

case 2 =>
val cur = cur0.asInstanceOf[Delay[Any]]
Expand Down Expand Up @@ -315,7 +319,11 @@ private final class IOFiber[A](

case 1 =>
val error = ioe.asInstanceOf[Error]
runLoop(failed(error.t, 0), nextCancelation - 1, nextAutoCede)
val ex = error.t
if (!NonFatal(ex))
onFatalFailure(ex)

runLoop(failed(ex, 0), nextCancelation - 1, nextAutoCede)

case 2 =>
val delay = ioe.asInstanceOf[Delay[Any]]
Expand Down Expand Up @@ -382,7 +390,11 @@ private final class IOFiber[A](

case 1 =>
val error = ioe.asInstanceOf[Error]
runLoop(failed(error.t, 0), nextCancelation - 1, nextAutoCede)
val ex = error.t
if (!NonFatal(ex))
onFatalFailure(ex)

runLoop(failed(ex, 0), nextCancelation - 1, nextAutoCede)

case 2 =>
val delay = ioe.asInstanceOf[Delay[Any]]
Expand Down Expand Up @@ -434,6 +446,8 @@ private final class IOFiber[A](
case 1 =>
val error = ioa.asInstanceOf[Error]
val t = error.t
if (!NonFatal(t))
onFatalFailure(t)
// We need to augment the exception here because it doesn't get
// forwarded to the `failed` path.
Tracing.augmentThrowable(runtime.enhancedExceptions, t, tracingEvents)
Expand Down
4 changes: 4 additions & 0 deletions tests/js/src/main/scala/catseffect/examplesplatform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ package examples {
register(Arguments)
register(NonFatalError)
register(FatalError)
register(RaiseFatalErrorAttempt)
register(RaiseFatalErrorHandle)
register(RaiseFatalErrorMap)
register(RaiseFatalErrorFlatMap)
registerRaw(FatalErrorRaw)
register(Canceled)
registerLazy("catseffect.examples.GlobalRacingInit", GlobalRacingInit)
Expand Down
28 changes: 28 additions & 0 deletions tests/jvm/src/test/scala/cats/effect/IOAppSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,34 @@ class IOAppSpec extends Specification {
h.stderr() must contain("Boom!")
}

"exit on raising a fatal error with attempt" in {
val h = platform(RaiseFatalErrorAttempt, List.empty)
h.awaitStatus() mustEqual 1
h.stderr() must contain("Boom!")
h.stdout() must not(contain("sadness"))
}

"exit on raising a fatal error with handleError" in {
val h = platform(RaiseFatalErrorHandle, List.empty)
h.awaitStatus() mustEqual 1
h.stderr() must contain("Boom!")
h.stdout() must not(contain("sadness"))
}

"exit on raising a fatal error inside a map" in {
val h = platform(RaiseFatalErrorMap, List.empty)
h.awaitStatus() mustEqual 1
h.stderr() must contain("Boom!")
h.stdout() must not(contain("sadness"))
}

"exit on raising a fatal error inside a flatMap" in {
val h = platform(RaiseFatalErrorFlatMap, List.empty)
h.awaitStatus() mustEqual 1
h.stderr() must contain("Boom!")
h.stdout() must not(contain("sadness"))
}

"warn on global runtime collision" in {
val h = platform(GlobalRacingInit, List.empty)
h.awaitStatus() mustEqual 0
Expand Down
38 changes: 38 additions & 0 deletions tests/shared/src/main/scala/catseffect/examples.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,44 @@ package examples {
}
}

object RaiseFatalErrorAttempt extends IOApp {
def run(args: List[String]): IO[ExitCode] = {
IO.raiseError[Unit](new OutOfMemoryError("Boom!"))
.attempt
.flatMap(_ => IO.println("sadness"))
.as(ExitCode.Success)
}
}

object RaiseFatalErrorHandle extends IOApp {
def run(args: List[String]): IO[ExitCode] = {
IO.raiseError[Unit](new OutOfMemoryError("Boom!"))
.handleError(_ => ())
.flatMap(_ => IO.println("sadness"))
.as(ExitCode.Success)
}
}

object RaiseFatalErrorMap extends IOApp {
def run(args: List[String]): IO[ExitCode] = {
IO.raiseError[Unit](new OutOfMemoryError("Boom!"))
.map(_ => ())
.handleError(_ => ())
.flatMap(_ => IO.println("sadness"))
.as(ExitCode.Success)
}
}

object RaiseFatalErrorFlatMap extends IOApp {
def run(args: List[String]): IO[ExitCode] = {
IO.raiseError[Unit](new OutOfMemoryError("Boom!"))
.flatMap(_ => IO(()))
.handleError(_ => ())
.flatMap(_ => IO.println("sadness"))
.as(ExitCode.Success)
}
}

object Canceled extends IOApp {
def run(args: List[String]): IO[ExitCode] =
IO.canceled.as(ExitCode.Success)
Expand Down

0 comments on commit 74b2b74

Please sign in to comment.