__attribute__((cleanup(function)) versus try/finally

12 views
Skip to first unread message

Alexander Terekhov

unread,
May 6, 2003, 5:52:32 PM5/6/03
to
Please take a look at:

http://gcc.gnu.org/ml/gcc/2003-05/msg00528.html

That "__attribute__((cleanup(function))" thing is basically a
DESTRUCTOR 'attached' to a local variable in C. Well, it
currently lacks the throw()-nothing semantics [as it SHOULD...
of course, without rather brain-damaged catch(...)-unwinding
prior to std::unexpected() invocation that is imposed by the
totally broken (in this respect) C++ standard]. Other than
that, I really like the idea. To me, it certainly looks much
better than rather archaic try/finally thing. Oh, BTW, this
is about Pthread cleanup stuff (don't miss Butenhofs article
referenced in the message above). What do you think? TIA.

regards,
alexander.

David Butenhof

unread,
May 7, 2003, 9:40:28 AM5/7/03
to
Alexander Terekhov wrote:

(Parenthetical definition: DETACHED handlers, like C++ destructors and POSIX
cleanup handlers, represent an independent function that runs in some
context nested on top of the thread's call frame at the point of exception
unwind. There may be any number of call frames separating it from the
original call frame in which it was established. ATTACHED handlers, in
contrast, represent a block of code within the call frame that established
the handler, and require that any nested frames have been unwound and
finalized before activation.)

There are two real differences, in terms of syntax/semantics:

1) The cleanup functions are DETACHED handlers, whereas try/catch/finally
handlers are ATTACHED.
2) The cleanup attributes are apparently associated with individual
variables whereas exception handlers are associated with code ranges.

Each difference provides advantages and disadvantages. The basis of
exception support on the OpenVMS platform is "condition handling", which
associates a DETACHED handler with each call frame on the stack. The basic
exception mechanism in Tru64 UNIX (via libexc.so) and in the standard IA64
calling standard also provides DETACHED handlers, via static pointers in
the static procedure descriptor, and associate those with code ranges via
PC mapping. The C++ runtime, and our DEC C __try/__except extension, are of
course based on those DETACHED handlers, but they use additional launch pad
mechanisms to bring caught exception execution streams back into the code
block, making them appear to the application as ATTACHED handlers.

POSIX cleanup handlers, for simplicity, are specified as DETACHED handlers.
Currently, although the basic exception mechanisms on both Tru64 UNIX and
OpenVMS are already DETACHED handlers, we actually "doubly indirect" the
launch sequences through the ATTACHED handler mechanism. Part of this is
because these already provide the finer code range scoping we need, whereas
doing it "more directly" on the lower level DETACHED handler would require
basically duplicating the higher level mechanism anyway.

This would be an issue for the cleanup function attribute, as a variable's
scope need not be the procedure. You'd need a calling standard and
exception infrastructure that provides support for multiple nested
exception scopes within a procedure, whether you use an existing mechanism
(such as the scoped handling provided by a C++ try/catch/destructor
package) or built it yourself -- into something that will in the end much
resemble that scoped exception mechanism.

ATTACHED handlers are simple to use, as the handler runs in the source scope
of the frame. You can use local variables, for example, though often some
caution (and perhaps 'volatile') may be required on some implementations to
ensure consistency across exception unwinding. Of course, on the other
hand, C++ destructors are DETACHED handlers -- but then the model deals
with "an object" (to which the destructor has an implicit pointer) rather
than with "the objects" within a code scope as in try/catch(...) or an Ada
style try/finally.

DETACHED handlers allow for the option of CONTINUE semantics, where the
entire chain of handlers can decide that there's no need to unwind or
finalize any frame... the application can continue from the point where the
exception was raised. You can't do this with ATTACHED handlers, because
they require that the call stack be unwound as they're activated. C++
doesn't allow continuable exceptions, and the POSIX cleanup model doesn't
require them. Both OpenVMS condition handling and Tru64 UNIX libexc, for
what it's worth, are designed to allow continuation.

I personally prefer ATTACHED handlers, except where continuable exceptions
are considered important. And generally I think that continuable exceptions
are a bad idea. ;-)

I don't believe there's any substantial implementation simplicity, or
performance advantage, in DETACHED handlers, because implementing
sub-procedure level code range scoping on most systems probably requires
essentially building in an equivalent level of infrastructure. Granted, if
you don't need to support ATTACHED application handlers anyway (which you
do, in order to do C++ try/catch), you could build a slightly simpler PC
mapping above the calling standard's procedure descriptor level to dispatch
an appropriate DETACHED handler from a table maintained by the compiler and
runtime. But given that you need to do that anyway for C++ ATTACHED
handlers, I suspect it's overall simpler to build that mechanism into the
common exception runtime and "reuse" it. (Remember that the C++ mechanism
has already been built, even though it may be currently packaged as part of
the C++ runtime rather than in a common runtime; it'll be easier to
generalize that than to throw it away and rebuild it on top of a lower
level sub-procedure DETACHED handler mechanism.)

--
/--------------------[ David.B...@hp.com ]--------------------\
| Hewlett-Packard Company Tru64 UNIX & VMS Thread Architect |
| My book: http://www.awl.com/cseng/titles/0-201-63392-2/ |
\----[ http://homepage.mac.com/dbutenhof/Threads/Threads.html ]---/

Alexander Terekhov

unread,
May 7, 2003, 10:33:28 AM5/7/03
to

David Butenhof wrote:

[... DETACHED handlers vs ATTACHED handlers ...]

My main problem with try/finally is that try/finally is indeed a
"handler". I don't want cleanup stuff act like a "handler" in the
sense that I don't want it to cause unnecessary unwinding for the
unexpected exceptions. I'd have no problems with some "attached"
try/finally-like beast that wouldn't cause unnecessary unwinding
for the unexpected exceptions. In my book, unexpected exceptions
shall cause std::unexpected() invocation at throw point. Another
problem is that code in the finally scope is allowed to "steal"
the original exception via throwing another one; I hate that too.

Thanks for your detailed explanation.

regards,
alexander.

David Butenhof

unread,
May 7, 2003, 1:50:52 PM5/7/03
to
Alexander Terekhov wrote:

Once you've changed the program state (by running any form of cleanup), the
program state presented to your std::unexpected() handler, or the contents
of a final core dump, no longer represent the actual state of the program
at the time the exception was raised. It's become inaccurate and
potentially misleading. You may have the static call stack, but you have
very likely lost critical information about how the program came to that
point.

Analysis tools that take advantage of the program state (including but not
restricted to the call stack) need to happen BEFORE (or instead of)
cleanup. Stack unwind is just one more form of cleanup. ATTACHED handlers
cleanup the call stack in sequence with the cleanup of other program state:
the result is, in some sense, a more accurate representation than to leave
the call stack intact while removing all the evidence of how it got that
way.

In the case of a fatal error such as SIGSEGV, an immediate core dump and
termination is best, capturing the ENTIRE state of the program (including
call stack) for analysis. Once you've changed anything, the rest is out of
context. For other "exceptional" conditions, the balance is less clear; but
I do think you can't have it both ways. Either you clean up (and accept
that you've lost critical analysis information, which might as well be lost
consistently), or you preserve everything as it is without any handling.

(Though I suppose you might compromise by writing a core file at the time
the initial exception is raised and archiving it for later analysis, while
the live program goes on to run cleanup handlers, unwind, and potentially
recover. Again, though, the unwind doesn't hurt... and if you're really
going to recover it's going to be necessary eventually anyway.)

Nor does having DETACHED handlers instead of ATTACHED necessarily prevent
the application from preempting the exception propagation with another
exception. Both Tru64 UNIX and OpenVMS can do that with DETACHED handlers
as well. Granted that the entire call stack of nested exception propagation
and handlers remains until someone unwinds -- but, again, once you've
changed any state your ability to diagnose what happened is decreased and
you simply have to accept that.

Alexander Terekhov

unread,
May 7, 2003, 5:36:20 PM5/7/03
to

David Butenhof wrote:
>
> Alexander Terekhov wrote:
>
> > David Butenhof wrote:
> >
> > [... DETACHED handlers vs ATTACHED handlers ...]
> >
> > My main problem with try/finally is that try/finally is indeed a
> > "handler". I don't want cleanup stuff act like a "handler" in the
> > sense that I don't want it to cause unnecessary unwinding for the
> > unexpected exceptions. I'd have no problems with some "attached"
> > try/finally-like beast that wouldn't cause unnecessary unwinding
> > for the unexpected exceptions. In my book, unexpected exceptions
> > shall cause std::unexpected() invocation at throw point. Another
> > problem is that code in the finally scope is allowed to "steal"
> > the original exception via throwing another one; I hate that too.
>
> Once you've changed the program state (by running any form of cleanup), the
> program state presented to your std::unexpected() handler, or the contents
> of a final core dump, no longer represent the actual state of the program
> at the time the exception was raised. It's become inaccurate and
> potentially misleading. You may have the static call stack, but you have
> very likely lost critical information about how the program came to that
> point.

Agreed. Perhaps we have a terminology problem. To me, 'unwinding'
IS 'cleanup'. I don't want to have unwinding/cleanup prior to the
std::unexpected() handler invocation. In two-phase EH, a search
for some matching catch-handler is nothing but a simple read-only
lookup, AFAIK.

>
> Analysis tools that take advantage of the program state (including but not
> restricted to the call stack) need to happen BEFORE (or instead of)
> cleanup. Stack unwind is just one more form of cleanup. ATTACHED handlers
> cleanup the call stack in sequence with the cleanup of other program state:
> the result is, in some sense, a more accurate representation than to leave
> the call stack intact while removing all the evidence of how it got that
> way.
>
> In the case of a fatal error such as SIGSEGV, an immediate core dump and
> termination is best, capturing the ENTIRE state of the program (including
> call stack) for analysis. Once you've changed anything, the rest is out of
> context. For other "exceptional" conditions, the balance is less clear; but
> I do think you can't have it both ways. Either you clean up (and accept
> that you've lost critical analysis information, which might as well be lost
> consistently), or you preserve everything as it is without any handling.

We CAN have it both ways. That's easy. We can and should catch
recoverable stuff ONLY and use repaired/fixed exception specs to
"fence" the unexpected stuff (which is only good for "no-return"
std::unexpected()->abort(), "translation of exceptions"/throw-
another-one in std::unexpected handler for throw(...) violations
aside for a moment).

>
> (Though I suppose you might compromise by writing a core file at the time
> the initial exception is raised and archiving it for later analysis, while
> the live program goes on to run cleanup handlers, unwind, and potentially
> recover. Again, though, the unwind doesn't hurt... and if you're really
> going to recover it's going to be necessary eventually anyway.)

No. An unexpected unwind does hurt -- it's just like throw()-
nothing violation.

>
> Nor does having DETACHED handlers instead of ATTACHED necessarily prevent
> the application from preempting the exception propagation with another
> exception. Both Tru64 UNIX and OpenVMS can do that with DETACHED handlers
> as well.

Well, the matching catch-handler certainly can "preempt". But
that's NOT the case with C++ destructors -- std::terminate() is
called.

> Granted that the entire call stack of nested exception propagation
> and handlers remains until someone unwinds -- but, again, once you've
> changed any state your ability to diagnose what happened is decreased and
> you simply have to accept that.

Yeah, that's true in the "conditions"-model-with-resumption. We
also have it on "z" machines... and just like you, I don't like it.
Oh, BTW, they need a "POSIX(OFF)" environment -- Not Bad, oder? ;-)

http://publibz.boulder.ibm.com/cgi-bin/bookmgr_OS390/BOOKS/CEEA2130/3.4
(3.4 Chapter 15. Language Environment Condition Handling Introduction)

regards,
alexander.

David Butenhof

unread,
May 8, 2003, 8:13:40 AM5/8/03
to
Alexander Terekhov wrote:

Ah. Perhaps it's not so much a terminology problem as a "scope" problem. I
had read your "unexpected" catchall as "unhandled", probably mostly because
I'm thinking in terms of general (language independent) exception
infrastructure, whereas you may be talking about semantics peculiar to C++.

Yes, perhaps "pure C++" code should terminate "in place" when an exception
cannot be finalized, without running any intervening destructors. This is
indeed complicated by a 'catch()' clause that instead of finalizing
actually resumes the unwind with a different exception. While I would agree
that could be frustrating, it's also a deliberate action of the
application, not something imposed by the model or environment, and I don't
see it as a problem. Either it's there because it was perceived to be
useful (whether you agree or not is beside the point), or it's an error
that should be corrected in any case.

However, many (perhaps even most) applications aren't "pure C++". If the
call stack includes non-C++ exception-aware code, the situation becomes
very different. An unwind analyzer might, in theory, determine that a
destructor, or a POSIX cleanup handler, CANNOT finalize the exception, only
change state, and could therefore continue unwinding to determine whether
the application can actually finalize and continue -- or otherwise dump
core without letting anything change the state. However, on most systems
even C++ object destructors are likely implemented as local PC-range
exception scopes, indistinguishable at runtime from an Ada TRY/FINALLY or a
C __try/__except.

I don't think the distinction you're probably trying to make is practical or
perhaps even possible without devising an entirely new calling standard...
as well as a body of programming convention that may be reasonable for pure
C++ but not in a mixed language "real world" environment.

Alexander Terekhov

unread,
May 8, 2003, 10:24:08 AM5/8/03
to

David Butenhof wrote:
[...]

Yeah, let's try to forget the general (language independent)
"condition handling" with detached handlers and fully concentrate
on the C++ exception handling and unwinding, please.

The current C++ standard says:

"The process of calling destructors for automatic objects
constructed on the path from a try block to a throw-expression
is called 'stack unwinding.'"

That's okay (there's a nice definition of 'try-block' as well).

Now, the current C++ standard also has a couple of kinda-relevant
"implementation-defined" bits:

a) "If no matching handler is found in a program, the function
terminate() is called; whether or not the stack is unwound
before this call to terminate() is implementation-defined",

b) "In the situation where no matching handler is found, it is
implementation-defined whether or not the stack is unwound
before terminate() is called.".

and it also defines the semantics of exception specifications...
in effect, as just a bunch of noop/rethrowing function-try-block
catch-handlers with a trailing catch(...)-thing that invokes
std::unexpected().

I just hate that. To begin with, the definition of the term
'stack unwinding' makes me wonder what "try-block" is meant
here:

int main() {
object o; // <-- 'unwinding' is implementation-defined
throw "trouble";
}

and here:

void Main() throw() {
object o; // <-- 'unwinding' is REQUIRED here
throw "trouble";
}

I believe that in both cases above, std::unexpected() shall
be invoked as the result of successful evaluating of throw-
expression and without any 'unwinding' taking place. Further,
I believe that ALL dtors shall have an implicit throw()-
nothing exception-spec imposed on them. Finally, I believe
that the standard should say that 'execution of each and
every thread' [initial/main including] shall be done "as if"

whatever run(...whatever...) throw(std::thread_exit,
std::thread_cancel);

is called; that implementation simply does finalization
of thread_exit or thread_cancel exception (if thrown)
resulting in thread termination and that any other uncaught
exception will end up in std::unexpected() invoked at throw
point.

>
> Yes, perhaps "pure C++" code should terminate "in place" when an exception
> cannot be finalized, without running any intervening destructors. This is
> indeed complicated by a 'catch()' clause that instead of finalizing
> actually resumes the unwind with a different exception. While I would agree
> that could be frustrating, it's also a deliberate action of the
> application, not something imposed by the model or environment, and I don't
> see it as a problem.

Agreed.

> Either it's there because it was perceived to be
> useful (whether you agree or not is beside the point), or it's an error
> that should be corrected in any case.

Well, <copy&paste>

I'll admit that I'll have no problems with something like

template<class _FwdIt, class _Tval> inline
void _Uninit_fill(_FwdIt _First, _FwdIt _Last, const _Tval& _Val,
_Nonscalar_ptr_iterator_tag)
{ // copy _Val throughout raw [_First, _Last), arbitrary type
_FwdIt _Next = _First;
try {
for (; _First != _Last; ++_First)
_Construct(&*_First, _Val);
}
action_on_propagation_of(...) { /* THIS DOESN'T CATCH *UNEXPECTED* EXCEPTIONS */
/* THIS DOES RETHROW EXCEPTIONS AUTOMATICALLY */
/* NOTHING ELSE CAN BE THROWN FROM THIS SCOPE */
for (; _Next != _First; ++_Next)
_Destroy(&*_Next);
}
}

</copy&paste>

You might want to take a look at this as well: (stuff like
"better finally for C++"/"cleanup { /*...*/ };", etc.)

http://groups.google.com/groups?threadm=3CBA86A3.9C6805AF%40web.de
(Subject: Re: Alexandrescu on error handling at ACCU conference 2002)

>
> However, many (perhaps even most) applications aren't "pure C++". If the
> call stack includes non-C++ exception-aware code, the situation becomes
> very different. An unwind analyzer might, in theory, determine that a
> destructor, or a POSIX cleanup handler, CANNOT finalize the exception, only
> change state, and could therefore continue unwinding to determine whether
> the application can actually finalize and continue -- or otherwise dump
> core without letting anything change the state. However, on most systems
> even C++ object destructors are likely implemented as local PC-range
> exception scopes, indistinguishable at runtime from an Ada TRY/FINALLY or a
> C __try/__except.

I'd love to declare all such systems "obsolete"... if not broken. ;-)

regards,
alexander.

David Butenhof

unread,
May 8, 2003, 12:45:33 PM5/8/03
to
Alexander Terekhov wrote:

>> Ah. Perhaps it's not so much a terminology problem as a "scope" problem.
>> I had read your "unexpected" catchall as "unhandled", probably mostly
>> because I'm thinking in terms of general (language independent) exception
>> infrastructure, whereas you may be talking about semantics peculiar to
>> C++.
>
> Yeah, let's try to forget the general (language independent)
> "condition handling" with detached handlers and fully concentrate
> on the C++ exception handling and unwinding, please.

Yeah, well, that's the problem, because I'm not interested in discussion
that's specific to the C++ language. Such a discussion is of some concern
to the C++ standards committed, but not very relevant to much of anyone
else because the real world is rarely single-language. (And where it is,
that language is far more likely to be C, Java, or even Ada than C++.)

Besides, Alexander, you started this with a quote of a proposed C language
extension to deal with exceptions; NOT as a C++ "language purity" topic!

> I believe that in both cases above, std::unexpected() shall
> be invoked as the result of successful evaluating of throw-
> expression and without any 'unwinding' taking place. Further,
> I believe that ALL dtors shall have an implicit throw()-
> nothing exception-spec imposed on them. Finally, I believe
> that the standard should say that 'execution of each and
> every thread' [initial/main including] shall be done "as if"
>
> whatever run(...whatever...) throw(std::thread_exit,
> std::thread_cancel);

That's a topic for C++ standardization, or Boost. I agree that seems like a
prefectly reasonable model.

Even so, whenever a destructor runs, you've altered the state of the program
and rendered an eventual core dump (and it's associate call stack[s])
essentially invalid. If you IGNORE destructors for active objects even when
you can be absolutely sure the call stack cannot finalize an exception, you
risk breaking external invariants -- state that might be seen by other
processes. For example, a mutex guard destructor might release a pshared
mutex.

If there ARE no destructors, and no catch() clauses that could finalize the
exception, then, sure, an ideal exception infrastructure will cleanly
terminate without changing any state. Good. That won't cover most cases,
where there's SOME legitimate cleanup in scope, because ANY cleanup means
your analysis starts with irrelevant data. (I almost said "bad" data, but
in fact it might be the opposite -- the cleanup might restore a busted
invariant that was the root cause of the exception.)

> I'll admit that I'll have no problems with something like
>
> template<class _FwdIt, class _Tval> inline
> void _Uninit_fill(_FwdIt _First, _FwdIt _Last, const _Tval& _Val,
> _Nonscalar_ptr_iterator_tag)
> { // copy _Val throughout raw [_First, _Last), arbitrary type
> _FwdIt _Next = _First;
> try {
> for (; _First != _Last; ++_First)
> _Construct(&*_First, _Val);
> }
> action_on_propagation_of(...) { /* THIS DOESN'T CATCH *UNEXPECTED*
> EXCEPTIONS */
> /* THIS DOES RETHROW EXCEPTIONS
> AUTOMATICALLY */ /* NOTHING ELSE CAN BE
> THROWN FROM THIS SCOPE */
> for (; _Next != _First; ++_Next)
> _Destroy(&*_Next);
> }
> }

But that's just a 'finally', really, with language restrictions on what you
can do in the ATTACHED (i.e., requiring unwind) handler. You may RETHROW,
and that's fine, but you've still unwound and changed other application
state. You previously seemed to be saying you didn't like this. Now it's
OK?

> You might want to take a look at this as well: (stuff like
> "better finally for C++"/"cleanup { /*...*/ };", etc.)
>
> http://groups.google.com/groups?threadm=3CBA86A3.9C6805AF%40web.de
> (Subject: Re: Alexandrescu on error handling at ACCU conference 2002)

Yes, OpenVMS has the concept of "chained conditions", with a "primary"
condition (the one actually signalled/raised) and an attached list of
secondary conditions. (And each may have arguments.) It's quite common for
a facility to catch a condition raised by a lower-level facility and
"reraise" a new condition with a new primary condition... and the original
condition appended as a secondary.

For example, if open_foo_library calls open(), and open() were to raise ("No
such file %s", "/usr/users/xyzzy/library/index.dat"), open_foo_library
might re-raise ("Library %s lacks index", "/usr/users/xyzzy/library", "No
such file %s", "/usr/users/xyzzy/library/index.dat"). When the process dies
of the unhandled condition, the "last chance handler" will format and
report the entire condition chain.

Now, on OpenVMS, with 2-phase handling and continuation, it's perfectly
possible and practical to do all this without any unwinding; though in many
cases it requires uplevel local references to determine what the caller
(e.g., our open_foo_library) really meant to do. Neither C nor C++ supports
uplevel references automatically, and this is clearly easier in languages
that do. An ATTACHED handler, on the other hand, makes this
straightforward, because you can just read the local variables you need,
directly. Of course, since the intent here is "end user" diagnosis, rather
than internal diagnosis of the facility that's doing the unwind and
chaining, the lost of the call stack leaves isn't usually relevant.
(Except, of course, in those rare cases where the open() failure was
unexpected and represents an internal or dependency failure that does need
to be diagnosed! ;-) )

>> However, many (perhaps even most) applications aren't "pure C++". If the
>> call stack includes non-C++ exception-aware code, the situation becomes
>> very different. An unwind analyzer might, in theory, determine that a
>> destructor, or a POSIX cleanup handler, CANNOT finalize the exception,
>> only change state, and could therefore continue unwinding to determine
>> whether the application can actually finalize and continue -- or
>> otherwise dump core without letting anything change the state. However,
>> on most systems even C++ object destructors are likely implemented as
>> local PC-range exception scopes, indistinguishable at runtime from an Ada
>> TRY/FINALLY or a C __try/__except.
>
> I'd love to declare all such systems "obsolete"... if not broken. ;-)

Let's start by declaring UNIX, C, and C++ obsolete and start from scratch.
But that's not going to happen EITHER. So deal with it. ;-)

Alexander Terekhov

unread,
May 9, 2003, 12:38:42 PM5/9/03
to

David Butenhof wrote:
>
> Alexander Terekhov wrote:
>
> >> Ah. Perhaps it's not so much a terminology problem as a "scope" problem.
> >> I had read your "unexpected" catchall as "unhandled", probably mostly
> >> because I'm thinking in terms of general (language independent) exception
> >> infrastructure, whereas you may be talking about semantics peculiar to
> >> C++.
> >
> > Yeah, let's try to forget the general (language independent)
> > "condition handling" with detached handlers and fully concentrate
> > on the C++ exception handling and unwinding, please.
>
> Yeah, well, that's the problem, because I'm not interested in discussion
> that's specific to the C++ language. Such a discussion is of some concern
> to the C++ standards committed,

I've dropped recently a google link pointing to this thread to
comp.std.c++; perhaps someone will click on it and might even
join. Who knows.

> but not very relevant to much of anyone
> else because the real world is rarely single-language. (And where it is,
> that language is far more likely to be C, Java, or even Ada than C++.)
>
> Besides, Alexander, you started this with a quote of a proposed C language
> extension to deal with exceptions; NOT as a C++ "language purity" topic!

Yeah, actually, I was thinking of crossposting it comp.std.c
from the beginning (but refrained because since a couple of
day ago, folks seem to be quite busy over there -- trigraphs,
freed pointers, whatnot... the traffic is unbelievably heavy).

I don't understand. First off, a C++ destructor canNOT
finalize any exception -- only some try-block catch handler
(but not from function-try-block) can. My "language purity"
would just prevent unneeded and harmful unwinding in all
situations where it's currently implementation-defined or
"erroneously" required (I mean totally broken exception
specs[1]), that's it.

>
> > I'll admit that I'll have no problems with something like
> >
> > template<class _FwdIt, class _Tval> inline
> > void _Uninit_fill(_FwdIt _First, _FwdIt _Last, const _Tval& _Val,
> > _Nonscalar_ptr_iterator_tag)
> > { // copy _Val throughout raw [_First, _Last), arbitrary type
> > _FwdIt _Next = _First;
> > try {
> > for (; _First != _Last; ++_First)
> > _Construct(&*_First, _Val);
> > }
> > action_on_propagation_of(...) { /* THIS DOESN'T CATCH *UNEXPECTED*
> > EXCEPTIONS */
> > /* THIS DOES RETHROW EXCEPTIONS
> > AUTOMATICALLY */ /* NOTHING ELSE CAN BE
> > THROWN FROM THIS SCOPE */
> > for (; _Next != _First; ++_Next)
> > _Destroy(&*_Next);
> > }
> > }
>
> But that's just a 'finally', really, with language restrictions on what you
> can do in the ATTACHED (i.e., requiring unwind) handler. You may RETHROW,
> and that's fine, but you've still unwound and changed other application
> state. You previously seemed to be saying you didn't like this. Now it's
> OK?

It is NOT 'finally' because it fires only when some matching
exception is raised within its try-block scope AND there's
a matching handler found *somewhere else* in the surrounding
dynamic context. AFAICS, 'finally' doesn't work that way.
The "action" thing can even examine the propagating exception
(e.g. using try { throw; } catch... technique) and perhaps
even append some info to it (catching by reference). The key
point is that "THIS DOESN'T CATCH *UNEXPECTED* EXCEPTIONS".
You can think of it as a sort of WEAK (it should have been
that way from the beginning[2]) function-try-block handler
of some constructor or destructor because such handlers also
rethrow exceptions "automatically". WEAK simply means that it
can't be a propagation target because it's just not "visible"
in the search phase.

Do you understand me?

regards,
alexander.

[1] http://tinyurl.com/bdp1

"Arrgh! Now I understand why you are unhappy about this.
This goes against everything I'd understood about the way
exceptions worked. I thought the whole point was that the
stack was supposed to unwind cleanly and _completely_."

[2] The standard says:

"The exception being handled is rethrown if control reaches
the end of a handler of the function-try-block of a
constructor or destructor. Otherwise, a function returns
when control reaches the end of a handler for the function-
try-block (6.6.3). Flowing off the end of a function-try-
block is equivalent to a return with no value; this results
in undefined behavior in a value-returning function (6.6.3)"

I'd rather make ALL function-try-block handlers *rethrowing*
AND WEAK -- just like "action_on_propagation_of" thing above.

David Butenhof

unread,
May 12, 2003, 8:52:23 AM5/12/03
to
Alexander Terekhov wrote:

>
> David Butenhof wrote:
>>
>> If there ARE no destructors, and no catch() clauses that could finalize
>> the exception, then, sure, an ideal exception infrastructure will cleanly
>> terminate without changing any state. Good. That won't cover most cases,
>> where there's SOME legitimate cleanup in scope, because ANY cleanup means
>> your analysis starts with irrelevant data. (I almost said "bad" data, but
>> in fact it might be the opposite -- the cleanup might restore a busted
>> invariant that was the root cause of the exception.)
>
> I don't understand. First off, a C++ destructor canNOT
> finalize any exception -- only some try-block catch handler
> (but not from function-try-block) can. My "language purity"
> would just prevent unneeded and harmful unwinding in all
> situations where it's currently implementation-defined or
> "erroneously" required (I mean totally broken exception
> specs[1]), that's it.

No, a C++ destructor doesn't finalize -- but it does change program state.
That's its entire purpose. The only point I see to avoiding unwind is that
you retain the original program state at the time of the exception, for
diagnosis. But if you've changed anything, you've got a program shell that
PRETENDS (misleadingly) to represent the real program state. You're better
off unwinding so that's clear.

> It is NOT 'finally' because it fires only when some matching
> exception is raised within its try-block scope AND there's
> a matching handler found *somewhere else* in the surrounding
> dynamic context. AFAICS, 'finally' doesn't work that way.
> The "action" thing can even examine the propagating exception
> (e.g. using try { throw; } catch... technique) and perhaps
> even append some info to it (catching by reference). The key
> point is that "THIS DOESN'T CATCH *UNEXPECTED* EXCEPTIONS".
> You can think of it as a sort of WEAK (it should have been
> that way from the beginning[2]) function-try-block handler
> of some constructor or destructor because such handlers also
> rethrow exceptions "automatically". WEAK simply means that it
> can't be a propagation target because it's just not "visible"
> in the search phase.
>
> Do you understand me?

OK, so, "I have cleanup when exception <foo> will cause an unwind". There
are still cleanups you'll need to make even when there won't be an unwind,
to deal with external predicates visible to other processes. And once you
changed anything at all in the program state, expending extra effort to
avoid additional change seems pretty pointless.

But again, you're arguing "pure C++" semantics here. You've clearly
completely abandoned the original topic of C language extensions, and are
far away from any middle ground of cross language exception support. This
is a different discussion, and not one I really care to be persuing.

A cross-language exception model (the only one of interest to me!) has to
support "finally" because you need it for existing language models.
Designing a C++ "re-definition" that prevents it from fitting into that
integrated world doesn't interest me. I think it would be a bad idea, in
fact, regardless of any theoretical/philosophical advantages it might have.

Alexander Terekhov

unread,
May 12, 2003, 10:03:13 AM5/12/03
to

David Butenhof wrote:
[...]

> >> If there ARE no destructors, and no catch() clauses that could finalize
> >> the exception, then, sure, an ideal exception infrastructure will cleanly
> >> terminate without changing any state. Good. That won't cover most cases,
> >> where there's SOME legitimate cleanup in scope, because ANY cleanup means
> >> your analysis starts with irrelevant data. (I almost said "bad" data, but
> >> in fact it might be the opposite -- the cleanup might restore a busted
> >> invariant that was the root cause of the exception.)
> >
> > I don't understand. First off, a C++ destructor canNOT
> > finalize any exception -- only some try-block catch handler
> > (but not from function-try-block) can. My "language purity"
> > would just prevent unneeded and harmful unwinding in all
> > situations where it's currently implementation-defined or
> > "erroneously" required (I mean totally broken exception
> > specs[1]), that's it.
>
> No, a C++ destructor doesn't finalize -- but it does change program state.
> That's its entire purpose. The only point I see to avoiding unwind is that
> you retain the original program state at the time of the exception, for
> diagnosis. But if you've changed anything, you've got a program shell that
> PRETENDS (misleadingly) to represent the real program state. You're better
> off unwinding so that's clear.

By default, std::unexpected() causes std::terminate() which results
in abort(). That's just fine in most situations (things like prompty
turning off the speaker doing an emergency cleanup aside for a moment).
Any cleanup relying/affecting/modifying program state in the case of
unexpected failures is foolish, I believe. Emergency cleanup, if
needed, can be done using std::unexpected() and std::terminate()
handlers (using std::set_unexpected() and/or std::set_terminate()).

>
> > It is NOT 'finally' because it fires only when some matching
> > exception is raised within its try-block scope AND there's
> > a matching handler found *somewhere else* in the surrounding
> > dynamic context. AFAICS, 'finally' doesn't work that way.
> > The "action" thing can even examine the propagating exception
> > (e.g. using try { throw; } catch... technique) and perhaps
> > even append some info to it (catching by reference). The key
> > point is that "THIS DOESN'T CATCH *UNEXPECTED* EXCEPTIONS".
> > You can think of it as a sort of WEAK (it should have been
> > that way from the beginning[2]) function-try-block handler
> > of some constructor or destructor because such handlers also
> > rethrow exceptions "automatically". WEAK simply means that it
> > can't be a propagation target because it's just not "visible"
> > in the search phase.
> >
> > Do you understand me?
>
> OK, so, "I have cleanup when exception <foo> will cause an unwind". There
> are still cleanups you'll need to make even when there won't be an unwind,
> to deal with external predicates visible to other processes.

Really? How can one "deal" with a broken mutex (from within a "broken"
process(es))? I'd rather do all such cleanups from a separate "clean"
process.

> And once you
> changed anything at all in the program state, expending extra effort to
> avoid additional change seems pretty pointless.

I don't want to change anything at all in the program state.

>
> But again, you're arguing "pure C++" semantics here. You've clearly
> completely abandoned the original topic of C language extensions, and are
> far away from any middle ground of cross language exception support. This
> is a different discussion, and not one I really care to be persuing.

That's fine. My point is/was that "finally" is broken.

>
> A cross-language exception model (the only one of interest to me!) has to
> support "finally" because you need it for existing language models.

I don't want to have finally in C-with-exceptions, that's the point.

> Designing a C++ "re-definition" that prevents it from fitting into that
> integrated world doesn't interest me.

Nothing prevents it from fitting into existing world (WITH Java's
"finally", catch(...)-all, whatever-"cross language"-things). Heck,
people would still be able to write rather silly things like:

int main() {
try {
whatever();
}
catch(...) {
/* ... */
}
}

without using ES to "fence" really unexpected stuff. However,
"redefined", C++ (and C-with-exceptions-without-finally-as-pthread
cleanup) with fixed exception specs and always-throw()-nothing-dtors
(in C, for pthread-cleanup, to begin with) would allow a bit more
"robust" programming (currently, it doesn't really allow it,
unfortunately).

> I think it would be a bad idea, in
> fact, regardless of any theoretical/philosophical advantages it might have.

I still don't see what makes you think so. :-(

regards,
alexander.

David Butenhof

unread,
May 13, 2003, 9:36:04 AM5/13/03
to
Alexander Terekhov wrote:

>> OK, so, "I have cleanup when exception <foo> will cause an unwind". There
>> are still cleanups you'll need to make even when there won't be an
>> unwind, to deal with external predicates visible to other processes.
>
> Really? How can one "deal" with a broken mutex (from within a "broken"
> process(es))? I'd rather do all such cleanups from a separate "clean"
> process.

And in many cases that's the best solution. Maybe even in most cases. It's
not always practical, though, nor appropriate for all applications.

>> But again, you're arguing "pure C++" semantics here. You've clearly
>> completely abandoned the original topic of C language extensions, and are
>> far away from any middle ground of cross language exception support. This
>> is a different discussion, and not one I really care to be persuing.
>
> That's fine. My point is/was that "finally" is broken.

Well, "broken" is a strong term. It doesn't work the way you'd prefer that
applications be designed. For many people I understand there's no
difference; unfortunately the second is highly subjective.

>> A cross-language exception model (the only one of interest to me!) has to
>> support "finally" because you need it for existing language models.
>
> I don't want to have finally in C-with-exceptions, that's the point.

OK, fine. Let's standardize on a low-level exception support environment
with DETACHED 2-phase PC-mapped exceptions. On the first "search" phase,
the handler (if any) associated with each PC region that's active on the
call stack is presented (in order from most recent frame) with the cause of
the exception; say, a status code and arbitrary counted argument list. At
any point, a handler may determine that an UNWIND is necessary, targeting a
particular active stack frame. All handlers from the "inmost" out to that
frame's will be activated a second time with a status that indicates an
UNWIND is in progress. If the search phase reaches the base frame with no
unwind, then a core dump is taken. Only on the UNWIND phase should any
handler make actual changes in any program state, consistent with the
unwinding of that frame. Thus, cleanup occurs only when an UNWIND has been
ordered -- and of course it may also be conditionalized on the identity of
the exception that's unwinding.

This provides everything you need for your conditional "cleanup only if
unwind will occur" syntax, in C++ and/or C. It still provides sufficient
support for existing exception models like Ada and Modula-2+ that already
have 'finally' and depend on a traditional interpretation. (Having any of
these frames active will cause your conditional cleanup for any "inner
frame" to fire because there WILL be an unwind, but that's life.)

(What I've described, by the way, is essentially how the OpenVMS condition
handling facility has worked since 1977, though many users have always
ignored the unenforced recommendation that cleanup be done only on unwind.)

>> I think it would be a bad idea, in
>> fact, regardless of any theoretical/philosophical advantages it might
>> have.
>
> I still don't see what makes you think so. :-(

I suspect this disagreement comes from the fundamental fuzzy focus of the
discussion. It's not entirely clear from moment to moment whether you're
talking "pure C++ language semantics", "hypothetical gcc exception
extension for C language", or "design philosophy of cross-language
exception facility".

I've been too busy to get organized and "go back to first principles", so
I've been dealing with your points independently as they come up, basically
in whatever mode I happen to interpret that particular point. This probably
makes my replies as unfocused as yours, with the inevitable result that the
whole thread is an absolute mess.

There's nothing wrong with ADDITIONAL C++ syntax to support 2-phase
conditional unwind, though I suspect the idea won't go over very well with
those who don't already have 2-phase exceptions. I think it'd be fine to
have similar conditional cleanup mechanisms for C extensions (e.g., for
example, even something like a "finally" that triggers only on unwind but
won't actually cause one). However, none of this must prevent implementing
languages that already have unconditional unwinding finally semantics, and
the exception package & conventions must to be designed to promote clean
interoperability between all languages.

Alexander Terekhov

unread,
May 13, 2003, 1:47:28 PM5/13/03
to

David Butenhof wrote:
>
> Alexander Terekhov wrote:
>
> >> OK, so, "I have cleanup when exception <foo> will cause an unwind". There
> >> are still cleanups you'll need to make even when there won't be an
> >> unwind, to deal with external predicates visible to other processes.
> >
> > Really? How can one "deal" with a broken mutex (from within a "broken"
> > process(es))? I'd rather do all such cleanups from a separate "clean"
> > process.
>
> And in many cases that's the best solution. Maybe even in most cases. It's
> not always practical, though, nor appropriate for all applications.

Agreed. In C++ applications (and "components"), things like
catch(...) and std::unexpected/terminate handlers would satisfy
those needs, I guess.

Unless I put a "fence" in the form of an exception specification
(please see below) prohibiting the propagation of *unexpected*
exceptions, right?

>
> (What I've described, by the way, is essentially how the OpenVMS condition
> handling facility has worked since 1977, though many users have always
> ignored the unenforced recommendation that cleanup be done only on unwind.)

I like it very much. Now,

--- in C/POSIX module ---

void cleanup(void *) {
printf("hello\n");
}

void c_f() {
pthread_cleanup_push(cleanup, 0);
pthread_exit(0);
pthread_cleanup_pop(0);
}

--- in C++ 'main' module ---

struct object { ~object() { printf("hello\n"); } };

void f() throw() {
object o;
c_f();
}

int main() {
f();
}

How many times will we see "hello"?

regards,
alexander.

David Butenhof

unread,
May 14, 2003, 7:44:34 AM5/14/03
to
Alexander Terekhov wrote:

> David Butenhof wrote:
>>
>> (What I've described, by the way, is essentially how the OpenVMS
>> condition handling facility has worked since 1977, though many users have
>> always ignored the unenforced recommendation that cleanup be done only on
>> unwind.)
>
> I like it very much. Now,
>
> --- in C/POSIX module ---
>
> void cleanup(void *) {
> printf("hello\n");
> }
>
> void c_f() {
> pthread_cleanup_push(cleanup, 0);
> pthread_exit(0);
> pthread_cleanup_pop(0);
> }
>
> --- in C++ 'main' module ---
>
> struct object { ~object() { printf("hello\n"); } };
>
> void f() throw() {
> object o;
> c_f();
> }
>
> int main() {
> f();
> }
>
> How many times will we see "hello"?

I see it three times, because that's how many times you typed it. (Was that
a trick question? ;-) )

Seriously, though, I think that's very much the crux of your argument, isn't
it? I would expect "hello" to be output by the POSIX cleanup handler as
c_f() is unwound in response to pthread_exit(). THAT is clearly specified
by current standards.

Now, though, we face what may be a philosophical issue, if it's not
carefully tied down by the C++ standard (which I haven't read, much less
analyzed in detail). If C++ is required to have 2-phase exceptions AND if
an implementation is not allowed to SEARCH (phase 1) through f()'s empty
throw() specification, then I would expect to see std::unexpected() fire
before the second "hello" can be written by o's destructor.

However, you have suggested that a single phase unwind implementation is
allowed, and in such an implementation I would expect o's destructor to
run, printing a second "hello", and THEN for std::unexpected() to fire as
the runtime attempts to unwind through the empty throw() specification.

I can tell you that the C++ implementation on both Tru64 UNIX and OpenVMS,
compiled and run with default options, print "hello" twice and THEN abort
with unexpected(). (Proving that, as I said, while OpenVMS has always
supported 2-phase exceptions and recommends cleanup on the unwind phase,
not everyone actually uses it that way. ;-) )

I can also say that on Tru64 UNIX (I didn't bother going through the extra
gyrations to get and analyze a process core dump on OpenVMS), the core file
leads one to the f() frame (though there's terminate and abort and raise
and all that stuff on top of it) so that someone diagnosing the abort might
be lead to examine the f() function and notice the empty throw()
specification. (While OpenVMS reports that "terminate or unexpected" has
been invoked, on Tru64 you get only "Resources lost(coredump)".)

This at least meets my minimal requirements, that the cleanup and unwind be
properly synchronized. I'd be vexed at an implementation that maintained a
separate stack of cleanup handlers, for example, and called them without
actually unwinding the call stack, so that the final core file might show
c_f() as the active frame even though cleanup had occurred out through f().
I'd be annoyed if the core file showed NO active frames, because there'd be
no clue to what happened.

I also understand that YOU would prefer that std::unexpected() would fire
without running ANY cleanup OR unwinding any frames as the SEARCH exception
pass (phase 1) ran up against the empty throw() specification.

I'm inclined to agree with you philosophically, but with one foot (plus a
heel of the other foot) planted firmly in the real world, I'd worry about
the consequences of breaking external invariants by suddenly adding a
requirement that ~o() not be run in this case. Even your trivial example
shows where this could cause problems, because you do have an external
invariant. The output of this program might be redirected into a file that
might be used as a benchmark for testing (or for other purposes), and the
change in output from "hello\nhello\n" to "hello\n" between one version of
the C++ runtime and another could indeed be an issue.

Alexander Terekhov

unread,
May 14, 2003, 10:55:52 AM5/14/03
to

David Butenhof wrote:
[...]

> > --- in C/POSIX module ---
> >
> > void cleanup(void *) {
> > printf("hello\n");
> > }
> >
> > void c_f() {
> > pthread_cleanup_push(cleanup, 0);
> > pthread_exit(0);
> > pthread_cleanup_pop(0);
> > }
> >
> > --- in C++ 'main' module ---
> >
> > struct object { ~object() { printf("hello\n"); } };
> >
> > void f() throw() {
> > object o;
> > c_f();
> > }
> >
> > int main() {
> > f();
> > }
> >
> > How many times will we see "hello"?
>
> I see it three times, because that's how many times you typed it. (Was that
> a trick question? ;-) )

;-)

>
> Seriously, though, I think that's very much the crux of your argument, isn't
> it?

Yep.

> I would expect "hello" to be output by the POSIX cleanup handler as
> c_f() is unwound in response to pthread_exit(). THAT is clearly specified
> by current standards.

NO! I can see nothing in the POSIX standard that would prohibit
the following implementation of pthread_exit():

extern "C" void pthread_exit(void * ptr) {
std::thread_exit(ptr);
}

using something along the lines of: (from the "std" namespace)

class thread_termination_request : public std::exception ...
class thread_cancel_request : public std::thread_termination_request ...
class thread_exit_request : public std::thread_termination_request ...

template<typename T>
class thread_exit_value : public std::thread_exit_request ...

template<typename T>
void thread_exit(T value) {
assert(std::thread_self().can_exit_with<T>());
throw thread_exit_value(value);
}

< as an aside: Attila, do you follow me? >

I see almost-no-problems** catching and "finalizing" ANY of these
exceptions. If one can catch-and-finalize "thread termination" and
cause an abnormal process termination right after "finalizing" it,
I don't see why this can't be done by the implementation at throw
point due to ES violation.

**) http://www.opengroup.org/austin/mailarchives/austin-group-l/msg05202.html
(Subject: XSH ERN 77, 81 and 82)

>
> Now, though, we face what may be a philosophical issue, if it's not
> carefully tied down by the C++ standard (which I haven't read, much less
> analyzed in detail). If C++ is required to have 2-phase exceptions AND if

It isn't required, currently.

> an implementation is not allowed to SEARCH (phase 1) through f()'s empty
> throw() specification, then I would expect to see std::unexpected() fire
> before the second "hello" can be written by o's destructor.

NO! Before the FIRST "hello"! (I know that you know that the answer
I wanted is a sort of /pthread_/null of "hello" ;-) ). Because the
"handler" inject by pthread_cleanup_push() shall be modeled upon
a C++ destructor, not archaic try/finally.

>
> However, you have suggested that a single phase unwind implementation is
> allowed, and in such an implementation I would expect o's destructor to
> run, printing a second "hello", and THEN for std::unexpected() to fire as
> the runtime attempts to unwind through the empty throw() specification.

Yes. Well, please take a loot at:

http://groups.google.com/groups?selm=3EC0ECAA.6520B266%40web.de
(Subject: Exception handling... it's time to fix the standard)

>
> I can tell you that the C++ implementation on both Tru64 UNIX and OpenVMS,
> compiled and run with default options, print "hello" twice and THEN abort
> with unexpected().

That's exactly what the C++ standard currently mandates, so to speak.

> (Proving that, as I said, while OpenVMS has always
> supported 2-phase exceptions and recommends cleanup on the unwind phase,
> not everyone actually uses it that way. ;-) )

Yeah, unfortunately, that's seems to be true with respect to majority
of C++ committee members too.

>
> I can also say that on Tru64 UNIX (I didn't bother going through the extra
> gyrations to get and analyze a process core dump on OpenVMS), the core file
> leads one to the f() frame (though there's terminate and abort and raise
> and all that stuff on top of it) so that someone diagnosing the abort might
> be lead to examine the f() function and notice the empty throw()
> specification. (While OpenVMS reports that "terminate or unexpected" has
> been invoked, on Tru64 you get only "Resources lost(coredump)".)
>
> This at least meets my minimal requirements, that the cleanup and unwind be
> properly synchronized. I'd be vexed at an implementation that maintained a
> separate stack of cleanup handlers, for example, and called them without
> actually unwinding the call stack, so that the final core file might show
> c_f() as the active frame even though cleanup had occurred out through f().
> I'd be annoyed if the core file showed NO active frames, because there'd be
> no clue to what happened.

Yes.

>
> I also understand that YOU would prefer that std::unexpected() would fire
> without running ANY cleanup OR unwinding any frames as the SEARCH exception
> pass (phase 1) ran up against the empty throw() specification.

YES!

>
> I'm inclined to agree with you philosophically, but with one foot (plus a
> heel of the other foot) planted firmly in the real world, I'd worry about
> the consequences of breaking external invariants by suddenly adding a
> requirement that ~o() not be run in this case. Even your trivial example
> shows where this could cause problems, because you do have an external
> invariant. The output of this program might be redirected into a file that
> might be used as a benchmark for testing (or for other purposes),

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Oh, http://groups.google.com/groups?selm=3EA6B22B.50C5B37%40web.de

#include <cassert> // The C++ stuff
#include <stdio.h> // The POSIX one
#include "errbutt" // That's my own

int main() {
int rc;
rc = printf("Hello World\n");
assert(rc);
if (rc < 0 || (rc = fclose(stdout)) == EOF) {
rc = errno;
assert(rc);
}
else {
assert(!rc);
}
return rc ? report_error(rc) : 0;
}

;-)



> and the
> change in output from "hello\nhello\n" to "hello\n" between one version of
> the C++ runtime and another could indeed be an issue.

Yes, I understand. But please note that propagation of exceptions
(unwinding) when "no matching handler found" is implemention-defined
(well, ES aside) in the current C++ standard. Portably, folks just
can't rely on always-unwind... if they don't use catch(...) and/or
"current" version of ES. The funny thing is that exceptions specs
are considered sort-of "harmful" by many "prominent" members of the
C++ community and aren't recommended.

http://www.gotw.ca/publications/mill22.htm
http://www.boost.org/more/lib_guide.htm#Exception-specification

regards,
alexander.

--
http://groups.google.com/groups?selm=3D3C0BCA.A5E2F8B2%40web.de

David Butenhof

unread,
May 15, 2003, 8:05:54 AM5/15/03
to
Alexander Terekhov wrote:

Sorry, but the standard says that the cleanup handler "shall be popped from
the cancellation cleanup stack and invoked with the argument arg when: 1)
The thread exits (that is, calls pthread_exit()). 2) The thread acts upon a
cancelation request. 3) The thread calls pthread_cleanup_pop() with a
non-zero execute argument."

This is clear: the handler SHALL be called when the thread calls
pthread_exit(). There's no allowance for "MAY be called if the exception is
unexpected or isn't finalized." POSIX doesn't even have exceptions, and
there's no way that the pthread_exit "exception" can possibly be
unexpected, or not finalized. The concepts just don't exist within the
scope of POSIX.

This discussion is not quite the same as contradicting your statement,
though. I don't know if that's what you intended, or if you were just
approaching a subtle truth from the back door. The problem (or perhaps
advantage) is that you've approached this from way beyond the boundaries of
POSIX by mixing C++ and POSIX threads, and PRESUMING a rational (but all
the same currently rare) implementation that combines C++ exceptions and
POSIX cleanup into a coherent exception model. So in that sense, no, POSIX
doesn't preclude what you've called for, because none of the rules really
apply.

But that's cheating. I think that's cheating. I don't know; maybe it's just
being creative. Perhaps it doesn't matter. No "conforming POSIX
application" could ever detect your "funky" implementation that might skip
cleanup handlers, because no conforming application could ever set up a
call stack like that.

So, OK, I'll give you that one on a technicality. ;-)

>> (Proving that, as I said, while OpenVMS has always
>> supported 2-phase exceptions and recommends cleanup on the unwind phase,
>> not everyone actually uses it that way. ;-) )
>
> Yeah, unfortunately, that's seems to be true with respect to majority
> of C++ committee members too.

I can understand a reluctance to require something that could radically
disrupt existing implementations. Too bad it wasn't specified as 2-phase in
the first place. There would have been many advantages. But I suspect it's
one of those things that's too subtle to see until you've got experience
with the alternatives; and probably nobody had really perceived the power
of a good 2-phase exception system that's used correctly. ;-)

>> change in output from "hello\nhello\n" to "hello\n" between one version
>> of the C++ runtime and another could indeed be an issue.
>
> Yes, I understand. But please note that propagation of exceptions
> (unwinding) when "no matching handler found" is implemention-defined
> (well, ES aside) in the current C++ standard. Portably, folks just
> can't rely on always-unwind... if they don't use catch(...) and/or
> "current" version of ES. The funny thing is that exceptions specs
> are considered sort-of "harmful" by many "prominent" members of the
> C++ community and aren't recommended.

This is sad; but I can also understand on grounds of compatibility. There's
a lot of older C++ code that doesn't have throw specs, and suddenly adding
them everywhere is a major headache if not a nightmare.

Alexander Terekhov

unread,
May 15, 2003, 8:35:21 AM5/15/03
to

David Butenhof wrote:
[...]

Thanks. ;-)

A) The term 'thread termination' is currently NOT defined in the
standard.

B) The POSIX Rationale for Thread Cancellation Cleanup Handlers makes
one thing clear:

"The alternative to providing these simple cancellation cleanup
handlers (whose only use is for cleaning up when a thread is
canceled) is to define a general exception package that could
be used for handling and cleaning up after hardware traps and
software-detected errors. This was too far removed from the
charter of providing threads to handle asynchrony. However, it
is an explicit goal of IEEE Std 1003.1-2001 to be compatible
with existing exception facilities and languages having
exceptions.

The interaction of this facility and other procedure-based or
language-level exception facilities is unspecified in this
version of IEEE Std 1003.1-2001. However, it is intended that
it be possible for an implementation to define the relationship
between these cancellation cleanup handlers and Ada, C++, or
other language-level exception handling facilities."

C) I just love aardvarks. ;-)

regards,
alexander.

David Butenhof

unread,
May 16, 2003, 8:55:17 AM5/16/03
to
Alexander Terekhov wrote:

>
> David Butenhof wrote:
>>
>> So, OK, I'll give you that one on a technicality. ;-)
>
> Thanks. ;-)
>
> A) The term 'thread termination' is currently NOT defined in the
> standard.

But that's not relevant, because the popping and execution of the cleanup
handler is, in any case, defined to happen "when the thread calls
pthread_exit()". It's not based on termination, though termination is
presumed to occur AFTER all cleanup handlers have been popped and executed.

> B) The POSIX Rationale for Thread Cancellation Cleanup Handlers makes
> one thing clear:
>
> "The alternative to providing these simple cancellation cleanup
> handlers (whose only use is for cleaning up when a thread is
> canceled) is to define a general exception package that could
> be used for handling and cleaning up after hardware traps and
> software-detected errors. This was too far removed from the
> charter of providing threads to handle asynchrony. However, it
> is an explicit goal of IEEE Std 1003.1-2001 to be compatible
> with existing exception facilities and languages having
> exceptions.
>
> The interaction of this facility and other procedure-based or
> language-level exception facilities is unspecified in this
> version of IEEE Std 1003.1-2001. However, it is intended that
> it be possible for an implementation to define the relationship
> between these cancellation cleanup handlers and Ada, C++, or
> other language-level exception handling facilities."

While all this is true, the POSIX cleanup model clearly says that the
cleanup handlers SHALL ("must") be executed when cancel is delivered to a
thread, or when it calls pthread_exit(). The fact that it ought to be
implemented on top of exceptions merely means that a strict implementation
of POSIX must use a traditional model of 'finally' where the cleanup always
runs.

And of course POSIX is a C language standard which has no "exception like"
stuff except for cancel and exit, which cannot terminate the process. An
"exception" can never be considered either "unhandled" or "unexpected"...
so the issue of whether cleanup handlers ought to run when the process will
be terminated on an unexpected or unhandled exception is completely
irrelevant.

Once you move beyond the scope of that standard, the strict rules and in
particular the limitations are no longer meant to apply. We always intended
that a hypothetical C++ binding wouldn't have cleanup push/pop, but that
you'd use destructors or catch.

In strict POSIX there's no way to "finalize" exit or cancel short of thread
termination, and no other form of cleanup. In an implementation that
EXTENDS the strict POSIX standard with generalized exceptions (whether C++,
C, or whatever), then any thread using those generalized capabilities need
no longer follow the strict limitations, especially where they don't even
make sense.

> C) I just love aardvarks. ;-)

Well, they do eat bugs; but I don't think I'd want one as a pet. ;-)

(And I do agree that the Open Group's choice in naming their bug tracking
system "aardvark" is cute.)

Alexander Terekhov

unread,
May 16, 2003, 11:02:04 AM5/16/03
to

David Butenhof wrote:
[...]

> > A) The term 'thread termination' is currently NOT defined in the
> > standard.
>
> But that's not relevant, because the popping and execution of the cleanup
> handler is, in any case, defined to happen "when the thread calls
> pthread_exit()". It's not based on termination, though termination is
> presumed to occur AFTER all cleanup handlers have been popped and executed.

Really? ;-) <http://tinyurl.com/bxc7>

How about <possible addition to XBD: "Thread termination", draft>:

Thread termination occurs by a return from thread routine or when
REQUESTED by an explicit call to pthread_exit() or cancelation
delivery which has an effect of pthread_exit(PTHREAD_CANCELED)
called by the implementation at the cancelation point or from
async-cancel-safe region (after "disabling cancelability", of
course; see XSH ERN 77, 81 and 82) in the case of asynchronous
cancelation. It is IMPLEMENTATION-DEFINED whether thread
termination REQUEST may be "rejected" resulting in either normal
or abnormal process termination or shall always result... <blah-
blah about "possible catching"-while-cleanup-handlers unwinding,
TSD-cleanup, release of user-defined stack and wakeup of joiner(s)
["s" in the case of a "miltijoin" thread] ;-)>

regards,
alexander.

--
http://www.opengroup.org/onlinepubs/007904975/basedefs/xbd_chap03.html#tag_03_297

David Butenhof

unread,
May 19, 2003, 8:33:37 AM5/19/03
to
Alexander Terekhov wrote:

>
> David Butenhof wrote:
> [...]
>> > A) The term 'thread termination' is currently NOT defined in the
>> > standard.
>>
>> But that's not relevant, because the popping and execution of the cleanup
>> handler is, in any case, defined to happen "when the thread calls
>> pthread_exit()". It's not based on termination, though termination is
>> presumed to occur AFTER all cleanup handlers have been popped and
>> executed.
>
> Really? ;-) <http://tinyurl.com/bxc7>

Sigh. Definition games can be extremely tiring, Alexander. The point of
contention in the message you quote was the point at which a waiting joiner
is awakened. And granted there's room for silly games since there is, after
all, no standard definition of "thread termination". In this case, all that
is irrelevant. The terminating thread itself must call cleanup handlers and
TSD destructors, and therefore must still exist when they are called.

I'm glad you included a smiley on that reference, because I can at least
imagine that you called it out merely for idle amusement rather than
because you thought it proved anything. ;-)



> How about <possible addition to XBD: "Thread termination", draft>:
>
> Thread termination occurs by a return from thread routine or when
> REQUESTED by an explicit call to pthread_exit() or cancelation
> delivery which has an effect of pthread_exit(PTHREAD_CANCELED)
> called by the implementation at the cancelation point or from
> async-cancel-safe region (after "disabling cancelability", of
> course; see XSH ERN 77, 81 and 82) in the case of asynchronous
> cancelation. It is IMPLEMENTATION-DEFINED whether thread
> termination REQUEST may be "rejected" resulting in either normal
> or abnormal process termination or shall always result... <blah-
> blah about "possible catching"-while-cleanup-handlers unwinding,
> TSD-cleanup, release of user-defined stack and wakeup of joiner(s)
> ["s" in the case of a "miltijoin" thread] ;-)>

All you've done here is to remove any possibility of a strictly conforming
application being able to know that it can terminate a thread without
terminating the process. (Or even to know whether that termination will be
"normal" or "abnormal".) This is completely unacceptable. It is NOT
"implementation defined" whether pthread_exit or pthread_cancel works as
specified!

POSIX doesn't prohibit extensions. You can add interfaces that allow the
application to FINALIZE the propagation of an exit/cancel before
termination -- as is common in most implementations of exceptions. That's
fine, because no strictly conforming application will use such. You could
add interfaces to define cleanup handlers that run only when unwind will
occur.

You can do all sorts of things in implementations. But you cannot remove the
ability to write a strictly conforming application that behaves as defined
by the standard... and that's precisely what your "implementation defined"
does.

Aside from that, and the fact that it's not particularly well written, your
definition is a convoluted and tiresomely obvious restatement of what
everyone already knows. Which is to say, the first part of the paragraph,
(deleting everything from "It is IMPLEMENTATION-DEFINED" on), wouldn't look
entirely out of place in the standard, but we could do better with a little
thought.

Alexander Terekhov

unread,
May 19, 2003, 9:22:46 AM5/19/03
to

David Butenhof wrote:
>
> Alexander Terekhov wrote:
>
> >
> > David Butenhof wrote:
> > [...]
> >> > A) The term 'thread termination' is currently NOT defined in the
> >> > standard.
> >>
> >> But that's not relevant, because the popping and execution of the cleanup
> >> handler is, in any case, defined to happen "when the thread calls
> >> pthread_exit()". It's not based on termination, though termination is
> >> presumed to occur AFTER all cleanup handlers have been popped and
> >> executed.
> >
> > Really? ;-) <http://tinyurl.com/bxc7>
>
> Sigh. Definition games can be extremely tiring, Alexander. The point of
> contention in the message you quote was the point at which a waiting joiner
> is awakened.

Well, actually, the point of contention {in that thread} was the
following {erroneous} statements of mine (with respect to "join";
and the quoted "wishful-thinking" bits from the Rationale volume):

----
"join" provides a mechanism to ensure that a thread
has really been terminated and all its resources
(such as id, stack, etc) have really been reclaimed
(true on return from "join"). condition variable
solution cannot provide such a mechanism (sure,
that is not an issue if you never terminate threads;
really want to hold associated resources even if
you do *not* currently have (or anticipate in a
"short time") any new work for "idle" threads).

"The pthread_join() function provides a simple mechanism
allowing an application to wait for a thread to terminate.
After the thread terminates, the application may then choose
to clean up resources that were used by the thread. For
instance, after pthread_join() returns, any application-
provided stack storage could be reclaimed."
----

> And granted there's room for silly games since there is, after
> all, no standard definition of "thread termination". In this case, all that
> is irrelevant. The terminating thread itself must call cleanup handlers and
> TSD destructors, and therefore must still exist when they are called.
>
> I'm glad you included a smiley on that reference, because I can at least
> imagine that you called it out merely for idle amusement rather than
> because you thought it proved anything. ;-)

That's correct.

>
> > How about <possible addition to XBD: "Thread termination", draft>:
> >
> > Thread termination occurs by a return from thread routine or when
> > REQUESTED by an explicit call to pthread_exit() or cancelation
> > delivery which has an effect of pthread_exit(PTHREAD_CANCELED)
> > called by the implementation at the cancelation point or from
> > async-cancel-safe region (after "disabling cancelability", of
> > course; see XSH ERN 77, 81 and 82) in the case of asynchronous
> > cancelation. It is IMPLEMENTATION-DEFINED whether thread
> > termination REQUEST may be "rejected" resulting in either normal
> > or abnormal process termination or shall always result... <blah-
> > blah about "possible catching"-while-cleanup-handlers unwinding,
> > TSD-cleanup, release of user-defined stack and wakeup of joiner(s)
> > ["s" in the case of a "miltijoin" thread] ;-)>
>
> All you've done here is to remove any possibility of a strictly conforming
> application being able to know that it can terminate a thread without
> terminating the process. (Or even to know whether that termination will be
> "normal" or "abnormal".) This is completely unacceptable. It is NOT
> "implementation defined" whether pthread_exit or pthread_cancel works as
> specified!

Ok, ok. I agree that a strictly confirming *application* should rely
on guaranteed thread termination and always-unwinding. Perhaps we have
a "scope" problem here, again. All I want is that a strictly conforming
*function* shall not rely on unwinding IF it can be invoked within a
C++ scope/functions that could restrict propagation and unwinding using
exception specifications. There just ought to be some loophole/hint for
that, don't you think so?

>
> POSIX doesn't prohibit extensions. You can add interfaces that allow the
> application to FINALIZE the propagation of an exit/cancel before
> termination -- as is common in most implementations of exceptions. That's
> fine, because no strictly conforming application will use such. You could
> add interfaces to define cleanup handlers that run only when unwind will
> occur.
>
> You can do all sorts of things in implementations. But you cannot remove the
> ability to write a strictly conforming application that behaves as defined
> by the standard... and that's precisely what your "implementation defined"
> does.
>
> Aside from that, and the fact that it's not particularly well written, your
> definition is a convoluted and tiresomely obvious restatement of what
> everyone already knows. Which is to say, the first part of the paragraph,
> (deleting everything from "It is IMPLEMENTATION-DEFINED" on), wouldn't look
> entirely out of place in the standard, but we could do better with a little
> thought.

What would it look like? ;-)

regards,
alexander.

David Butenhof

unread,
May 20, 2003, 7:17:34 AM5/20/03
to
Alexander Terekhov wrote:

> David Butenhof wrote:
>>
>> All you've done here is to remove any possibility of a strictly
>> conforming application being able to know that it can terminate a thread
>> without terminating the process. (Or even to know whether that
>> termination will be "normal" or "abnormal".) This is completely
>> unacceptable. It is NOT "implementation defined" whether pthread_exit or
>> pthread_cancel works as specified!
>
> Ok, ok. I agree that a strictly confirming *application* should rely
> on guaranteed thread termination and always-unwinding. Perhaps we have
> a "scope" problem here, again. All I want is that a strictly conforming
> *function* shall not rely on unwinding IF it can be invoked within a
> C++ scope/functions that could restrict propagation and unwinding using
> exception specifications. There just ought to be some loophole/hint for
> that, don't you think so?

No, I don't, because none of this is even remotely within the scope of the
POSIX standard. POSIX deals with thread cleanup handlers, which are called
before the thread terminates. Period. There's no finalization; there's no
chance of process termination. It is completely irrelevant to "strictly
conforming" POSIX implementations or applications what might happen if it
were, hypothetically, possible for an "unhandled" "exception" to terminate
the process, because neither "unhandled" nor "exception" are meaningful
concepts.

IF there were a "C++ binding to POSIX", and IF that binding said that the
mechanism for POSIX cleanup was actually "C++" exception propagation, this
would need to be covered. But that hasn't happened and very likely won't
happen.

Anyone is free to build an implementation where thread exit/cancel can be
finalized (we do that), as long as it doesn't affect a strictly conforming
application -- and it can't because that strictly conforming application
obviously cannot be using non-standard stuff like our C TRY/FINALLY, or Ada
try/finally, or C++ try/catch(...). Similarly, my implementation can run
cleanup handlers when propagating a C++/Java/Ada/etc. exception, because
strictly conforming POSIX applications can't raise exceptions and it
doesn't matter to them.

In theory, you can also build an implementation where it's possible to have
POSIX cleanup handlers that won't be run when an exception propagates but
isn't handled. However, you must figure out some way to introduce this
extension that won't break any strictly conforming POSIX application. This
is a little more complicated. The first step is to note that only thread
exit and cancel "exceptions" exist in POSIX, and they're always handled; so
any unhandled exception (or any other exception) is already nonstandard.
However, in this case I think it's dangerous to simply declare that cleanup
handlers won't be run -- because even though the application as a whole is
clearly "not strictly conforming", your difference in behavior isn't
"gentle". Even reasonable "conforming with extensions" code may depend on
cleanup handlers, for example, to restore external invariants.

>> Aside from that, and the fact that it's not particularly well written,
>> your definition is a convoluted and tiresomely obvious restatement of
>> what everyone already knows. Which is to say, the first part of the
>> paragraph, (deleting everything from "It is IMPLEMENTATION-DEFINED" on),
>> wouldn't look entirely out of place in the standard, but we could do
>> better with a little thought.
>
> What would it look like? ;-)

Personally, I'd start from the "process termination", but instead of
"normal" and "abnormal" termination I'd talk about normal thread exit and
cancellation exit; where the difference is that in the former case the
value is that passed to pthread_exit() or returned by the thread start
routine, while in the latter the value is PTHREAD_CANCELED. All the stuff
about from where it might seem pthread_exit() was called on cancellation is
pointless, since there's no way a conforming (much less strictly
conforming) application can tell the difference. POSIX doesn't even require
that the stack be available at the time of pthread_join(), and at the very
least it'll probably be unwound; the joiner cannot possibly count on any
ability to determine the state of the thread at the time of the cancel or
exit call.

I really don't have time now to do more "wordsmithing", though. While I
realize this isn't all quite "idle conversation", I do already spend far
too much time on it!

Alexander Terekhov

unread,
May 20, 2003, 10:55:30 AM5/20/03
to

David Butenhof wrote:
[...]

> I really don't have time now to do more "wordsmithing", though. While I
> realize this isn't all quite "idle conversation", I do already spend far
> too much time on it!

Ok. Let's close it (for now). BTW, FYI:

<Forward Quoted: Subject: Re: Re: boost::mutex::scoped_lock conventions
Date: Tue, 20 May 2003 09:13:04 -0500 (CDT)
Newsgroups: gmane.comp.lib.boost.user>

"William E. Kempf" wrote:
>
> Alexander Terekhov said:
> >
> > "William E. Kempf" wrote:
> > [...]
> >> >> BTW: If the above functions are what I think they are, I can
> >> provide them in Boost.Threads with out anything being changed in
> >> the POSIX standard.
> >> >
> >> > We need a POSIX.C++ standard, that's why you and others should join
> >> the Austin group. (check out some messages on the "Austin Off Topic
> >> Discussion" reflector)
> >>
> >> Why do we need that?
> >>
> >> I'm not trying to say that it's a bad idea, but POSIX is not a
> >> standard that's universally adopted, and is focused on language
> >> extensions. It's more than worth considering what the POSIX standard
> >> says and does by Boost.Threads, and it would be folly for me to
> >> recommend to the C++ standards committee any library that violated
> >> POSIX in any way, or was counter to POSIX, or couldn't leverage
> >> current or future POSIX standards. But that doesn't mean that I should
> >> have a vested interest in shaping a POSIX.C++ standard.
> >
> > Here's an illustration. (no link this time; see c.p.t. for details)
> >
> > <quote>


> >
> >> Ok, ok. I agree that a strictly confirming *application* should rely
> >> on guaranteed thread termination and always-unwinding. Perhaps we have
> >> a "scope" problem here, again. All I want is that a strictly
> >> conforming *function* shall not rely on unwinding IF it can be invoked
> >> within a C++ scope/functions that could restrict propagation and
> >> unwinding using exception specifications. There just ought to be some
> >> loophole/hint for that, don't you think so?
> >
> > No, I don't, because none of this is even remotely within the scope of
> > the POSIX standard. POSIX deals with thread cleanup handlers, which are
> > called before the thread terminates. Period. There's no finalization;
> > there's no chance of process termination. It is completely irrelevant
> > to "strictly conforming" POSIX implementations or applications what
> > might happen if it were, hypothetically, possible for an "unhandled"
> > "exception" to terminate the process, because neither "unhandled" nor
> > "exception" are meaningful concepts.
> >
> > IF there were a "C++ binding to POSIX", and IF that binding said that
> > the mechanism for POSIX cleanup was actually "C++" exception
> > propagation, this would need to be covered. But that hasn't happened
> > and very likely won't happen.
> >

> > </quote>
>
> The motivations are backwards here, though. If the C++ language adopts a
> threading library, POSIX systems will have a lot of motivation for
> defining a POSIX C++ binding, or at the very least, making a particular
> implementation's POSIX binding compatible with the C++ threading.
>
> From my perspective (and I would assume the perspective of the C++
> committee), what's important is that anything I do in Boost.Threads needs
> to be compatible with POSIX (as well as other threading systems), but
> that's really it. I don't have any vested interest in extending POSIX for
> any reason. So, I'm intested in what's going on, but I'm not a good
> candidate for helping champion any proposals you're making for POSIX.
>
> --
> William E. Kempf
>
> ------------------------ Yahoo! Groups Sponsor ---------------------~-->
> Get A Free Psychic Reading!
> Your Online Answer To Life's Important Questions.
> http://us.click.yahoo.com/aM1XQD/od7FAA/uetFAA/EbFolB/TM
> ---------------------------------------------------------------------~->
>
> Info: <http://www.boost.org>
> Wiki: <http://www.crystalclearsoftware.com/cgi-bin/boost_wiki/wiki.pl>
> Unsubscribe: <mailto:boost-users...@yahoogroups.com>
>
>
> Your use of Yahoo! Groups is subject to http://docs.yahoo.com/info/terms/

regards,
alexander.

David Butenhof

unread,
May 21, 2003, 9:46:47 AM5/21/03
to
Alexander Terekhov wrote:

>> > "William E. Kempf" wrote:
>> The motivations are backwards here, though. If the C++ language adopts a
>> threading library, POSIX systems will have a lot of motivation for
>> defining a POSIX C++ binding, or at the very least, making a particular
>> implementation's POSIX binding compatible with the C++ threading.

Right now, the C++ language has, by default and convention, a POSIX binding;
1003.1-2001. The C and C++ languages are sufficiently interoperable that
this presents only a few restrictions on the use by C++ code, around
exceptions and member functions. OK, so the thread start routine needs to
be 'extern "C"' -- a minor inconvenience. OK, so there's no portable
standard on interoperability between POSIX cleanup and C++ exceptions, and
I'll resist suggesting that only an idiot would fail to make them
completely compatible and interoperable; but at least most people can be
educated to realize that they ought to be.

The big hurdle for a true C++ binding is that the current state of affairs
is "good enough" for most people, and the political process of developing a
full native C++ binding would be painful. (Remember, it's not just saying
that the thread::create method takes a class member at which the thread
will start... it means reviewing every method and template in the STL to
determine which have thread safety requirements, and deciding precisely
what those requirements are and how to meet them. Then there's the matter
of cancellation points... and so forth.)

When and if the C++ standard adds true thread support, that will be, by
default and in practice, the thread binding for C++; whether the underlying
thread environment is POSIX, Win32, or something else. This is great, as
long as it doesn't do or say anything stupid, but it still leaves a few
loopholes because inevitably people will continue to write applications
that mix languages. Mixing C and C++ has never been a problem; but if the
thread model in C++ is radically different, it could become a problem.
Furthermore, there's a missing piece that neither POSIX 1003.1-2001 plus
ISO C++ 2005 (or whatever), or even 1003.1-2001 plus a hypothetical
"1003.C++" will necessarily (or even likely) supply -- and that's how the
two interoperate.

If C++ or 1003.C++ says that thread::cancel raises an exception, and 1003.1
says that pthread_cancel() invokes cleanup handlers, does that mean that
cancelling a thread with pthread_cancel() will trigger catch(...), or even
destructors? Well, maybe not. This could more easily be solved with a
1003.C++, perhaps, since at least the two standards are in a family. Since
the C++ standard is unlikely to mention POSIX any more than now, it's
unlikely to provide any guarantees.

Perhaps that would provide an opportunity for a smaller POSIX project,
though; a PROFILE that would chink the holes where the two walls meet. In
effect, specifying a "POSIX platform" supporting both threads and C++ that
simply says "C++ cancellation is the same as POSIX cancellation", "POSIX
cleanup handlers are logically and semantically the same as C++ object
destructors", and "POSIX cancellation is visible as a C++ exception".

Alexander Terekhov

unread,
May 21, 2003, 5:45:23 PM5/21/03
to
< I'm once again "googling" >

David Butenhof wrote:
[...]

> This is sad; ....

<Forward Inline>

Received: from [128.213.8.10] (helo=netlab.cs.rpi.edu)
by mx22.web.de with esmtp (WEB.DE 4.98 #244)
id 19IaCa-00065I-00
for tere...@web.de; Wed, 21 May 2003 22:28:44 +0200
Received: (from cppmods@localhost)
by netlab.cs.rpi.edu (8.9.1/8.9.1) id QAA00297;
Wed, 21 May 2003 16:30:55 -0400 (EDT)
Date: Wed, 21 May 2003 16:30:55 -0400 (EDT)
Message-Id: <2003052120...@netlab.cs.rpi.edu>
From: c++-r...@netlab.cs.rpi.edu
To: tere...@web.de
Cc: c++-r...@netlab.cs.rpi.edu
Subject: reject [nothing new]
Sender: cpp...@netlab.cs.rpi.edu


Message-ID: <3ECB7BD3...@web.de>
Newsgroups: comp.lang.c++.moderated
From: Alexander Terekhov <tere...@web.de>
Reply-To: tere...@web.de
Subject: Re: Error handling: strategies, patterns, solutions
Organization: unknown
References: <b4272b91.0305...@posting.google.com>
X-Original-Date: Wed, 21 May 2003 15:14:59 +0200
X-Submission-Address: c++-s...@netlab.cs.rpi.edu

{There was no new technical content added to the quoted article
(I-agrees, me-toos, and repasting from other articles fall into
this category). Please add some technical C++ content and
resubmit. Thanks! -mod}


Soukhonossenko Kirill wrote:
>
> Hi!
> Could someone suggest good readings covering subj? Web links, books,
> code samples are welcome. I'm espessially intrested in production
> system's approaches|solutions|patterns, ....

http://www.bleading-edge.com/Publications/C++Report/v9603/Article2a.htm
http://www.bleading-edge.com/Publications/C++Report/v9605/Column1.htm
http://www.bleading-edge.com/Publications/C++Report/v9607/Column2.htm
http://www.cuj.com/experts/2103/reeves.htm
http://www.boost.org/more/generic_exception_safety.html
http://www.gotw.ca
http://groups.google.com/groups?selm=38192d73.19559206%40nntp.netcom.ca
http://groups.google.com/groups?selm=c29b5e33.0202161451.2ef75f1f%40post
ing.google.com
http://lists.boost.org/MailArchives/boost/msg34151.php
http://lists.boost.org/MailArchives/boost/msg36228.php
http://lists.boost.org/MailArchives/boost/msg36230.php
http://lists.boost.org/MailArchives/boost/msg36232.php

http://groups.google.com/groups?threadm=3EC0ECAA.6520B266%40web.de


(Subject: Exception handling... it's time to fix the standard)

Well, recently, I also wrote (and posted to c.l.c++) this:

----->8
Robb Williams wrote:
[...]
> The function throwing the exception can advertise to the callers what
> exceptions it can possibly throw. The possible thrown exceptions are
> listed in the function prototype. This will give the callers a clue as
> to what to catch.

Unfortunately, it will also inject a function-try-block with totally
braindamaged catch(...); also-injected rethrowing/noop "handlers"
aside for a moment. *Documentation* shall be used to give a clue to
callers. Exception specifications (once/if they become repaired) shall
be used to inject exception propagation FENCES... that would facilitate
faster code (throw() optimizations)... and totally robust "failover"
(in the case of unexpected "errors") with post-mortem (or even JIT)
problem analyses. Well, you'd also be able to use RAII with respect to
the std::unexpected handers (and provide some meaningful "translation",
for example) -- you just can't do it currently due to a bunch of rather
braindead bits in the current C++ standard.
----->8

regards,
alexander.

--
"Somebody asked for a *technical* point that distinguishes mainframes.
I believe the one above is one of the most important. It is was makes
it possible for software (like z/OS) to fence off problems so the rest
of the system continues to run. A lot of this is already happening
below the OS, at the microcode and even hardware level: instruction
checkpoint and retry, spare processor takeover, etc. The cardinal
rule is that errors must not be allowed to propagate, and if in doubt,
it is better to stop dead (and let higher layers attempt recovery)."

-- "hack"

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

Reply all
Reply to author
Forward
0 new messages