Ticket #4035 (closed proposal: fixed)
Asynchronous exception wormholes kill modularity
|Reported by:||basvandijk||Owned by:||simonmar|
|Type of failure:||None/Unknown||Difficulty:|
|Test Case:||Blocked By:|
Functions like finally create, what I call, an asynchronous exception wormhole because they unblock asynchronous exceptions even if you call them in a blocked scope:
finally :: IO a -> IO b -> IO a a `finally` sequel = block $ do r <- unblock a `onException` sequel _ <- sequel return r
This hurts modularity.
I proposed solving this as follows:
finally :: IO a -> IO b -> IO a a `finally` sequel = do b <- blocked block $ do r <- (if b then unblock a else a) `onException` sequel _ <- sequel return r
Besides finally the following functions also have a wormhole:
In the interesting discussion that followed several other solutions were proposed (for example a block and unblock that count nesting levels).
Later, Simon Marlow proposed an even nicer solution:
mask :: ((IO a -> IO a) -> IO b) -> IO b mask io = do b <- blocked if b then io id else block $ io unblock
to be used like this:
a `finally` sequel = mask $ \restore -> do r <- restore a `onException` sequel sequel return r
I created this ticket so we won't forget about this problem.