Search code examples
scalaasynchronousscala-catscats-effect

In Scala library cats-effect 2, how to reliably trigger an IO cancelling?


Here is a short example:

Assuming that forever is an IO that never ends, I want to run it for 3 seconds and trigger the cancellation:

    val start = forever.runCancelable {
      case Left(ee) =>
        IO {
          println(s"FAILUE: ${ee.toString}")
        }
      case Right(kk) =>
        IO {
          println("SUCCESS!")
        }
    }

    val cancelling = start.unsafeRunSync()

    Thread.sleep(3000)

    cancelling.unsafeRunSync()

    println("finished")

When this snippet is executed, I found that none of the println cancellation function was executed (neither println and breakpoint works).

As a result, I have 2 questions:

  1. What is the proper way to trigger it? (Including, but limited to, process termination)

  2. (UPDATED) What is the equivalent implementation in cats-effect 3.5.x that is guaranteed to have the same behaviour? runCancelable is removed in cats-effect 3, there must be a replacement.


Solution

  • For CE3

    I am still not sure if I am completely understanding your requirements but I guess something like this should work:

    def runFinalizerAfter[A](program: IO[A], time: FiniteDuration)(finalizer: Option[OutcomeIO[A]] => IO[Unit]): IO[Unit] =
    IO.ref(Option.empty[OutcomeIO[A]]).flatMap { ref =>
      program.guaranteedCase(outcome => ref.set(Some(outcome))).background.surround {
        IO.sleep(time) >> ref.get.flatMap {
          case Some(outcome) => finalizer(outcome) // Finished, failed, or cancelled.
          case None => IO.unit // Still running.
        }
      }
    }
    

    This cancels the program after running the finalizer.

    For making this return Unit rather than IO[Unit] either just unsafeRunSync it or use a Dispatcher