Chapter 11. Higher-Level Concurrency Abstractions
The preceding sections covered the basic interfaces for writing concurrent code in Haskell. These are enough for simple tasks, but for larger and more complex programs we need to raise the level of abstraction.
The previous chapters developed the Async interface for
performing operations asynchronously and waiting for the results. In
this chapter, we will be revisiting that interface and expanding it
with some more sophisticated functionality. In particular, we will
provide a way to create an Async that is automatically cancelled if
its parent dies and then use this to build more compositional
functionality.
What we are aiming for is the ability to build trees of threads, such that when a thread dies for whatever reason, two things happen: any children it has are automatically terminated, and its parent is informed. Thus the tree is always collapsed from the bottom up, and no threads are ever left running accidentally. Furthermore, all threads are given a chance to clean up when they die, by handling exceptions.
Avoiding Thread Leakage
Let’s review the last version of the Async API that we encountered
from Async Revisited:
dataAsyncasync::IOa->IO(Asynca)cancel::Asynca->IO()waitCatchSTM::Asynca->STM(EitherSomeExceptiona)waitCatch::Asynca->IO(EitherSomeExceptiona)waitSTM::Asynca->STMawait::Asynca->IOawaitEither::Asynca->Asyncb->IO(Eitherab)
Now we’ll define a way to create an