-- | `ki` is a lightweight structured concurrency library. -- -- For a variant of this API generalized to -- @<https://hackage.haskell.org/package/unliftio-core/docs/Control-Monad-IO-Unlift.html#t:MonadUnliftIO MonadUnliftIO>@, -- see @<https://hackage.haskell.org/package/ki-unlifted ki-unlifted>@. -- -- Remember to link your program with @-threaded@ to use the threaded runtime! module Ki ( -- * Introduction -- $introduction -- * Core API Scope, Thread, scoped, fork, forkTry, await, awaitAll, -- * Extended API fork_, forkWith, forkWith_, forkTryWith, -- ** Thread options ThreadOptions (..), defaultThreadOptions, ThreadAffinity (..), -- ** Byte count ByteCount, kilobytes, megabytes, ) where import Ki.Internal.ByteCount (ByteCount, kilobytes, megabytes) import Ki.Internal.Scope ( Scope, awaitAll, fork, forkTry, forkTryWith, forkWith, forkWith_, fork_, scoped, ) import Ki.Internal.Thread ( Thread, ThreadAffinity (..), ThreadOptions (..), await, defaultThreadOptions, ) -- $introduction -- -- Structured concurrency is a paradigm of concurrent programming in which a lexical scope delimits the lifetime of each -- thread. Threads therefore form a "call tree" hierarchy in which no child can outlive its parent. -- -- Exceptions are propagated promptly from child to parent and vice-versa: -- -- * If an exception is raised in a child thread, the child raises the same exception in its parent, then -- terminates. -- -- * If an exception is raised in a parent thread, the parent first raises an exception in all of its living -- children, waits for them to terminate, then re-raises the original exception. -- -- All together, this library: -- -- * Guarantees the absence of "ghost threads" (/i.e./ threads that accidentally continue to run alongside the main -- thread after the function that spawned them returns). -- -- * Performs prompt, bidirectional exception propagation when an exception is raised anywhere in the call tree. -- -- * Provides a safe and flexible API that can be used directly, or with which higher-level concurrency patterns can -- be built on top, such as worker queues, pub-sub pipelines, and supervision trees. -- -- For a longer introduction to structured concurrency, including an educative analogy to structured programming, please -- read Nathaniel J. Smith's blog post, -- <https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/ "Notes on structured concurrency, or: Go statement considered harmful">. -- -- ==== __👉 Quick start examples__ -- -- * Perform two actions concurrently, and wait for both of them to complete. -- -- @ -- concurrently :: IO a -> IO b -> IO (a, b) -- concurrently action1 action2 = -- Ki.'Ki.scoped' \\scope -> do -- thread1 <- Ki.'Ki.fork' scope action1 -- result2 <- action2 -- result1 <- atomically (Ki.'Ki.await' thread1) -- pure (result1, result2) -- @ -- -- * Perform two actions concurrently, and when the first action terminates, stop executing the other. -- -- @ -- race :: IO a -> IO a -> IO a -- race action1 action2 = -- Ki.'Ki.scoped' \\scope -> do -- resultVar \<- newEmptyMVar -- _ \<- Ki.'Ki.fork' scope (action1 \>>= tryPutMVar resultVar) -- _ \<- Ki.'Ki.fork' scope (action2 \>>= tryPutMVar resultVar) -- takeMVar resultVar -- @