[Haskell-cafe] interrupting an accept()ing thread

Lukas Mai l.mai at web.de
Sat Jul 7 08:47:44 EDT 2007


Am Freitag, 6. Juli 2007 01:24 schrieb Lukas Mai:
> Hello, cafe!
>
> I have the following code (paraphrased):
>
> ...
> forkIO spin
> ...
> spin = do
>     (t, _) <- accept s   -- (*)
>     forkIO $ dealWith t  -- (**)
>     spin
>
> My problem is that I want to stop spin from another thread. The "obvious"
> solution would be to throw it an exception. However, that leaks a socket
> (t) if the exception arrives between (*) and (**). I could wrap the whole
> thing in block, but from looking at the source of Network.Socket it seems
> that accept itself is not exception safe; so no matter what I do, I can't
> use asynchronous exceptions to make spin exit.
>
> (Is this actually true? Should accept be fixed (along with a lot of other
> library functions)?)

Answering myself: I now think that the above isn't true. :-)

Quoting Control.Exception:

> Some operations are interruptible, which means that they can receive
> asynchronous exceptions even in the scope of a block. Any function which
> may itself block is defined as interruptible; this includes takeMVar (but
> not tryTakeMVar), and most operations which perform some I/O with the
> outside world. The reason for having interruptible operations is so that we
> can write things like
>
>       block (
>          a <- takeMVar m
>          catch (unblock (...))
>                (\e -> ...)
>       )
>
> if the takeMVar was not interruptible, then this particular combination
> could lead to deadlock, because the thread itself would be blocked in a
> state where it can't receive any asynchronous exceptions. With takeMVar
> interruptible, however, we can be safe in the knowledge that the thread can
> receive exceptions right up until the point when the takeMVar succeeds.
> Similar arguments apply for other interruptible operations like openFile.

If I understand this correctly, spin should be written as:

spin = do
    block $ do
        (t, _) <- accept s
        unblock (forkIO $ doStuff t) `finally` sClose t
    spin

Now t can't leak from spin because it's protected by block, while
the underlying accept() syscall is still interruptible by
asynchronous exceptions.

Lukas


More information about the Haskell-Cafe mailing list