In Scala, as explained in the PR that introduced it, parasitic allows to steal
execution time from other threads by having its
Runnables run on theThreadwhich callsexecuteand then yielding back control to the caller after all itsRunnables have been executed.
It appears to be a neat trick to avoid context switches when:
- you are doing a trivial operation following on a
Futurecoming from an actually long running operation, or - you are working with an API that doesn't allow you to specify an
ExecutionContextfor aFuturebut you but you would like to make sure the operation continues on that same thread, without introducing a different threadpool
The PR that originally introduced parasitic further explains that
When using
parasiticwith abstractions such asFutureit will in many cases be non-deterministic as to whichThreadwill be executing the logic, as it depends on when/if thatFutureis completed.
This concept is also repeated in the official Scala documentation in the paragraphs about Synchronous Execution Contexts:
One might be tempted to have an ExecutionContext that runs computations within the current thread:
val currentThreadExecutionContext = ExecutionContext.fromExecutor( new Executor { // Do not do this! def execute(runnable: Runnable) { runnable.run() } })This should be avoided as it introduces non-determinism in the execution of your future.
Future { doSomething }(ExecutionContext.global).map { doSomethingElse }(currentThreadExecutionContext)The
doSomethingElsecall might either execute indoSomething’s thread or in the main thread, and therefore be either asynchronous or synchronous. As explained here a callback should not be both.
I have a couple of doubts:
- how is
parasiticdifferent from the synchronous execution context in the Scala documentation? - what is the source of non-determinism mentioned in both sources? From the comment in the PR that introduced
parasiticit sounds like ifdoSomethingcompletes very quickly it may return control to the main thread and you may end up actually not runningdoSomethingElseon aglobalthread but on the main one. That's what I could make of it, but I would like to confirm. - is there a reliable way to have a computation run on the same thread as its preceding task? I guess using a lazy implementation of the
Futureabstraction (e.g.IOin Cats) could make this more easy and reliable, but I'd like to understand if this is possible at all using theFuturefrom the standard library.