[Haskell-cafe] Printing of asynchronous exceptions to stderr

Bas van Dijk v.dijk.bas at gmail.com
Wed Nov 10 07:39:12 EST 2010


On Wed, Nov 10, 2010 at 10:50 AM, Mitar <mmitar at gmail.com> wrote:
> Hi!
> On Wed, Nov 10, 2010 at 8:54 AM, Bas van Dijk <v.dijk.bas at gmail.com> wrote:
>> A ThreadKilled exception is not printed to stderr because it's not
>> really an error and should not be reported as such.
>
> So, how to make custom exceptions which are "not really an error"?

I would say that any exception which isn't handled must be considered
an error, any exception which is handled is just an exceptional
situation.  For example if your application does not handle
DivideByZero than it's an error if one is thrown. However, your
application does handle MyTerminateException, so when one is thrown it
will just be an exceptional situation that your application can
handle.

>> What do you mean by "hanging there". Are they blocked on an MVar?
>
> I have something like this:
>
> terminated <- newEmptyMVar
> forkIO $ doSomething `catches` [
>                      Handler (\(_ :: MyTerminateException) -> return
> ()), -- we just terminate, that is the idea at least
>                      Handler (...) -- handle other exceptions
>                    ] `finally` (putMVar terminated ())
> takeMVar terminated
>
> The problem is, that if I send multiple MyTerminateException
> exceptions to the thread one of them is printed (or maybe even more, I
> do not know, because I have many threads). My explanation is that
> after the first one is handled and MVar is written, thread stays
> "active", just no computation is evaluating.  Because of that another
> exception can be thrown at the thread. And then it is not handled
> anymore and so the library prints the exception and exits the thread.

First of all, are you sure you're using a unique MVar for each thread?
If not, there could be the danger of deadlock.

Secondly, it could just be the case that when you throw your first
MyTerminateException it is catched by your handler which will then
just return as expected. Finally the 'putMVar terminated ()'
computation is performed. Now when you throw your second
MyTerminateException during the execution of this computation, there's
no handler any more to catch it so it will be reported.

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'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. You could rewrite your code to something like:

import qualified Control.Concurrent.Thread as Thread

main = do
  (tid, wait) <- Thread.forkIO $
                   doSomething `catches`
                     [ Handler (\(_ :: MyTerminateException) -> return ())
                     , Handler (...)
                     ]
  _ <- wait

Hopefully that helps.

> If I change things into:
>
> `finally` (putMVar dissolved () >> throwIO ThreadKilled)
>
> it works as expected.

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

Regards,

Bas

[1] http://hackage.haskell.org/package/threads


More information about the Haskell-Cafe mailing list