2020-05-15 02:24:09 +03:00
|
|
|
`ki` is a lightweight structured-concurrency library inspired by
|
2020-05-14 17:31:57 +03:00
|
|
|
[libdill](http://libdill.org/), [trio](https://github.com/python-trio/trio),
|
|
|
|
and [golang.org/pkg/context](https://golang.org/pkg/context/).
|
|
|
|
|
|
|
|
The primary abstraction is the *scope*, which delimits the lifetime of *threads*
|
|
|
|
forked within it.
|
|
|
|
|
|
|
|
```haskell
|
2020-05-15 02:05:27 +03:00
|
|
|
-- Create a new scope
|
2020-05-14 17:31:57 +03:00
|
|
|
scoped :: Context -> (Scope -> IO a) -> IO a
|
|
|
|
|
2020-05-15 02:05:27 +03:00
|
|
|
-- Fork a thread within a scope
|
2020-05-14 17:31:57 +03:00
|
|
|
async :: Scope -> (Context -> IO a) -> IO (Thread a)
|
|
|
|
```
|
|
|
|
|
|
|
|
```haskell
|
2020-05-15 02:24:09 +03:00
|
|
|
Ki.scoped context \scope -> do
|
2020-05-14 17:31:57 +03:00
|
|
|
-- Fork three worker threads
|
2020-05-15 02:24:09 +03:00
|
|
|
Ki.async_ scope worker1
|
|
|
|
Ki.async_ scope worker2
|
|
|
|
Ki.async_ scope worker3
|
2020-05-14 17:31:57 +03:00
|
|
|
|
|
|
|
-- Block until either:
|
|
|
|
-- * They all finish successfully
|
|
|
|
-- * One throws an exception
|
|
|
|
-- * Someone throws an asynchronous exception to us
|
2020-05-15 02:24:09 +03:00
|
|
|
Ki.wait scope
|
2020-05-14 17:31:57 +03:00
|
|
|
```
|
|
|
|
|
|
|
|
A *scope* can be hard-cancelled: when the callback provided to `scoped` returns,
|
|
|
|
all remaining *threads* forked within it are killed. By the time `scoped` itself
|
|
|
|
returns, they're guaranteed to have finished.
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
2020-05-15 02:05:27 +03:00
|
|
|
```haskell
|
|
|
|
-- Should I finish up?
|
|
|
|
cancelled :: Context -> IO Bool
|
|
|
|
```
|
|
|
|
|
2020-05-14 17:31:57 +03:00
|
|
|
```haskell
|
2020-05-15 02:24:09 +03:00
|
|
|
Ki.scoped context \scope -> do
|
2020-05-14 17:31:57 +03:00
|
|
|
-- Fork a worker thread
|
2020-05-15 02:24:09 +03:00
|
|
|
Ki.async_ scope worker
|
2020-05-14 17:31:57 +03:00
|
|
|
|
|
|
|
-- Signal soft-cancellation, and block until either:
|
|
|
|
-- * It finishes, either successfully or by throwing an exception
|
|
|
|
-- * Someone throws an asynchronous exception to us
|
|
|
|
-- * 1 second elapses
|
2020-05-15 02:24:09 +03:00
|
|
|
Ki.waitFor scope 1000000
|
2020-05-14 17:31:57 +03:00
|
|
|
```
|
|
|
|
|
|
|
|
Soft-cancellation is hierarchical: it is observable by all *threads* forked
|
2020-05-15 02:05:27 +03:00
|
|
|
within a *scope*, all *threads* _forked by_ them, and so on.
|
2020-05-14 17:31:57 +03:00
|
|
|
|
|
|
|
The implementation is tested for deadlocks, race conditions, and other
|
|
|
|
concurrency anomalies by [dejafu](http://hackage.haskell.org/package/dejafu), a
|
|
|
|
fantastic unit-testing library for concurrent programs.
|