module Async.Combinators
(
WorkerExited (WorkerExited, WorkerFailed)
, withWorker
) where
import Universum
import Control.Concurrent (myThreadId)
import Control.Concurrent.Async (withAsync)
import Control.Exception (asyncExceptionFromException, asyncExceptionToException)
import Control.Exception.Safe (Exception (..), finally, throwTo, tryAsync)
import Control.Monad.IO.Unlift (MonadUnliftIO, withRunInIO)
import qualified GHC.Show as Show (Show (show))
data WorkerExited = WorkerExited Text
| WorkerFailed Text SomeException
instance Show WorkerExited where
show (WorkerExited n) = toString $ "Worker '" <> n <> "' returned"
show (WorkerFailed n e) = toString $ "Worker '" <> n <> "' failed: " <> show e
instance Exception WorkerExited where
toException = asyncExceptionToException
fromException = asyncExceptionFromException
withWorker :: MonadUnliftIO m
=> Text
-> m ()
-> m b
-> m b
withWorker name worker go = withRunInIO $ \run -> do
tid <- myThreadId
mainDone <- newIORef False
let worker' = do
res <- tryAsync $ run worker
unlessM (readIORef mainDone) $ throwTo tid $
case res of
Right () -> WorkerExited name
Left e -> WorkerFailed name e
withAsync worker' $ \_ -> run go `finally` atomicWriteIORef mainDone True