(Hopefully) simple question on Scalaz Zio.
I have some old code that I refactored to Zio. I want one path of that code to keep behaving exactly as it was:
How can I run an IO
such that it behaves like the old blocking code?
I currently use:
private lazy val blockingRts = new RTS {}
def runBlocking[E, A](io: IO[E, A]): Either[E, A] = {
blockingRts.unsafeRun(io.attempt)
}
It seems to do the trick, but I am far from sure that this is correct. Is this 100% backwards compatible with the old code?
Okay, I finally looked under the hood and implemented something that seems to be fulfilling my requirements:
/**
* Executes the IO synchronous and blocking on the current thread, thus running an IO
* without any of the advantages of IO. This can be useful for maintaining backwards compatibility.
* Rethrows any exception that was not handled by the IO's error handling.
*/
@throws
def runLegacy[E, A](io: IO[E, A]): Either[E, A] = {
syncBlockingRunTimeSystem.unsafeRunSync[Nothing, Either[E, A]](io.either) match {
case Exit.Success(v) => v
case Exit.Failure(Cause.Die(exception)) => throw exception
case Exit.Failure(Cause.Interrupt) => throw new InterruptedException
case Exit.Failure(fail) => throw FiberFailure(fail)
}
}
private lazy val syncBlockingRunTimeSystem = Runtime(
(),
PlatformLive.fromExecutor(new Executor {
override def yieldOpCount: Int = Int.MaxValue
override def metrics: Option[ExecutionMetrics] = None
override def submit(runnable: Runnable): Boolean = {
runnable.run()
true
}
override def here: Boolean = true
})
)
I also wrote a couple of tests:
"runLegacy" should {
"run synchronous code in blocking fashion on current thread" in {
var runCount = 0
val io = IO.succeedLazy { runCount += 1 }
.map { _ => runCount +=1 }
.flatMap { _ =>
runCount += 1
IO.effect {
runCount += 1
Thread.currentThread()
}
}
runCount shouldBe 0
runLegacy(io) shouldBe Right(Thread.currentThread())
runCount shouldBe 4
}
"run parallel code sequentially on current thread" in {
val ios = (1 to 500).map { i => IO.succeedLazy { i } }
runLegacy(IO.reduceAll(IO.succeed(0), ios) {
case (a, b) => a + b
}) shouldBe Right((500 * 501) / 2)
}
"run many flatMaps without overflowing" in {
var runCount = 0
val io = IO.succeedLazy { runCount += 1 }
val manyIo = (1 to 9999).foldLeft(io) { case (acc, _) => acc.flatMap { _ => io } }
runLegacy(manyIo)
runCount shouldBe 10000
}
case object TestException extends Throwable
"handle sync blocking errors" in {
case object TestException extends Throwable
runLegacy(IO.effect(throw TestException)) shouldBe Left(TestException)
}
"rethrow unhandled exceptions" in {
assertThrows[TestException.type] {
runLegacy(IO.succeedLazy(throw TestException))
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With