Skip to content
Snippets Groups Projects
Commit c606b92c authored by Jens Nolte's avatar Jens Nolte
Browse files

Add async variant that additionally accepts an exception handler

parent 0c8b0ec5
No related branches found
No related tags found
No related merge requests found
...@@ -5,6 +5,12 @@ module Quasar.Async ( ...@@ -5,6 +5,12 @@ module Quasar.Async (
asyncWithUnmask, asyncWithUnmask,
asyncWithUnmask_, asyncWithUnmask_,
-- ** Async with explicit error handling
handleAsync,
handleAsync_,
handleAsyncWithUnmask,
handleAsyncWithUnmask_,
-- ** Async exceptions -- ** Async exceptions
CancelAsync(..), CancelAsync(..),
AsyncDisposed(..), AsyncDisposed(..),
...@@ -29,23 +35,39 @@ async action = asyncWithUnmask \unmask -> unmask action ...@@ -29,23 +35,39 @@ async action = asyncWithUnmask \unmask -> unmask action
-- The action will be run with asynchronous exceptions masked and will be passed an action that can be used to unmask. -- The action will be run with asynchronous exceptions masked and will be passed an action that can be used to unmask.
asyncWithUnmask :: MonadResourceManager m => ((ResourceManagerIO a -> ResourceManagerIO a) -> ResourceManagerIO r) -> m (Awaitable r) asyncWithUnmask :: MonadResourceManager m => ((ResourceManagerIO a -> ResourceManagerIO a) -> ResourceManagerIO r) -> m (Awaitable r)
asyncWithUnmask action = do asyncWithUnmask action = do
resourceManager <- askResourceManager
handleAsyncWithUnmask (throwToResourceManager resourceManager) action
async_ :: MonadResourceManager m => (ResourceManagerIO ()) -> m ()
async_ action = void $ async action
asyncWithUnmask_ :: MonadResourceManager m => ((ResourceManagerIO a -> ResourceManagerIO a) -> ResourceManagerIO ()) -> m ()
asyncWithUnmask_ action = void $ asyncWithUnmask action
-- | TODO: Documentation
--
-- The action will be run with asynchronous exceptions unmasked. When an exception is thrown that is not caused from
-- the disposable instance (i.e. the task being canceled), the handler is called with that exception.
handleAsyncWithUnmask :: MonadResourceManager m => (SomeException -> IO ()) -> ((ResourceManagerIO a -> ResourceManagerIO a) -> ResourceManagerIO r) -> m (Awaitable r)
handleAsyncWithUnmask handler action = do
resourceManager <- askResourceManager resourceManager <- askResourceManager
toAwaitable <$> registerNewResource do toAwaitable <$> registerNewResource do
coreAsyncImplementation (handler resourceManager) \unmask -> coreAsyncImplementation wrappedHandler \unmask ->
onResourceManager resourceManager (action (liftUnmask unmask)) onResourceManager resourceManager (action (liftUnmask unmask))
where where
handler :: ResourceManager -> SomeException -> IO () wrappedHandler :: SomeException -> IO ()
handler resourceManager ex = when (fromException ex /= Just AsyncDisposed) do wrappedHandler (fromException -> Just AsyncDisposed) = pure ()
-- Throwing to the resource manager is safe because the handler runs on the async thread the resource manager wrappedHandler ex = handler ex
-- cannot reach disposed state until the thread exits
throwToResourceManager resourceManager ex
liftUnmask :: (forall b. IO b -> IO b) -> ResourceManagerIO a -> ResourceManagerIO a liftUnmask :: (forall b. IO b -> IO b) -> ResourceManagerIO a -> ResourceManagerIO a
liftUnmask unmask innerAction = do liftUnmask unmask innerAction = do
resourceManager <- askResourceManager resourceManager <- askResourceManager
liftIO $ unmask $ onResourceManager resourceManager innerAction liftIO $ unmask $ onResourceManager resourceManager innerAction
async_ :: MonadResourceManager m => (ResourceManagerIO ()) -> m () handleAsyncWithUnmask_ :: MonadResourceManager m => (SomeException -> IO ()) -> ((ResourceManagerIO a -> ResourceManagerIO a) -> ResourceManagerIO r) -> m ()
async_ action = void $ async action handleAsyncWithUnmask_ handler action = void $ handleAsyncWithUnmask handler action
asyncWithUnmask_ :: MonadResourceManager m => ((ResourceManagerIO a -> ResourceManagerIO a) -> ResourceManagerIO ()) -> m () handleAsync :: MonadResourceManager m => (SomeException -> IO ()) -> ResourceManagerIO r -> m (Awaitable r)
asyncWithUnmask_ action = void $ asyncWithUnmask action handleAsync handler action = handleAsyncWithUnmask handler \unmask -> unmask action
handleAsync_ :: MonadResourceManager m => (SomeException -> IO ()) -> ResourceManagerIO r -> m ()
handleAsync_ handler action = void $ handleAsync handler action
...@@ -99,21 +99,22 @@ coreAsyncImplementation handler action = do ...@@ -99,21 +99,22 @@ coreAsyncImplementation handler action = do
-- The `action` has completed its work. -- The `action` has completed its work.
-- "disarm" dispose: -- "disarm" dispose:
handleIf catchIf
do \(CancelAsync exKey) -> key == exKey do \(CancelAsync exKey) -> key == exKey
do mempty -- ignore exception if it matches; this can only happen once
do do
atomically $ readTVar stateVar >>= \case atomically $ readTVar stateVar >>= \case
TaskStateInitializing -> retry TaskStateInitializing -> retry
TaskStateRunning _ -> writeTVar stateVar TaskStateCompleted TaskStateRunning _ -> writeTVar stateVar TaskStateCompleted
TaskStateThrowing -> retry -- Could not disarm so we have to wait for the exception to arrive TaskStateThrowing -> retry -- Could not disarm so we have to wait for the exception to arrive
TaskStateCompleted -> pure () TaskStateCompleted -> pure ()
do mempty -- ignore exception if it matches; this can only happen once (see TaskStateThrowing above)
catchAll catchAll
case result of case result of
Left ex -> when (fromException ex /= Just AsyncDisposed) $ handler ex Left (fromException -> Just AsyncDisposed) -> pure ()
Right _ -> pure () Left ex -> handler ex
\ex -> undefined _ -> pure ()
\ex -> traceIO $ "An exception was thrown while handling an async exception: " <> displayException ex
atomically do atomically do
putAsyncVarEitherSTM_ resultVar result putAsyncVarEitherSTM_ resultVar result
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment