Ticket #4035 (closed proposal: fixed)
Asynchronous exception wormholes kill modularity
| Reported by: | basvandijk | Owned by: | simonmar |
|---|---|---|---|
| Priority: | high | Milestone: | 7.0.1 |
| Component: | libraries/base | Version: | 6.12.2 |
| Keywords: | Cc: | iavor.diatchki@… | |
| Operating System: | Unknown/Multiple | Architecture: | Unknown/Multiple |
| Type of failure: | None/Unknown | Difficulty: | |
| Test Case: | Blocked By: | ||
| Blocking: | Related Tickets: |
Description
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:
- Control.Exception.finally/bracket/bracketOnError
- Control.Concurrent.MVar.withMVar/modifyMVar_/modifyMVar
- Foreign.Marshal.Pool.withPool
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.
Change History
Note: See
TracTickets for help on using
tickets.
