<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On 2 July 2016 at 17:25, Edward Z. Yang <span dir="ltr"><<a href="mailto:ezyang@mit.edu" target="_blank">ezyang@mit.edu</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Excerpts from Simon Marlow's message of 2016-07-02 05:58:14 -0400:<br>
<span>> > Claim 1: Here is some code which reimplements 'unblock':<br>
> ><br>
> >     import Control.Exception<br>
> >     import Control.Concurrent<br>
> >     import Control.Concurrent.MVar<br>
> ><br>
> >     unblock :: IO a -> IO a<br>
> >     unblock io = do<br>
> >         m <- newEmptyMVar<br>
> >         _ <- forkIO (io >>= putMVar m)<br>
> >         takeMVar m<br>
> ><br>
> ><br>
> This isn't really an implementation of unblock, because it doesn't enable<br>
> fully-asynchronous exceptions inside io.  If a stack overflow occurs, it<br>
> won't be thrown, for example.  Also, io will not be interrupted by an<br>
> asynchronous exception thrown to the current thread.<br>
<br>
</span>Oh, that's true. I suppose you could work around this by passing<br>
on an asynchronous exception to a child thread that is unmasked<br>
using forkIOWithUnmask, although maybe you would consider that<br>
cheating?<span><br></span></blockquote><div><br></div><div>Yes, you can use forkIOWithUnmask as a way to break out of mask.  Perhaps for that reason it should have "unsafe" in the name, but I think it's hard to use it by accident.<br></div><div><br></div><div>I actually do agree with you that the "modularity" provided by mask isn't really useful.  But my reasoning is a bit different.<br><br></div><div>The caller of mask is saying "I want asynchronous exceptions to only occur at known places.".  Those known places are interruptible operations, and library code (because we can't know whether library code performs an interruptible operation or not).  From the point of view of the caller of mask, they cannot tell the difference between library code that invokes an interruptible operation, and library code that calls "unblock".  So it would be perfectly fine to provide an "unblock" that re-enables fully asynchronous exceptions.<br><br></div><div>(indeed I think this was kind of what I had in mind with the original block/unblock, but I didn't articulate the argument clearly enough when everyone was asking for "mask")<br><br></div><div>However, things are a bit different with uninterruptibleMask.  Here the caller is saying "I don't expect to see *any* asynchronous exceptions, either in my code or from library code".  So clearly an unblock cannot undo an uninterruptibleMask.<br><br></div><div>Having said all this, I don't think the current API is necessarily bad, it just provides more guarantees than we really need, and perhaps it's a bit less efficient than it could be, due to the need to pass the IO action to mask.  But we would still need to do this for uninterruptibleMask, and having the API of uninterruptibleMask be the same as mask is good.<br></div><div><br><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span>
> We already have a way to allow asynchronous exceptions to be thrown within<br>
> a mask, it's called allowInterrupt:<br>
> <a href="http://hackage.haskell.org/package/base-4.9.0.0/docs/Control-Exception.html#v:allowInterrupt" rel="noreferrer" target="_blank">http://hackage.haskell.org/package/base-4.9.0.0/docs/Control-Exception.html#v:allowInterrupt</a><br>
<br>
</span>Well, it's different, right?  allowInterrupt allows asynchronous exceptions to<br>
be thrown at a specific point of execution; unblock allows asynchronous<br>
exceptions to be thrown at any point while the inner IO action is<br>
executing.  I don't see why you would allow the former without the<br>
latter.<span><br></span></blockquote><div><br></div><div>Ok, so the point I was trying to make was that the idea of blocking to allow asynchronous exceptions to be thrown inside a mask is fully sanctioned, and we made an API for it.  But you're quite right that it's not exactly the same as unblock.<br></div><div> <br></div></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span>
> > You could very well argue that interruptible actions are a design flaw.<br>
> ><br>
><br>
> I disagree - it's impossible to define withMVar without interruptible mask.<br>
<br>
</span>What about this version of withMVar using uninterruptible? (Assume<br>
no other producers.)<br>
<br>
    withMVarUninterruptible :: MVar a -> (a -> IO b) -> IO b<br>
    withMVarUninterruptible m io =<br>
      uninterruptibleMask $ \restore -> do<br>
        a <- restore (takeMVar m)<br>
        b <- restore (io a) `onException` putMVar m a<br>
        putMVar m a<br>
        return b<br>
<br>
I don't think it is quite right, as there is race between when<br>
takeMVar unblocks, and when the uninterruptible mask is restored.<br>
But perhaps the primary utility of interruptible masks is to<br>
let you eliminate this race.<span><br></span></blockquote><div><br></div><div>Exactly!  This race condition is the reason for interruptible operations. <br></div><span></span><br></div><div class="gmail_quote">[snip]<br></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<span><font color="#888888"><br>
Edward<br>
</font></span></blockquote></div><br></div><div class="gmail_extra">Cheers<br></div><div class="gmail_extra">Simon<br></div></div>