3

I am migrating from Cats Effect 2 to 3 and since Blocker is not available anymore I wonder how is it possible to run F in the blocking execution context?

Previously, the following code was used

val blocker: Blocker = Blocker.liftExecutionContext(blockingEc)
blocker.blockOn(F)

According to the CE3 docs, blocking contexts is inside a Runtime and this field is private. There is a method Sync[F].blocking(f: => A), but I need an alternative one with the possibility to pass F[A] to the blocking context like Sync[F].blockingF(f: F[A]).

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
Mike
  • 347
  • 1
  • 2
  • 15
  • 1
    Ideally whoever created that `F` should use `blocking` instead. You may ask in the **Discord** server for more advice: https://discord.gg/eX9MK8YT – Luis Miguel Mejía Suárez Feb 07 '23 at 14:23
  • 3
    To expand what @LuisMiguelMejíaSuárez said: you are not taking existing `F[A]` and telling runtime "it is blocking" because it was already created and whoever created it, defined it in a way that it _requires_ blocking. Taking blocking `F[A]` and tagging it as "blocking" is an error-prone afterthought. Whoever creates this `F[A]` should use `Sync[F].blocking(=>A)` to _construct_ this `F[A]` as "blocking", so that whoever uses it won't have an opportunity to mess up by not calling `blocker.blockOn(fa)`. – Mateusz Kubuszok Feb 07 '23 at 17:30
  • Sure, but what if you don't control the library? And the author forgot to block? I think we need a way out. `IO` is just a program and we should be able to run it on whatever we want. – allidoiswin Feb 13 '23 at 00:59

1 Answers1

0

The best way to handle this is to correctly construct the value using Sync[F].blocking(code).

If you can't do that because you don't control the creating code, then the next best way is to file a ticket for the code author to fix it. This is a bug in their code.

As a workaround if the author is unresponsive or for whatever other reason, you can create your own blocking thread pool. The reason you can't access the built in one is because it's actually the same thread pool as compute threads - the IORuntime is just a lot more clever in how it allocates and manages work now. It relies on the signal from blocking(..) to tell it that the work is blocking.

import cats.effect.syntax.all._
def newEC: Resource[IO, ExecutionContext] = Resource
  .make(IO.delay(Executors.newCachedThreadPool()))(ex =>
    IO.delay(ex.shutdown())
  )
  .map(ExecutionContext.fromExecutor)

newEC.use { blocker =>
  myIO.evalOn(blocker)
}

All that said - try to fix the bug rather than working around it

Daenyth
  • 35,856
  • 13
  • 85
  • 124