Boost logo

Boost :

From: Darryl Green (green_at_[hidden])
Date: 2002-08-12 22:58:18


I'm trying to clarify a point in this thread not broaden the discussion. I
can't seem to do that by responding to your individual points so I'll make
this one last attempt to state mine.

As I recall of this thread you have stated that propogating exceptions can
be dangerous. I agree it *can* be.

I have been trying to make the point (poorly it would seem) that async
termination can be at least as dangerous. You want to discourage users from
thinking that exceptions handling is automagically "right" merely because
the lib propogates them across thread boundaries. I really don't see why
anyone would think that any more than they would think that exception
handling was automagically right beause C++ propogates them up the call
stack.

I don't see that propogating the exception is any worse than terminate()
without an app specific handler. In fact I think it is much better.

I think that a useful app specific terminate handler in MT C++ is an
abomination that I'd rather avoid. Consider the scenario I've been
describing where an app interacts with a lib in a way that isn't explicitly
MT/async but the lib is internally MT:

It would be foolish to try to do anything with the lib after it threw an
exception indicating it was "broken". This is independent of whether it came
from another thread. To deal with the case where an exception is thrown from
another thread, the lib author should apparently be as knowledgeable as you
are on threading and C++ and have done everything right so that the
exception will propogate correctly. The author should carefully hand craft
this mechanism to suit each and every thread usage scenario. This handling
should be written and reliable even (especially) where the exception in
question is irrecoverable/not specifically handled by the lib (ie. it
indicates "this lib is in an irrecoverable state, time to stop"). When this
exception propogates my code does - well - whatever it would have done with
no threading in the lib - probably just rethrows and relies on my own
beautifully hand crafted MT aware exception handling to sort it all out :-).
The lib isn't particularly "mission critical" but the app which uses it has
portions which are more so.

Lets try to deal with this using terminate instead: I need to handle the
termination of the library thread and the fact that that library has become
unuseable by bringing the app as a whole to a civilised halt in an app (not
lib) dependent way. It is not clear to me what state "my" threads will be in
at the time terminate() is called as a consequence of an unhandled exception
in another thread - if I can assume that no stacks have been unwound except
(possibly) the thread that threw, I guess I can recover in a terminate
handler - though its going to be hard because I (assume) the handler is
running in the context of a thread I didn't create. I'm going to have to
somehow synchronise with and transfer control to thread(s) I do control. I'm
going to have to do a join() so that my terminate handler won't return. Does
this sound like a good thing? Is relying on these undefined behaviours a
good thing (or are they somehow defined)? Have you proposed a better way?

By the way, through all this I've been talking about "returning" the
exception. I don't know that I really need that to magically work - I'd
settle for some sort of result (another exception would make the most sense)
from the synchronisation point that indicated that thread termination was
the cause of the problem.

> -----Original Message-----
> From: William Kempf [mailto:williamkempf_at_[hidden]]
>
> Sometimes you have to die, unfortunately. Sometimes there's
> simply no way
> to actually recover from an exception. Granted this usually
> occurs because
> code outside of your control failed to deal with the exception
> appropriately, but this is the case with or without threads,
> and I believe
> that forced terminate() is more likely to get programmers to put the
> appropriate exception handling code in the place where it's
> most likely they
> can deal with it correctly.
>
snip...
>
> The lib that creates the thread is the only one that's likely
> to have any
> chance of correctly handling an exception on that thread.
> You are even less
> likely to be able to do anything useful with a passed
> exceptions several
> levels of libraries later.
>
snip...
>
> Yes, rereading it I see that I did lose you. The library
> that starts the
> thread is likely the only library prepared to deal with an
> exception thrown
> out of that thread. Everyone else is likely left with having
> to terminate()
> any way.
>
snip...
>
> Yes. Typical thread usage depends on the thread to be
> running between the
> calls to create it and join it. Exception thrown in between in this
> scenario are likely to result in deadlock or other erroneous
> behavior.
> Either a universal mechanism for determining this needs to be
> applied (and
> polling won't work as it causes race conditions), or case
> specific coding is
> required. I can't envision a universal mechanism for this
> scenario, and
> that's why I'm advocating terminate(), to force the user to write the
> specific exception handling code to deal with this (or not,
> if they feel
> terminate() is appropriate, which often is).
>
snip

>
> An async call is a call in which you invoke the call, which returns
> immediately, and you receive the response from the call at a
> later point
> through some polling mechanism. This might be a little more
> liberal than
> some definitions since it allows for things such as the Win32
> message queue
> to be viewed as a mechanism for performing asynchronous calls, while
> technically since everything occurs within a single thread
> it's not truly
> asynchronous. But even if you remove that I can still think
> of 3 truly
> asynchronous calling conventions.
>
> 1) Within a single process, across thread boundaries.
> 2) Across process boundaries.
> 3) Across machines or other hardware boundaries.
>

Is this a contest? If thre is some point to this I've missed it?

snip

>
> Actually, sometimes you have to deal with the mechanism as
> well. There are
> implementation differences between the various mechanisms
> that are likely to
> make a single universal solution impossible. But at least
> with the function
> object wrappers you can achieve a universal interface for the
> various forms
> of async calls. (Or there may well be a universal solution
> to this that I'm
> not yet seeing.)

I didn't mean a universal implementation - only a universal interface - so
we agree there.

>
> >I still find it perverse that the "safe thing to do" if a
> poorly written
> >thread throws an exception is to crash the other threads
> (for the lack of a
> >better term for the state of the system when terminate() is called).
>
> This is the behavior of at least one thread today. Also, but
> reading the
> rules for the underlying thread APIs (or lack there of in
> this case) it's
> very possible, even probable, that there are implementations
> today that
> behave the way I've suggested (i.e. they terminate() on
> uncaught exceptions
> as well). So, even if I agreed, I can't gaurantee you that
> there won't be
> some thread in your application that will cause your application to
> terminate() in any event.

Well I guess we had better catch all exceptions then - maybe in a lib
threading lib... Oops - haven't we been here before?

>
> >However, it seems this is beyond the power of your lib to
> deal with, so
> >I'll
> >let you off :-)
>
> Well, I believe it is, but I'm still open to be proven wrong.

The more I think about it the more I can live with odd behaviour of main.
The can't be said for the odd behaviour of terminate() as I understand it.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk