[Haskell-cafe] Re: Asynchronous exception wormholes kill
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
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