[3.3] Followup to C++ forced unwinding

Nathan Myers ncm@cantrip.org
Thu May 1 16:53:00 GMT 2003


On Thu, May 01, 2003 at 09:17:22AM -0500, William E. Kempf wrote:
> 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.
> >
> > This alternative is almost tolerable.  I will explain below.
> > [...]
> > 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.

The paragraph quoted was an argument that library maintainers cannot 
be expected to manage libraries in which catch(...) clauses are skipped 
during unwinding.

While it is still common for C libraries to have threaded and non-
threaded variants, those variants tend to differ only in whether locking 
and unlocking operations are no-ops, rather than in the semantics of 
their basic control-flow constructs.  (Even so they are a serious 
nuisance.)  Imagine if you could not even test the code without running
both a threaded and an unthreaded program, because the control flow
paths would be different.

> > 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). 

There are three parties involved:

  - the thread being cancelled,
  - the complete program, the source of the cancellation,
  - the library which has state in the thread's execution context.

"Schedule" refers to the complete program's expectation that the 
thread will die, and soon enough, whatever that turns out to mean.  
If it doesn't, the program has failed.  The library author cannot be 
presumed to know anything about the goals of the complete program's 
author.  A library can only be coded according to reasonable 
best-practice rules which the program author must verify are compatible 
with its use in the complete program.

We have to make sure the rules are clear enough and simple enough
that it's practical to meet the needs of both library authors and
program authors.

> 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 without re-throwing, and isn't
> a violation of the cooperative cancellation mechanism, so long as the
> cancelled state remains true.

The use of termination (i.e. the call to std::terminate()) here is 
analogous to its use when exception handling has got too bollixed up 
to continue, such as when it is called by unexpected() (See 15.5.1).  

The purpose is to prevent spurious bug reports from users who have 
corrupted their process state all unawares.  Calling terminate() means 
the error is detected immediately and unambiguously.  As a default, 
therefore, it's reasonably safe.  A sophisticated user must be equipped 
to turn it off, though, and deal with the consequences,

I agree that it would be better to make the cancellation event behave
as an ordinary exception, but the thread library maintainers seem 
unwilling to implement it.  Programs that want that behavior would
have to add (perhaps) "set_cancellation_destructor(0)" to the common 
preamble at the beginning of main(), along with the 
"std::ios::sync_with_stdio(false);" and "signal(SIGPIPE, SIG_IGN);" 
we are already accustomed to doing. :-P

A compiler flag, perhaps "-Wrethrow", might enable warnings about 
falling off the end of a "catch(...)" block.

Nathan Myers
ncm@cantrip.org



More information about the Gcc-patches mailing list