[Haskell-cafe] Printing of asynchronous exceptions to stderr

Mitar mmitar at gmail.com
Wed Nov 10 08:39:58 EST 2010


On Wed, Nov 10, 2010 at 1:39 PM, Bas van Dijk <v.dijk.bas at gmail.com> wrote:
> First of all, are you sure you're using a unique MVar for each thread?
> If not, there could be the danger of deadlock.

I am.

> Finally the 'putMVar terminated ()' computation is performed.

Yes. But this should be done fast if MVar is empty, so putMVar does
not block. And also MVar is filled, because takeMVar does not block at
the end. So It is not that putMVar is interrupted (and thus no
handler) but something after that.

> Finally, your code has dangerous deadlock potential: Because you don't
> mask asynchronous exceptions before you fork it could be the case that
> an asynchronous exception is thrown to the thread before your
> exception handler is registered. This will cause the thread to abort
> without running your finalizer: 'putMVar terminated ()'.  Your
> 'takeMVar terminated' will then deadlock because the terminated MVar
> will stay empty.

I know that (I read one post from you some time ago). It is in TODO
commend before this code. I am waiting for GHC 7.0 for this because I
do not like current block/unblock approach.

Because blocked parts can still be interrupted, good example of that
is takeMVar. The other is (almost?) any IO. So if code would be:

block $ forkIO $ do
  putStrLn "Forked"
  (unblock doSomething) `finally` (putMVar terminated ())

There is a chance that putStrLn gets interrupted even with block. So
for me it is better to have a big TODO warning before the code than to
use block and believe that this is now fixed. And when I will add some
code one year later I will have problems. Of course I could add a
comment warning not to add any IO before finally. But I want to have a
reason to switch to GHC 7.0. ;-)

I am using such big hack for this:

-- Big hack to prevent interruption: it simply retries interrupted computation
uninterruptible :: IO a -> IO a
uninterruptible a = block $ a `catch` (\(_ :: SomeException) ->
uninterruptible a)

So I can call "uninterruptible $ takeMVar terminated" and be really
sure that I have waited for thread to terminate. Not that thread in
its last breaths send me some exception which interrupted me waiting
for it and thus not allowing it to terminate properly. Of course if
you now that you do not want that your waiting is interrupted in any
way. Maybe there could be an UserInterrupt exception to this
uninterruptible way of handling exceptions. ;-)

> I've made this same error and have seen others make it too. For this
> reason I created the threads[1] package to deal with it once and for
> all.

I know. I checked it. But was put off by mention of "unsafe".

> Strange. It would help if you could show more of of your code.

I am attaching a sample program which shows this. I am using 6.12.3 on
both Linux and Mac OS X. And I run this program with runhaskell
Test.hs. Without "throwIO ThreadKilled" it outputs:

Test.hs: MyTerminateException
MVar was successfully taken

With "throwIO ThreadKilled" is as expected, just:

MVar was successfully taken

So MVar is filled. What means that thread gets exception after that.
But there is nothing after that. ;-) (At least nothing visible.)

-------------- next part --------------
A non-text attachment was scrubbed...
Name: Test.hs
Type: application/octet-stream
Size: 1200 bytes
Desc: not available
Url : http://www.haskell.org/pipermail/haskell-cafe/attachments/20101110/85221acd/Test.obj

More information about the Haskell-Cafe mailing list