
Hi Bas, Bas van Dijk wrote:
block $ do ... modifyMVar_ m f ...
From a quick glanse at this code it looks like asynchronous exceptions can't be thrown to this transaction because we block them. However the unblock in modifyMVar_ opens an asynchronous exception "wormhole" right into our blocked computation. This destroys modularity.
We can solve it by introducing two handy functions 'blockedApply' and 'blockedApply2' and wrapping each of the operations in them:
blockedApply :: IO a -> (IO a -> IO b) -> IO b blockedApply a f = do b <- blocked if b then f a else block $ f $ unblock a
blockedApply2 :: (c -> IO a) -> ((c -> IO a) -> IO b) -> IO b blockedApply2 g f = do b <- blocked if b then f g else block $ f $ unblock . g
I think it might be slightly more complicated than that. Any call to takeMVar or putMVar introduces it's own little wormhole, if it can't be serviced immediately, regardless of the mask-state of the thread. This is documented here (under interruptible operations): http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Exception... And is confirmed by a simple test (with GHC 6.10.4 on Linux): import Prelude hiding(catch) import Control.Concurrent import Control.Exception main = do chan <- newEmptyMVar done <- newEmptyMVar kill <- block $ forkIO $ do (takeMVar chan >>= putMVar done) `onException` putMVar done "Exception received during takeMVar" forkIO $ do killThread kill threadDelay 2000000 putMVar chan "No exception received during takeMVar" takeMVar done >>= putStrLn So we currently have: (1) a state in which asynchronous exceptions are propagated when execution blocks on an interruptible operation, and are deferred otherwise; and (2) a state in which asynchronous exceptions are not deferred. I agree with the documentation that (1) is at least sometimes necessary, but it might also have the same negative effect on modularity that you describe. So do we need a third state (3) in which asynchronous exceptions are deferred, even if execution blocks on a takeMVar or putMVar? If so, is the choice between (1) and (3) always localised, as in the example in the above documentation? Or is that choice also subject to modularity concerns? Regards, Matthew