Re: [c++-pthreads] Re: C++ and POSIX Threads Mailing List
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [c++-pthreads] Re: C++ and POSIX Threads Mailing List



>> * Should cancellation throw an exception?
> Of course.

Not clear.

First, you are stuck with the POSIX thread implmentations.
They do not provide any way of catching thread cancellation.
All they do is guarantee that somehow the cleanup routines
will be executed and then the thread will terminate.

Furthermore, the POSIX/UNIX standards do not define the
semantics for continuation of a thread after cancellation.  If you
are building on an existing C-language thread implementation, or
want to be compatible with one, once a thread is cancelled, you
can't count on doing much/anything in that thread other than
excecuting cleanup handlers.

Back when the POSIX threads standard was being balloted, one of
the things the Ada folks asked for was that is should be possible
to do a longjmp() from a cancellation cleanup routine, and that
longjmp() would execute the stack of cleanup handlers out to the
point of the corresponding setjmp().  The balloting group was
immovable on this.

As a result, we had to accept that Ada asynchronous transfers of
control (including task abort) are not fully interoperable with C
thread cancellation, but had been ported to all POSIX and UNIX
thread implementations, and allows mixing C and Ada code in a
single program so long as one does not try to cancel a C thread
using an Ada abort, cancel an Ada task (implemented as a C thread)
using pthread_cancel, or mix handlers of the two different
languages in the same nest of calls.

I guess that for C++ you do want to preserve interoperability with
C, up to some point.  However, if you want good integration with
exception handling, you may have to make some compromise like we
did with Ada. 

If you do make exceptions part of your POSIX C++ API, you should
think hard about the software engineering issues also.  From my 20
years or so experience using and implementing Ada exceptions, I
have found that they are mixed blessing.  Exceptions are already
pretty nasty from the point of view of info. hiding, proofs of
correctness using pre/postconditions, structured testing, etc.  --
because they introduce a huge potential number of alternate flows
of control -- a huge new potential for spaghetti code.  In
general, you cannot tell for certain that a given piece of code
will *not* throw some kind of exception, especially as the code is
"maintined", and *especially* if you have asynchronous forms of
exception like cancellation.

Exceptions are useful in very limited ways, because when one gets
to the handler one generally can assume very little about the
system state (since the exception may have come from virtually
anywhere).

A good use is for general error recovery, at a coarse-grained level,
where one does not need to assume much about system state.  Recovery
consists of logging the failure, and then either terminating the
process or restarting by setting all state that might have been
affected to known valid values. The latter still is often a problem,
especially as a system evolves and operations hidden several layers
down in abstractions develop side-effects on persistent state that
were not anticipated when the recovery code was written.

I've also seen exceptions used for two good local uses:
(1) exiting deep recursions, as in a depth-first search.
(2) catching "expected" exceptions, such as reaching the end of
    an input stream

Outside of these just about every example I've seen of "cute"
uses of exception handling ends up having some flaw.

Whe we did the POSIX Ada bindings (IEEE 1003.5*) the working group
was enamored of exceptions, and made the choice to map all system
call failures to exceptions.  This has made the API awkward to use
for local recovery, and added code to the binding.  The
implementor has to code something like (excuse the Ada code):

if c_system_call(...) = -1 then raise POSIX_Error; end if;

And then the user has to code something like (excuse the Ada):

begin
   ada_system_call(...);                
exception
   when POSIX_Error => ...recover...
end;

The latter is not only cumbersome to read and write, but it also
imposes runtime overhead.  To keep recovery possible, one needs to
keep the scope of the handler very local.  The larger the scope of
the handler, the more things might have gone wrong, and the less
hope one has of writing correct recovery code.  (I guess you know
that error recovery code is generically the least tested and most
error-prone code in a system.  Just try exhaustive testing of
exception handling code in any reasonably large system.)  However,
even putting the handler right around a single system call does
not make recovery easy; POSIX/UNIX standards allow system calls to
fail for nonstandard and undocumented reasons, so when one gets to
the handler one is stuck with a mess trying to figure out what
actually failed.  Many a time I've wished for the old C-language
interface, with just the if-statement, for system calls.

On the other hand, if you expect all your system calls to succeed,
and only want to do global recovery, raising exceptions is the way
to go.  The programmer does the thing most C programmers do in real
life (i.e., ignore the function return value), and if anything goes
wrong it raises an exception.

>> * What happens if the exception is caught, and not rethrown?
> Nothing special. Pls see "example" in the message referenced below.

This violates basic principles of abstraction and information
hiding.

1) The expected effect of cancellation is to terminate a thread.
Anything that does not result in termination of the thread breaks
the abstraction.

2) If you allow local handling of cancellation, without
rethrowing, a hidden operation, many levels deep, may nullify the
intended effect of cancelation.  This becomes a serious
maintenance problem, as some newbie programmer decides to protect
his favorite critical section against cancelation and forgets to
rethrow it.  Suddenly, you have an intermittent bug that will be
*very* hard to track down.  How often will a thread be cancelled,
and how often will it be cancelled at that point? Imagine trying
to reproduce this kind of failure.

--Ted Baker