[SPAM] - [c++-pthreads] Re: pthread_cancel and EH: let's try this again - Email found in subject

Ted Baker baker at cs.fsu.edu
Mon Jul 18 12:27:11 UTC 2005


I've been reading this thread from the beginning, and holding back
from commenting because my views on this subject are likely to be
dismissed as biased by my Ada background.

However, among this discussion (as in the last incarnation) is not
converging very well.  I have observed a few points in the
discussion that are consistent with my experience and may deserve
repeating.

Both thread cancellation (POSIX style) and what I'll loosely call
"asynchronous" exceptions (whether raised in a truly asynchronous
manner or by polling for a flag that can be set by an asynchronous
event) may be seductive intellectually, but they are a nightmare
when it comes to both implementation and to use in any application
where reliability and efficiency matter.  Ada is now stuck with
them.  I think C programmers should refrain from using thread
cancellation, and C++ would be better off not opening this
Pandora's box.

(I speak as a convert.  I was one of the strongest proponents of
adding asynchronous task abort to the Ada 95 language, and it is
probably true that if I had opposed it then this feature might not
have been part of the language.)
 
In particular, I would recommend that you simply specify that the
uses of thread cancellation and C++ are incompatible.  If you want
to use C++ you don't use thread cancellation.  This could be
enforced fairly simply with cooperation from the implementors of
the C thread libraries.

Given all the discussions up to this point, I don't think you can
honestly expect to define a solution that will deliver thread
cancellation in form that can be used by C++ programmers without
some nasty surprises.  Responsibility for exposing the world to
these new hazards will be a burden you will have to live with for
the rest of your life.

If you insist on having thread cancellation and/or asynchronous
exceptions in C++, you must be prepared to give up one one or more
of the design goals that have been expressed in this discussion.
At worst, you will end up compromising on *every* goal.  It would
be better to focus on ranking what you are willing to give up in
order to have this feature, and then pick a solution that
compromises least on the goals that matter most to you.

If you have thread cancellation or asynchronous exceptions, you
need to guarantee that they do not interrupt at least certain
blocks of code, which must be run to completion in order to
preserve critical invariants (both in the runtime system and
application).  The best way to do this is to make protection the
default, i.e., implement cancellation or async. exceptions by
polling code that is inserted by the compiler and/or runtime
system, and further have an application-settable flag that can
enable/suppress the polling effect where desired.  This seems to
have been the prevailing thinking behind POSIX thread
cancellation.

C++ destructors are examples of blocks of code that should by
default not be interruptible by asynchronous exceptions, and which
one should normally be able to assume will never throw (or
propagate) an exception.  Anything less leads to chaos.  (The more
you think about it, the more things you will decide ought to be
atomic with respect to thread cancellation/async. exceptions, and
the more you will wonder whether they are such a good thing.)

Library routines written in C may "throw" cancellation.  This is a
big deal, since before you added thread cancellation or
async. exceptions there was no way C-language code could throw an
exception. (Of course it could call some C++ code that throws an
exception, but that is unlikely, and is fairly easy to avoid or
narrowly constrain by programming practice.  It is routine, normal
and practically necessary for C++ code to be able make use of C
libraries.)

This is not purely a problem created by making thread cancellation
into an exception.  If you had cancellation at all, you already
had the problem that some C code you call may call a function
includes a thread cancellation point.

However, if you used thread cancellation with C++ code that
expects C++ cleanup on stack unwinding you knew you were operating
outside the safe confines of well defined behavior.

The new problem with legitimizing this as a proper C++ exception
is that people will now think it is OK to use thread cancellation
with C++, and they will expect that normal C++ cleanup mechanisms
(including destructors) will work correctly with thread
cancellation.

I don't think you can deliver a safe solution, with acceptable
overhead and comprehensible to ordinary programmers.  So, wouldn't
it be more responsible to declare this combination as unsafe,
and let this discussion die?

--Ted

...
> > | I disagree; one of the problems with mapping cancellation requests to
> > | C++ exceptions is that certain system calls that did not throw before
> > | are changed into functions that do, and that existing (destructor or
> > | non-destructor) code won't always be able to deal with that.
> >
> > If the function (or destructor) has a throw() specification, it will
> > continue not to throw.  
> 
> Yeah, but at what cost?  Termination does not seem like a very
> desirable response to a cancellation request that happens to occur
> before a 'C' library call that ordinarily wouldn't throw.
> 
> > If it did not have that annotation then it was assumed to throw.
> 
> By the compiler, maybe, but not to programmers in general.  Few
> programmers habitually use throw() on their destructors, but nearly
> everybody who has learned to write exception-safe code assumes that no
> destructor throws unless otherwise specified.



More information about the c++-pthreads mailing list