module Control.Concurrent.ExceptionUtility where import Control.Exception {- | Utility function that uses an uninterruptable mask to make sure a resource is always acquired and released even with asynchronous exceptions. You can define the acquire, release and update actions. The acquire function should deliver the resource. The update function receives the acquired value and tries to update it but may run into (asynchronous) exceptions. And lastly the release function receives the updated value (or the old value in case of an exception) and can release the resource with it. This function is primarily used in situations with concurrent data where some value is always required to reside in the concurrent datastructure (ex. MVars). The acquire and release reside under the uninterruptable mask, the update function does not and may be interrupted or may cause exceptions. -} uninterruptibleUpdateResource :: IO a -- ^ Acquire resource -> (a -> IO b) -- ^ Release resource with either updated or old value -> (a -> IO a) -- ^ How to update the value -> IO a -- ^ Updated value uninterruptibleUpdateResource acquire release update = uninterruptibleMask $ \restore -> do aOld <- acquire catch ( do aNew <- restore (update aOld) _ <- release aNew return aNew ) ( \exception -> do _ <- release aOld throwIO (exception :: SomeException) ) {- | A version of uninterruptibleUpdateResource where you can also calculate a new value -} uninterruptibleUpdateResourceAndCalculate :: IO a -- ^ Acquire resource -> (a -> IO b) -- ^ Release resource with either updated or old value -> (a -> IO (a, c)) -- ^ How to update the value and calculate the result -> IO (a, c) -- ^ Updated value uninterruptibleUpdateResourceAndCalculate acquire release update = uninterruptibleMask $ \restore -> do aOld <- acquire catch ( do (aNew, c) <- restore (update aOld) _ <- release aNew return (aNew, c) ) ( \exception -> do _ <- release aOld throwIO (exception :: SomeException) )