-- | Functions and types for safely working with 'Async's in 'Scoped' blocks -- -- @since 0.1.0.0 module Control.Monad.Scoped.Async ( -- * Scoped 'Control.Concurrent.Async.Async' ScopedAsync -- * Allocating a new 'ScopedAsync' in a 'Scoped' block , async , asyncBound -- * Waiting for a 'ScopedAsync' to finish , wait , waitCatch -- * Waiting for a 'ScopedAsync' to finish as part of the handlers of the 'Scoped' block , waitScoped , waitCatchScoped ) where import Control.Concurrent.Async (Async) import Control.Exception (SomeException) import Control.Monad.IO.Class (MonadIO) import Control.Monad.Scoped.Internal (Scoped (UnsafeMkScoped), ScopedResource (UnsafeMkScopedResource, unsafeUnwrapScopedResource), registerHandler, (:<)) import UnliftIO (MonadUnliftIO) import UnliftIO.Async (withAsync, withAsyncBound) import UnliftIO.Async qualified as Async -- | Just like 'Async' but bound to a 'Scoped' block -- -- @since 0.1.0.0 type ScopedAsync s a = ScopedResource s (Async a) -- | Run an 'IO' action asynchronously in a Scoped block. When the 'Scoped' block ends, the 'Async' is 'Control.Concurrent.Async.cancel'led -- -- @since 0.1.0.0 async :: MonadUnliftIO m => m a -> Scoped (s : ss) m (ScopedAsync s a) async act = UnsafeMkScoped \k -> withAsync act (k . UnsafeMkScopedResource) -- | Like 'async' but uses 'Control.Concurrent.forkOS' internally -- -- @since 0.1.0.0 asyncBound :: MonadUnliftIO m => m a -> Scoped (s : ss) m (ScopedAsync s a) asyncBound act = UnsafeMkScoped \k -> withAsyncBound act (k . UnsafeMkScopedResource) -- | Wait for the 'ScopedAsync' to finish immediately -- -- @since 0.1.0.0 wait :: (MonadIO m, s :< ss) => ScopedAsync s a -> Scoped ss m a wait = Async.wait . unsafeUnwrapScopedResource -- | Like 'wait' but return either @'Left' 'SomeException'@ or @'Right' a@ -- -- @since 0.1.0.0 waitCatch :: (MonadIO m, s :< ss) => ScopedAsync s a -> Scoped ss m (Either SomeException a) waitCatch = Async.waitCatch . unsafeUnwrapScopedResource -- | Like 'wait' but wait as part of the handlers of the 'Scoped' block -- -- @since 0.1.0.0 waitScoped :: (MonadUnliftIO m, s :< ss) => ScopedAsync s a -> Scoped ss m () waitScoped a = registerHandler (Async.wait $ unsafeUnwrapScopedResource a) -- | Like 'waitCatch' but wait as part of the handlers of the 'Scoped' block -- -- @since 0.1.0.0 waitCatchScoped :: (MonadUnliftIO m, s :< ss) => ScopedAsync s a -> Scoped ss m () waitCatchScoped a = registerHandler (Async.waitCatch $ unsafeUnwrapScopedResource a)