Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ private object IOFiberConstants {
final val UncancelableK = 7
final val UnmaskK = 8
final val AttemptK = 9
final val OnCancelRequestedK = 10

// resume ids
final val ExecR = 0
Expand Down
2 changes: 1 addition & 1 deletion core/js/src/main/scala/cats/effect/IOApp.scala
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ trait IOApp {
case Left(Outcome.Errored(t)) => IO.raiseError(t)
case Left(Outcome.Succeeded(code)) => code
case Right(Outcome.Errored(t)) => IO.raiseError(t)
case Right(_) => sys.error("impossible")
case Right(_) => IO.delay(sys.error("impossible"))
}
.unsafeRunFiber(
hardExit(cancelCode),
Expand Down
1 change: 1 addition & 0 deletions core/jvm/src/main/java/cats/effect/IOFiberConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ final class IOFiberConstants {
static final byte UncancelableK = 7;
static final byte UnmaskK = 8;
static final byte AttemptK = 9;
static final byte OnCancelRequestedK = 10;

// resume ids
static final byte ExecR = 0;
Expand Down
34 changes: 34 additions & 0 deletions core/shared/src/main/scala/cats/effect/IO.scala
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,19 @@ sealed abstract class IO[+A] private () extends IOPlatform[A] {
def cancelable(fin: IO[Unit]): IO[A] =
Spawn[IO].cancelable(this, fin)

/**
* Run the given effect when cancelation is requested. Unlike [[onCancel]], this will run
* before cancelation is observed, on a separate fiber, and will always allow `fa` to complete
* before cancelation is observed.
*
* @param ack
* an effect which orchestrates some external state which terminates `fa`
* @see
* [[onCancel]]
*/
def onCancelRequested(ack: IO[Unit]): IO[A] =
IO.OnCancelRequested(this, ack)

def forceR[B](that: IO[B]): IO[B] =
// cast is needed here to trick the compiler into avoiding the IO[Any]
asInstanceOf[IO[Unit]].handleError(_ => ()).productR(that)
Expand Down Expand Up @@ -2059,6 +2072,23 @@ object IO extends IOCompanionPlatform with IOLowPriorityImplicits with TuplePara
def onCancel[A](ioa: IO[A], fin: IO[Unit]): IO[A] =
ioa.onCancel(fin)

/**
* Run the given effect when cancelation is requested. Unlike [[onCancel]], this will run
* before cancelation is observed, on a separate fiber, and will always allow `fa` to
* complete before cancelation is observed.
*
* @param fa
* the effect to be canceled
* @param ack
* an effect which orchestrates some external state which terminates `fa`
* @see
* [[cancelable]]
* @see
* [[onCancel]]
*/
override def onCancelRequested[A](fa: IO[A], ack: IO[Unit]): IO[A] =
fa.onCancelRequested(ack)

override def bracketFull[A, B](acquire: Poll[IO] => IO[A])(use: A => IO[B])(
release: (A, OutcomeIO[B]) => IO[Unit]): IO[B] =
IO.bracketFull(acquire)(use)(release)
Expand Down Expand Up @@ -2328,6 +2358,10 @@ object IO extends IOCompanionPlatform with IOLowPriorityImplicits with TuplePara
def tag = 24
}

private[effect] final case class OnCancelRequested[A](f: IO[A], ack: IO[Unit]) extends IO[A] {
def tag = 25
}

// INTERNAL, only created by the runloop itself as the terminal state of several operations
private[effect] case object EndFiber extends IO[Nothing] {
def tag = -1
Expand Down
Loading
Loading