Cats Effect 3.0.0-M3 is the third milestone release in the 3.0.0 line. We are expecting to follow this milestone with subsequent ones as necessary to further refine functionality and features, prior to some number of release candidates leading to a final release. The designation "M3" is meant to indicate our relative confidence in the functionality and API of the library, but it by no means indicates completeness or final compatibility. Neither binary compatibility nor source compatibility with anything are assured prior to 3.0.0, though major breakage is unlikely at this point.
What follows are the changes from M2 to M3. For a more complete summary of the changes generally included in Cats Effect 3.0.0, please see the M1 and M2 release notes.
Major Changes
Error Trapping
One of the "skeletons in the closet" for nearly all thread pools is fatal error trapping. There are a whole series of exceptions which can be raised by the JVM which represent varying levels of serious problems (most famously, OutOfMemoryError
). These exceptions are correctly ignored by IO
and similar runtime systems, but unfortunately that doesn't really solve the problem for the user.
Consider a scenario wherein an IO
-using application runs out of memory. When this happens, the error will be raised by the VM, which in turn will result in the carrier thread being killed and the active fiber deadlocking. Unfortunately, without further intervention, that's all that will happen. Other fibers can continue executing, and the VM may even try to recover from the error (which is actually unsound).
Similar things happen with link errors, stack overflows, and even thread interruption (which frequently occurs when using sbt run
). In all of these cases, the carrier thread is gone, at least one fiber is deadlocked, and the rest of the application has no idea. If you're lucky the fatal exception will have its stack trace printed, but it's easy to miss this in the logs, meaning that zombie processes are actually quite a common consequence of this situation.
Unfortunately, this is an incredibly difficult problem to solve because fatal errors, by definition, cannot be reliably handled. The VM is often in an undefined state and even basic things like allocation or virtual dispatch may not behave normally. Thus, the runtime needs to achieve a full shutdown and propagation of the error across threads, but without actually doing anything fancy.
The solution we've chosen here is to take the following steps any time a fatal error is trapped:
- Print the stack trace
- Trigger a full shutdown of the runtime thread pool
- Asynchronously propagate the error back to the original
unsafeRun
... call site
In most cases, this means that the error will be relocated back to the original unsafeRun
call site, which in turn will mean that errors will shut down the containing application and avoid the zombie scenario. In a worst-case, the entire application will be halted and the error will at least be printed.
This entirely resolves the "zombie process" problem, even in the face of an OutOfMemoryError
.
Respecialized racePair
racePair
can be implemented entirely in terms of Deferred
and Fiber
, meaning that it does not need to be a primitive in the IO
algebra. This is actually relatively significant since, as it turns out, the implementation in terms of Deferred
and Fiber
is faster than the "primitive" as it had been implemented in IO
itself. This in turn increases the performance of Parallel
and related operations.
Pull Requests
- #1392 – Re-specialized
racePair
inGenSpawn
for higher performance and correctness (@vasilmkd and @djspiewak) - #1361 – Asynchronous priority queue (@TimWSpence)
- #1315 – Removed
Resource.Bracket
by makingSyncIO
self-cancelable (@mpilquist and @vasilmkd) - #1368 – Added
surround
andsurroundK
functions onResource
(@pkowalcze) - #1364 – Added some missing default
Show
instances (@djspiewak) - #1362 – Generalized
Queue
constraints toGenConcurrent
(@vasilmkd) - #1349 – Trap and propagate fatal errors on pool shutdown (@vasilmkd)
- #1359 – Adjusted
Console
to use a tagless encoding (@vasilmkd) - #1356 – Further reductions in
IOFiber
memory footprint (@vasilmkd) - #1358 – Unseal
Dispatcher
(@SystemFw) - #1357 – Loosen Async constraints on Resource (@RaasAhsan)
You're all amazing, thank you!