
At Wed, 11 May 2011 13:02:21 +0100, Simon Marlow wrote:
There's no guarantee of the form that you mention - asynchronous exceptions can occur anywhere. However, there might be a way to do what you want (disclaimer: I haven't looked at the implementation of iterIO).
Control.Exception will have a new operation in 7.2.1:
allowInterrupt :: IO () allowInterrupt = unsafeUnmask $ return ()
which allows an asynchronous exception to be thrown inside mask (until 7.2.1 you can define it yourself, unsafeUnmask comes from GHC.IO).
Ah. I didn't know about unsafeUnmask. Is unmaskAsyncExceptions# low enough overhead that it would be reasonable to wrap every invocation of liftIO in unsafeUnmask? I'm now thinking it might be reasonable to execute all liftIO actions inside unsafeUnmask (with maybe a special liftIOmasked function for those few places where you don't want asynchronous exceptions). Most of the uses of mask are because you need two or more binds to execute without interruption, e.g.: bracket before after thing = mask $ \restore -> do a <- before -- Big problem if exception happens here -- r <- restore (thing a) `onException` after a _ <- after a return r But when bind sites are the only place an exception can be thrown, things get a lot simpler. For instance, it is perfectly reasonable to write: bracket before after thing = do a <- before thing a `finallyI` after a David