Reputation: 3972
I'm writing close() method for my ScheduledExecutorService-based timer:
override def close(implicit executionContext: ExecutionContext): Future[Unit] = {
val p = Promise[Unit]
executionContext.execute(new Runnable() {
override def run() = {
blocking {
p complete Try {
executor.shutdown()
//OK for default global execution context
//As we marked this code as blocking, additional thread
//will be used on that so no threadpool starvation
executor.awaitTermination(1, TimeUnit.DAYS)
}
}
}
})
p.future
}
But if I implement ExecutionContext by myself, this code will block one of the pool's threads because I did not find any way to get that blocking context.
So, question: Is it possible to create own ExecutionContext that can properly handle scala.concurrent.blocking?
Upvotes: 2
Views: 273
Reputation: 55569
Of course it's possible, it's just far from trivial. You would need to create an ExecutionContext
that creates threads that mix in BlockContext
which requires the following method:
def blockOn[T](thunk: => T)(implicit permission: CanAwait): T
blocking(thunk)
will eventually lead to calling blockOn(thunk)
, and blockOn
should figure out if the ExecutionContext
has reached starvation and needs to do something or not. scala.concurrent.ExecutionContext.Implicits.global
does it this way, but as you can see it uses a ForkJoinPool
to do the heavy-lifting, and the implementation of that is thousands of lines of code.
Keep in mind that whether you use ExecutionContext.Implicits.global
or your own `ExecutionContext, a thread will still be blocked by your code. The only difference is that the former spawns another thread to handle the fact that too many are blocked. Creating your own is likely to create some dangerous bugs though, as a lot of care has to be taken to avoid deadlocks or spawning too many threads.
Upvotes: 3