2.2 KiB
ki
ki
is a lightweight structured-concurrency library inspired by
libdill
, trio
,
and golang.org/pkg/context
.
The primary abstraction is the scope, which delimits the lifetime of threads forked within it.
-- Create a new scope
scoped :: Context -> (Scope -> IO a) -> IO a
-- Fork a thread within a scope
async :: Scope -> (Context -> IO a) -> IO (Thread a)
-- Should I finish up?
cancelled :: Context -> IO Bool
Ki.scoped context \scope -> do
-- Fork three worker threads
Ki.async_ scope worker1
Ki.async_ scope worker2
Ki.async_ scope worker3
-- Block until either:
-- * They all finish successfully
-- * One throws an exception
-- * Someone throws an asynchronous exception to us
Ki.wait scope
A scope can be hard-cancelled. When the callback provided to scoped
returns, all remaining threads within it are killed. By the time
scoped
itself returns, they're guaranteed to have finished, so
hard-cancellation is hierarchical.
A scope can be soft-cancelled, too, but it requires cooperation. A thread can observe whether it's meant to gracefully terminate, but it may never notice, or ignore the suggestion.
Soft-cancellation is also hierarchical. It is observable by all threads forked within a scope, and all threads forked by them, and so on.
There's another abstraction, too: the context. It's not as interesting. Just pass it around everywhere you need to create a new scope or check for cancellation.
ki-mtl
is an mtl
-compatible shim that passes the context around
implicitly in a MonadReader
.
type MonadKi r m
scoped :: MonadKi r m => (Scope -> m a) -> m a
async :: MonadKi r m => Scope -> m a -> m (Thread a)
cancelled :: MonadKi r m => m Bool
The implementation is tested for deadlocks, race conditions, and other
concurrency anomalies by dejafu
, a
fantastic unit-testing library for concurrent programs.