[Haskell-cafe] Re: Asynchronous exception wormholes kill modularity

Simon Marlow marlowsd at gmail.com
Tue Apr 20 06:56:09 EDT 2010

On 09/04/2010 12:14, Bertram Felgenhauer wrote:
> Simon Marlow wrote:
>> On 09/04/2010 09:40, Bertram Felgenhauer wrote:
>>>       timeout t io = mask $ \restore ->  do
>>>           result<- newEmptyMVar
>>>           tid<- forkIO $ restore (io>>= putMVar result)
>>>           threadDelay t `onException` killThread tid
>>>           killThread tid
>>>           tryTakeMVar result
> I'm worried about the case when this function is called with exceptions
> already blocked. Then 'restore' will be the identity, and exceptions
> will continue to be blocked inside the forked thread.
> You could argue that this is the responsibility of the whole chain of
> callers (who'd have to supply their own 'restore' functions that will
> have to be incorporated into the 'io' action), but that goes against
> modularity. In my opinion there's a valid demand for an escape hatch
> out of the blocked exception state for newly forked threads.
> It could be baked into a variant of the forkIO primitive, say
>      forkIOwithUnblock :: ((IO a ->  IO a) ->  IO b) ->  IO ThreadId

I agree with the argument here.  However, forkIOWithUnblock reintroduces 
the "wormhole", which is bad.

The existing System.Timeout.timeout does it the other way around: the 
forked thread sleeps and then sends an exception to the main thread. 
This version work if exceptions are masked, regardless of whether we 
have forkIOWithUnblock.

Arguably the fact that System.Timeout.timeout uses an exception is a 
visible part of its implementation: the caller must be prepared for 
this, so it is not unreasonable for the caller to also ensure that 
exceptions are unmasked.  But it does mean that a library cannot use 
System.Timeout.timeout invisibly as part of its implementation.  If we 
had forkIOWithUnblock that would solve this case too, as the library 
code can use a private thread in which exceptions are unmasked.  This is 
quite a nice solution too, since a private ThreadId is not visible to 
anyone else and hence cannot be the target of any unexpected exceptions.

So I think I'm convinced that forkIOWithUnblock is necessary.  It's a 
shame that it can be misused, but I don't see a way to avoid that.


More information about the Haskell-Cafe mailing list