Excerpts from Simon Marlow's message of 2016-07-02 05:58:14 -0400:
> > Claim 1: Here is some code which reimplements 'unblock':
> >
> > import Control.Exception
> > import Control.Concurrent
> > import Control.Concurrent.MVar
> >
> > unblock :: IO a -> IO a
> > unblock io = do
> > m <- newEmptyMVar
> > _ <- forkIO (io >>= putMVar m)
> > takeMVar m
> >
> >
> This isn't really an implementation of unblock, because it doesn't enable
> fully-asynchronous exceptions inside io. If a stack overflow occurs, it
> won't be thrown, for example. Also, io will not be interrupted by an
> asynchronous exception thrown to the current thread.
Oh, that's true. I suppose you could work around this by passing
on an asynchronous exception to a child thread that is unmasked
using forkIOWithUnmask, although maybe you would consider that
cheating?
> We already have a way to allow asynchronous exceptions to be thrown within
> a mask, it's called allowInterrupt:
> http://hackage.haskell.org/package/base-4.9.0.0/docs/Control-Exception.html#v:allowInterrupt
Well, it's different, right? allowInterrupt allows asynchronous exceptions to
be thrown at a specific point of execution; unblock allows asynchronous
exceptions to be thrown at any point while the inner IO action is
executing. I don't see why you would allow the former without the
latter.
> > You could very well argue that interruptible actions are a design flaw.
> >
>
> I disagree - it's impossible to define withMVar without interruptible mask.
What about this version of withMVar using uninterruptible? (Assume
no other producers.)
withMVarUninterruptible :: MVar a -> (a -> IO b) -> IO b
withMVarUninterruptible m io =
uninterruptibleMask $ \restore -> do
a <- restore (takeMVar m)
b <- restore (io a) `onException` putMVar m a
putMVar m a
return b
I don't think it is quite right, as there is race between when
takeMVar unblocks, and when the uninterruptible mask is restored.
But perhaps the primary utility of interruptible masks is to
let you eliminate this race.
Edward