module Try.ParallelAndConcurrentHaskell.Exceptions where
import UnliftIO
Exceptions
-
Form a hierarchy
-
Catching
-
Some handlers
try :: Exception e => IO a -> IO (Either e a) handle :: Exception e => (e -> IO a) -> IO a -> IO a onException :: IO a -> IO b -> IO a
-
onException
rethrows the exception. Example:bracket before after thing = mask $ \restore -> do a <- before r <- restore (thing a) `onException` after a _ <- after a return r
-
catchJust
,handleJust
select an exception by a predicate.
-
-
Throwing
throwIO :: Exception e => e -> IO a
guarantees the ordering of exceptionsthrow :: Exception e => e -> a
doesn't
-
Processing actions with possible exceptions
bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c finally :: IO a -> IO b -> IO a
Cancellation and Timeouts
Masking
mask :: ((IO a -> IO a) -> IO b) -> IO b
The mask operation defers the delivery of asynchronous exceptions for the duration of its argument.
Here, asynchronous exceptions can only be delivered inside the argument of restore
- inside f a
.
problem :: MVar a -> (a -> IO a) -> IO ()
problem m f = mask $ \restore -> do
a <- takeMVar m
r <- restore (f a) `catchAny` \e -> do putMVar m a; throwIO e
putMVar m r
mask is applied to a function, which takes as its argument a function restore. The restore function can be used to restore the delivery of asynchronous exceptions to its present state during execution of the argument to mask. If we imagine shading the entire argument to mask except for the expression (f a), asynchronous exceptions cannot be raised in the shaded portions.
Interruptibility
An interruptible
operation may receive an asynchronous exception only if it actually blocks.
In the case of problem above, we know the MVar is definitely empty when we call putMVar,
so putMVar cannot block, which means that it is not interruptible.
Get current masking state
getMaskingState :: IO MaskingState
data MaskingState
= Unmasked
| MaskedInterruptible
| MaskedUninterruptible
The getMaskingState function returns one of the following construc‐ tors:
Unmasked
- The current thread is not insidemask
oruninterruptibleMask
.MaskedInterruptible
- The current thread is insidemask
.MaskedUninterruptible
- The current thread is insideuninterruptibleMask
.
Multiple MVars
modifyMVar
and company first takeMVar
, then putMVar
.
modifyTwo :: MVar a -> MVar b -> (a -> b -> IO (a, b)) -> IO ()
modifyTwo ma mb f =
modifyMVar_ mb $ \b ->
modifyMVar ma $ \a -> f a b
If this blocks in the inner modifyMVar and an exception is raised, then the outer modifyMVar_ will restore the contents of the MVar it took.
Bracket
bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket before after thing =
mask $ \restore -> do
a <- before
r <- restore (thing a) `onException` after a
_ <- after a
return r
The IO actions passed in as before and after are performed inside mask. The bracket function guarantees that if before returns, after will be executed in the future. It is normal for before to contain a blocking operation; if an exception is raised while before is blocked, then no harm is done. But before should perform only one blocking oper‐ ation. An exception raised by a second blocking operation would not result in after being executed. If you need to perform two blocking operations, the right way is to nest calls to bracket, as we did with modifyMVar. Something else to watch out for here is using blocking operations in after. If you need to do this, then be aware that your blocking operation is interruptible and might receive an asynchronous exception.
Timeouts
See
Catching asynchronous exceptions
If you need to handle asynchronous exceptions, it’s usually important for the exception handler to be inside a mask so that you don’t get interrupted by another asynchronous exception before you’ve finished dealing with the first one. For that rea‐ son, catch or handle might be more appropriate, because you can take advantage of the built-in mask.
Don't handle exceptions inside a handler.
mask and forkIO
Use async