Re: [c++-pthreads] The Ada example
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [c++-pthreads] The Ada example



> But what would you expect the effect of such a longjmp to be on the
> cancellation request?

I would expect cancellation to be implemented as a longjmp()
to a setjmp() in the standard thread wrapper provided by pthread_create().

If the user does a longjmp() while a synchronous cancellation
request is pending, I would expect the user longjmp() to act as a
cancellation point, and simply initiate the processing 
of the pending cancellation (rather than going to the user's
intended setjmp() target).

> And what happens if one of the cleanups run by this
> longjmp also calls longjmp?

If done by a user, that would be an ill-behaved cleanup handler,
comparable to a cleanup handler that does some other erroneous
thing (e.g., try to derefence a junk pointer) tha can cause
chaotic behavior (e.g., random changes to code and data, SIGSEGV,
SIGBUS).

If done by a compiler abd/or language-support runtime system,
written by disciplined programmers, chaining longjmps might be the
way longjmp with cleanup is actually implemented.  This would only
be done with full knowledge of, and reliance on, the internals of
the particular setjmp() and longjmp() implementation.

> More generally, how does Ada deal with this situation?  From looking over
> 
>   http://www.adaic.org/standards/95lrm/html/RM-9-8.html
> 
> it seems that task abort runs finalizers for objects, but doesn't interact
> with exception handling.  In C++, the only way to run object destructors is
> via exception handling, unless we want to define a whole new parallel
> concept, which I doubt.

You are right. The Ada language defines task abort processing as
distinct from exception processing.  There is no way in the
language to handle a task abort.  The finalizers are run, as the
stack of the task is unwound, and then the task terminates.  What
can be done inside a finalizer is limited, in an effort to make
sure the task terminates in a bounded time, but one cannot
eliminate all possibilities (e.g., a finalizer with an infinite
loop).

The theory is that if you find a need to "handle" task abort you
do this by entering a frame with a finalizer, and let the
finalizer do the handling.  That is, the theory is that there
should not be any abort-specific processing that needs to be done.
It should all be "cleanup" that should be done on exit from the
frame, regardless of the cause.  It works pretty well.

On the other hand, the GNAT (gcc) *implementation* of task abort
is to treat it as an exception.  This is a "special exception",
for which the compiler can generate handlers (and does so, to
implement the execution of finalizers) but the compiler does not
allow user code to contain such a handler or raise this exception
other than by task abort.

We did put in a compiler flag that allows the Ada runtime system
code to contain handlers for this exception, and to raise it, in
order to implement task abort and the associated language
features.  We use this in a very disciplined way, always eventually
re-raising the abort exception, so that abort is not "swallowed". 

There is another GNAT-specific extension that we found handy.
That is the "at end" handler.  That is a syntactic block of code
that is *always* executed on exit from the corresponding frame
(both normal exit and exceptional exit, including task
abort). That is the building block on which finalizers are
implemented.  Since we needed it any to implement finalization, we
found it nice to have around for situations where we had some
cleanup to do but did not want to pay the runtime overhead of
creating and destroying a local controlled object just for the
finalization effect.