This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [3.3] Followup to C++ forced unwinding


Nathan Myers said:
> On Wed, Apr 30, 2003 at 07:50:39PM -0700, Ulrich Drepper wrote:
>> Richard Henderson wrote:
>> > On Wed, Apr 30, 2003 at 10:24:24PM -0400, Jason Merrill wrote:
>> >
>> >>Actually, I have an idea for how to DTRT here: give terminate() as
>> the destructor for the exception object in the forced unwind case.
>> So if the catch(...) block rethrows, all is good, but if we exit the
>> catch block some other way, we try to clean up the exception, which
>> calls terminate.
>> >
>> > Oh, cool.  This is a *much* nicer idea than I was contemplating.
>> Yes, I can agree to this.  Uli, does this satisfy?
>>
>> This means the object which is passed to the catch() code has a
>> destructor which is if necessary executed and can be prevented to be
>> called by explicitly rethrowing?  This sounds excellent.
>
> This alternative is almost tolerable.  I will explain below.
>
> First, though, we should run the choices by the ISO C++ committee  and
> the Boost.org list.  The latter, particularly, have done a long  and
> deep analysis of C++ thread-library semantics, and have been
> distributing for years a much-admired C++ binding to POSIX threads.
> Large systems have been constructed using this library, leaving both  a
> wealth of experience and a huge mass of existing good code.
>
> The Boost thread library implements delayed cancellation using a
> distinguished exception, and a "cancelled" flag that is checked at
> (many) cancellation points.  (The Boost implementation is unable to
> instrument many of the Posix cancellation points, such as read()
> and write(), so its threads don't necessarily die as early as they
> should.)  User code routinely catches this exception and re-throws it.
> Sometimes, though, the exception is caught and discarded, without
> grave consequences.

Small clarification here.  This is indeed the Boost.Threads design... but
it doesn't exist in the current implementation as released to the public. 
It will be released with the next Boost release, hopefully.

> If a cancellation exception is caught and discarded, then at the next
> cancellation point another is thrown.  This has turned out to be
> entirely practical.  Since the "change of state" that Ulrich speaks  of
> amounts to a boolean flag being set, to be checked at cancellation
> points, continuing out of a catch block has no more dire consequence
> than delaying the death of the thread (which executing the destructors
> does anyhow).  Since destructors may execute arbitrary code, to say
> that the thread state is somehow incapable of executing the code after
> the catch block seems to call for more support than a bald assertion,
> particularly given the Boost experience.
>
> Because the Boost.org thread library is almost a shoo-in candidate for
> adoption as part of the next Standard C++ Library, experience with it,
> and its designers and implementers, must not be taken lightly.  It
> would be a grave error to implement runtime semantics fundamentally
> incompatible with the Boost C++ threads library cancellation policy.

It's not necessarily a "soo-in", but there is at least interest.  Just
remember, though, that there isn't a large amount of real-world experience
with this cancellation mechanism.

> Now, about the consequences of registering terminate() as the
> destructor for the cancellation object...  First, the essential
> principle to observe in analyzing choices is that code compiled for  use
> by a thread has to work the same way in a non-threaded program,  such as
> a unit-test harness.  Nobody can afford to maintain libraries  that must
> behave differently when called by a thread.  Few can afford  to compile
> libraries differently when they might be linked with a  threaded
> program.

I only partially agree with this.  You often can't program to this ideal,
if for no other reason than because thread synchronization requires
significant overhead.  So many libraries do, in fact, get implemented to
compile differently when they might be linked with a threaded program.  In
fact, many C RTLs supplied by vendors have threaded and non-threaded
variants.

> Many of the requirements observed by exception-safe code are
> fortuitously very similar to those for thread-safe code, so it is
> common practice to write libraries that are both exception-safe and
> thread-safe.  Since exception-handling code is usually the least
> well-exercised part of any system, it is critical to keep it simple  and
> transparent.  Accommodating two different behaviors eliminates
> transparency.
>
> What makes the proposal almost tolerable is that it is considered  best
> practice under almost all circumstances to rethrow unidentified
> exceptions, and the overwhelming majority of existing code does so. Code
> that doesn't, though, tends to be that way for critically
> important reasons which we would be foolish to second-guess.  One
> such reason is that it is meant to be called from a language that
> cannot handle exceptions.

Precisely.

> What would probably be actually tolerable would be to copy some similar
> machinery from the existing C++ runtime, such as the unexpected(). (See
> ISO 14882, p345: section 18.6.2.2, [lib.unexpected.handler].)
>
> Instead of the cancellation object's destructor registering terminate()
> itself as the destructor, it would register another function which,  by
> default, calls terminate().  A program may call a function
> "set_cancellation_destructor(void (*)());" and pass it a function
> pointer to be called instead of terminate(), normally an empty
> function.  In that case discarding the cancellation would be like
> discarding any other exception, and it is up to the author of the  code
> to ensure that the thread dies on schedule.

On what schedule?  Cooperative cancellation is a request, not a demand. 
Ignoring the mechanism in which cancellation is carried out, with the
POSIX cooperative cancellation mechanism it is possible for a thread to be
"cancelled" but still never terminate.  For instance, if it never calls a
cancellation point, or turns off cancellation detection before it does,
the thread can continue on indefinately, even though a request to be
cancelled has occurred.  So, the only "schedule" is the explicit schedule
set by the thread being cancelled (not the thread making the request). 
Terminating (and I assume when you say terminate() you aren't talking
about a thread_terminate() that would abort only the thread) in this case
seems like a sledge hammer being used to enforce a very strict exception
handling mechanism.  I wouldn't have problems with an individual program
electing to do this, but the library should not do this (at least by
default, even if I can turn it off).  Forcing a re-throw would be a better
idea... but even that was rejected during the discussion on the Boost
list, for the reasons you gave above. IOW, it's sometimes valid/necessary
to catch with out re-throwing, and isn't a violation of the cooperative
cancellation mechanism, so long as the cancelled state remains true.

-- 
William E. Kempf



Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]