Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

throw() can be good for optimization

22 views
Skip to first unread message

P.M.

unread,
Feb 19, 2002, 12:32:47 AM2/19/02
to
The advice that exception specifications may introduce a performance
penalty (Exceptional C++ p54) seems to be contradicted in one special
circumstance.

The VC++ documentation has the following paragraph describing a
nothrow specification throw() [which, btw, is equivalent to
__declspec(nothrow)]:

"... the compiler can eliminate the mechanics of tracking the lifetime
of certain unwindable objects in such a function, and significantly
reduce the code size."

I wonder if this is true of other compilers as well?

- Peter.

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

Garry Lancaster

unread,
Feb 19, 2002, 2:38:03 PM2/19/02
to
Peter M. :

> The advice that exception specifications may introduce a performance
> penalty (Exceptional C++ p54) seems to be contradicted in one special
> circumstance.
>
> The VC++ documentation has the following paragraph describing a
> nothrow specification throw() [which, btw, is equivalent to
> __declspec(nothrow)]:
>
> "... the compiler can eliminate the mechanics of tracking the lifetime
> of certain unwindable objects in such a function, and significantly
> reduce the code size."
>
> I wonder if this is true of other compilers as well?

Hopefully not since Visual C++'s optimization
appears to be at the expense of correctness.

A program:

<code>
#include <iostream>
using namespace std;

void foo() throw();

class test
{
public:
test() { cout << "ctor.\n"; }
~test() { cout << "dtor.\n"; }
};

int main(int argc, char* argv[])
{
try
{
cout << "Before ES violation.\n";
foo();
cout << "After ES violation.\n";
}
catch(...)
{
cout << "Caught an exception!\n";
}
return 0;
}

void foo() throw()
{
test t;
throw 1;
}
</code>

VC6 and VC7 (beta 2) both print

Before ES violation.
ctor.
Caught an exception!

I believe they *should* print

Before ES violation.
ctor.
dtor.

In other words, both versions of VC leak when
they should unwind the stack and re-throw when
they should abort.

(Incidentally, Borland C++ Builder 5 gets it right.)

Kind regards

Garry Lancaster
Codemill Ltd
Visit our web site at http://www.codemill.net

Francis Glassborow

unread,
Feb 19, 2002, 3:15:46 PM2/19/02
to
In article <6db3f637.02021...@posting.google.com>, P.M.
<p...@cpugrid.net> writes

>The advice that exception specifications may introduce a performance
>penalty (Exceptional C++ p54) seems to be contradicted in one special
>circumstance.
>
>The VC++ documentation has the following paragraph describing a
>nothrow specification throw() [which, btw, is equivalent to
>__declspec(nothrow)]:
>
>"... the compiler can eliminate the mechanics of tracking the lifetime
>of certain unwindable objects in such a function, and significantly
>reduce the code size."
>
>I wonder if this is true of other compilers as well?

Yes, and it is one reason why throw() is used even when other ESs are
avoided.


--
Francis Glassborow
Check out the ACCU Spring Conference 2002
4 Days, 4 tracks, 4+ languages, World class speakers
For details see: http://www.accu.org/events/public/accu0204.htm

Garry Lancaster

unread,
Feb 20, 2002, 7:23:54 AM2/20/02
to
P.M. writes:
> >The advice that exception specifications may introduce a performance
> >penalty (Exceptional C++ p54) seems to be contradicted in one special
> >circumstance.
> >
> >The VC++ documentation has the following paragraph describing a
> >nothrow specification throw() [which, btw, is equivalent to
> >__declspec(nothrow)]:
> >
> >"... the compiler can eliminate the mechanics of tracking the lifetime
> >of certain unwindable objects in such a function, and significantly
> >reduce the code size."
> >
> >I wonder if this is true of other compilers as well?

Francis Glassborow:


> Yes, and it is one reason why throw() is used even when other ESs are
> avoided.

I don't understand the efficiency advantage of
throw() inside the throw() function itself.

I can understand how a compiler could make
efficiency gains for a function it could prove
could never encounter an exception. Indeed,
the no-throw guarantee offered by throw() could
be useful in this respect for those functions that
*call* a no-throw function. But this promise, and
the one made by throw() are orthogonal: a no-throw
function doesn't promise never to encounter an
exception, it just promises never to allow one
to leave the function.

If anything, this suggests a no-throw function
will itself be, if anything, *less* efficient than an
equivalent function without an exception
specification.

Kind regards

Garry Lancaster
Codemill Ltd
Visit our web site at http://www.codemill.net

Peter Dimov

unread,
Feb 20, 2002, 9:27:28 AM2/20/02
to
"Garry Lancaster" <glanc...@ntlworld.com> wrote in message news:<P0vc8.108997$H37.14...@news2-win.server.ntlworld.com>...

> Peter M. :
> > The advice that exception specifications may introduce a performance
> > penalty (Exceptional C++ p54) seems to be contradicted in one special
> > circumstance.
> >
> > The VC++ documentation has the following paragraph describing a
> > nothrow specification throw() [which, btw, is equivalent to
> > __declspec(nothrow)]:
> >
> > "... the compiler can eliminate the mechanics of tracking the lifetime
> > of certain unwindable objects in such a function, and significantly
> > reduce the code size."
> >
> > I wonder if this is true of other compilers as well?
>
> Hopefully not since Visual C++'s optimization
> appears to be at the expense of correctness.

Yes, MSVC implements the "undefined behavior if not met" ES variation;
this allows more optimizations than the "correct" ESs. An interesting
case where it's better than the standard. ;-) (It will probably be
"fixed" as part of the quest for compliance. Unfortunately.)

Francis Glassborow

unread,
Feb 20, 2002, 11:09:36 AM2/20/02
to
In article <OOKc8.69369$YA2.8...@news11-gui.server.ntli.net>, Garry
Lancaster <glanc...@ntlworld.com> writes

>Francis Glassborow:
> > Yes, and it is one reason why throw() is used even when other ESs are
> > avoided.
>
>I don't understand the efficiency advantage of
>throw() inside the throw() function itself.

Well the compiler knows that it does not need to provide any mechanism
for continuing the program other than to exit via the designated
callback provided by the unexpected_handler. I do not know if this could
make the code for the function more efficient, but I am certain that it
cannot make it less so (with the usual caveat that this refers to the
case where no exception actually attempts to propagate here)

The gains in functions calling nothrow functions can be considerable
because no mechanism needs to be provided within those functions to
handle exceptions propagated by that call. Major gains start to be
possible when a nothrow function only calls nothrow functions.


--
Francis Glassborow
Check out the ACCU Spring Conference 2002
4 Days, 4 tracks, 4+ languages, World class speakers
For details see: http://www.accu.org/events/public/accu0204.htm

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

David Abrahams

unread,
Feb 20, 2002, 3:04:20 PM2/20/02
to

"Francis Glassborow" <francis.g...@ntlworld.com> wrote in message
news:g7gdpUE9...@robinton.ntlworld.com...

> In article <OOKc8.69369$YA2.8...@news11-gui.server.ntli.net>, Garry
> Lancaster <glanc...@ntlworld.com> writes

> >I don't understand the efficiency advantage of


> >throw() inside the throw() function itself.
>
> Well the compiler knows that it does not need to provide any mechanism
> for continuing the program other than to exit via the designated
> callback provided by the unexpected_handler. I do not know if this could
> make the code for the function more efficient, but I am certain that it
> cannot make it less so (with the usual caveat that this refers to the
> case where no exception actually attempts to propagate here)

What makes you so certain?

void f();
void g() throw() { f(); }
void h() { g(); }

In a poor setjmp/longjmp-style implementation of EH, g() has to spend time
registering/unregistering what is essentially a try/catch block to handle
exceptions and call unexpected(), whether or not an exception is thrown.
That would be unneccessary if there were no exception-specification.

-Dave

Alexander Terekhov

unread,
Feb 20, 2002, 3:10:06 PM2/20/02
to

Garry Lancaster wrote:

[...unwinding on ES violation...]

Uhmmm...

"8 Whenever an exception is thrown and the search for a handler
^^^^^^^^^^^^^^^^^^^^
(15.3) encounters the outermost block of a function with an
exception-specification, the function unexpected() is
called (15.5.2) if the exception-specification does not allow
the exception."

"[lib.unexpected] 18.6.2.4 unexpected

void unexpected();
1 Called by the implementation when a function exits via an
^^^^^^^^^^^^^^^^
exception not allowed by its exception-specification
^^^^^^^^^^^^^^^^^^^^^^^^^^^
(15.5.2). May also be called directly by the program.
2 Effects: Calls the unexpected_handler function in
effect immediately after evaluating the throw-expression
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(18.6.2.2), if called by the implementation,"


#include <iostream>
#include <exception>
using namespace std;

void foo() throw( bad_exception );
void another_foo();

void my_unexpected()
{
throw;
}

int main(int argc, char* argv[])
{

set_unexpected( my_unexpected );


try
{
cout << "Before ES violation.\n";
foo();
cout << "After ES violation.\n";
}
catch(...)
{
cout << "Caught an exception!\n";
}
return 0;
}

void foo() throw( bad_exception )
{
try {
another_foo();
}
catch( bad_exception )
{
cout << "Do we reach this handler? I think "
"that it would be nice, really!!" << endl;
throw;
}
}

void another_foo()
{
throw 1;
}

regards,
alexander.

David Abrahams

unread,
Feb 20, 2002, 4:11:50 PM2/20/02
to

"Garry Lancaster" <glanc...@ntlworld.com> wrote in message
news:OOKc8.69369$YA2.8...@news11-gui.server.ntli.net...

Bravo!

That's exactly right, which is why throw() is only going to produce
efficiency gains if you use it consistently, all the way down to the leaf
functions. Furthermore, with a good implmenetation of exception-handling,
the savings will only be in the overall size of your executable image.
Probably worthwhile for embedded software, given the right compiler.

Oh, and BTW, the VC++6 documentation doesn't prove anything: they cheat by
not implementing the runtime semantics of exception-specifications. The
following program prints "caught" and exits normally:

#include <iostream>

void f() throw()
{
throw 1;
}

int main()
{
try {
f();
}catch(...)
{
std::cout << "caught\n";
}
return 0;

Garry Lancaster

unread,
Feb 20, 2002, 4:16:29 PM2/20/02
to
> > Peter M. :
> > > The advice that exception specifications may introduce a performance
> > > penalty (Exceptional C++ p54) seems to be contradicted in one special
> > > circumstance.
> > >
> > > The VC++ documentation has the following paragraph describing a
> > > nothrow specification throw() [which, btw, is equivalent to
> > > __declspec(nothrow)]:
> > >
> > > "... the compiler can eliminate the mechanics of tracking the
lifetime
> > > of certain unwindable objects in such a function, and significantly
> > > reduce the code size."
> > >
> > > I wonder if this is true of other compilers as well?

Garry Lancaster:


> > Hopefully not since Visual C++'s optimization
> > appears to be at the expense of correctness.

Peter Dimov:


> Yes, MSVC implements the "undefined behavior if not met" ES variation;
> this allows more optimizations than the "correct" ESs. An interesting
> case where it's better than the standard. ;-) (It will probably be
> "fixed" as part of the quest for compliance. Unfortunately.)

You are of course entitled to your opinion,
and I do note your smiley.

I don't like the current non-standard VC
implementation of ESs. If VC customers want a
special optimization that works in a different way to
the standard ESs, VC already has
__declspec(nothrow), which seems ideal for
switching on the optimization [1]. But please, MS,
fix standard ESs.

Kind regards

Garry Lancaster
Codemill Ltd
Visit our web site at http://www.codemill.net

NOTES:

1. For the throw() ES only. ESs with non-empty
parentheses probably won't benefit much, if at all,
from the optimization in any case.

Garry Lancaster

unread,
Feb 20, 2002, 4:23:06 PM2/20/02
to
> >Francis Glassborow:
> > > Yes, and it is one reason why throw() is used even when other ESs are
> > > avoided.

Garry Lancaster:


> >I don't understand the efficiency advantage of
> >throw() inside the throw() function itself.

Francis Glassborow:


> Well the compiler knows that it does not need to provide any mechanism
> for continuing the program other than to exit via the designated
> callback provided by the unexpected_handler. I do not know if this could
> make the code for the function more efficient, but I am certain that it
> cannot make it less so (with the usual caveat that this refers to the
> case where no exception actually attempts to propagate here)

I don't see where your gain is coming from.

1. The compiler needs to generate the
normal return sequence for non-exceptional
function exit. No gain there.

2. The compiler still has to generate code
within the function to unwind the stack
should an exception be encountered
[15.5.2/1] so, no gain there.

3. Plus, as with any ES, the compiler has to
add the moral equivalent of a catch block,
and add a call to unexpected(). I am
willing to believe that a smart compiler
can subsume this stuff within the general
exception scaffolding overhead [1] but I
bet some do not. So, best case is no
pain, but no gain either.

> The gains in functions calling nothrow functions can be considerable
> because no mechanism needs to be provided within those functions to
> handle exceptions propagated by that call.

Here we agree. I made this point in my
previous post. And that's why I was very
careful to write "I don't understand the


efficiency advantage of throw() inside the

throw() function itself", rather than just


"I don't understand the efficiency advantage

of throw()".

> Major gains start to be
> possible when a nothrow function only calls
> nothrow functions.

No extra gain above that achieved by any other
function calling a nothrow function, as far
as I can see. [2]

Kind regards

Garry Lancaster
Codemill Ltd
Visit our web site at http://www.codemill.net

NOTES:

1. Maybe this means the compiler is missing
some optimization opportunities for functions
without an exception specification. Then it
would not be so smart.

2. Of course, setting up a cascade of nothrow
functions has advantages purely because there
will then be more calls of nothrow functions.

Francis Glassborow

unread,
Feb 20, 2002, 6:33:10 PM2/20/02
to
In article <a50rhb$7un$1...@bob.news.rcn.net>, David Abrahams
<david.a...@rcn.com> writes

>In a poor setjmp/longjmp-style implementation of EH, g() has to spend time
>registering/unregistering what is essentially a try/catch block to handle
>exceptions and call unexpected(), whether or not an exception is thrown.
>That would be unneccessary if there were no exception-specification.

Well if we are going to consider poor implementations all bets are off.
I could even deal with ESs in such an inefficient way that empty spec
were the worst possible case. :)


--
Francis Glassborow
Check out the ACCU Spring Conference 2002
4 Days, 4 tracks, 4+ languages, World class speakers
For details see: http://www.accu.org/events/public/accu0204.htm

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Alexander Terekhov

unread,
Feb 20, 2002, 11:43:06 PM2/20/02
to

David Abrahams wrote:
[...]

> Oh, and BTW, the VC++6 documentation doesn't prove anything: they cheat by
> not implementing the runtime semantics of exception-specifications. The
> following program prints "caught" and exits normally:
>
> #include <iostream>
>
> void f() throw()
> {
> throw 1;
> }
>
> int main()
> {
> try {
> f();
> }catch(...)
> {
> std::cout << "caught\n";
> }
> return 0;
> }

#include <cstdlib>
#include <iostream>

void f() throw()
{
throw 1;
}

void my_terminate()
{ std::cout << "terminated" << std::endl; abort(); }

int main()
{
set_terminate( my_terminate );


try {
f();
}catch(...)
{
std::cout << "caught\n";
}
return 0;
}


C:\PROGRA~1\MICROS~4\VC98\Bin>cl -O2 -GX MS-dwa.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for
80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.

MS-dwa.cpp
Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

/out:MS-dwa.exe
MS-dwa.obj

C:\PROGRA~1\MICROS~4\VC98\Bin>MS-dwa
terminated

abnormal program termination

Alexander Terekhov

unread,
Feb 20, 2002, 11:43:45 PM2/20/02
to

Garry Lancaster wrote:
[...]

> 3. Plus, as with any ES, the compiler has to
> add the moral equivalent of a catch block,
> and add a call to unexpected().

That would mean *unwinding* on ES violation,
prior to "calling" unexpected()... but the
standard SAYS:

"void unexpected();
...


2 Effects: Calls the unexpected_handler function in
effect immediately after evaluating the throw-expression
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(18.6.2.2), if called by the implementation,"

Perhaps someone could clarify...

regards,
alexander.

David Abrahams

unread,
Feb 20, 2002, 11:58:32 PM2/20/02
to

"Francis Glassborow" <francis.g...@ntlworld.com> wrote in message
news:hR29DpIg...@robinton.ntlworld.com...

> In article <a50rhb$7un$1...@bob.news.rcn.net>, David Abrahams
> <david.a...@rcn.com> writes
> >In a poor setjmp/longjmp-style implementation of EH, g() has to spend
time
> >registering/unregistering what is essentially a try/catch block to handle
> >exceptions and call unexpected(), whether or not an exception is thrown.
> >That would be unneccessary if there were no exception-specification.
>
> Well if we are going to consider poor implementations all bets are off.
> I could even deal with ESs in such an inefficient way that empty spec
> were the worst possible case. :)

If we are /not/ going to consider poor implementations there's almost no
point in talking about efficiency in the context of exception handling: an
ideal implmentation will only expend executable image size (not speed) on
exception-handling, and even then the space won't impact anything except in
the case where exceptions are actually thrown, since the tables and code
will be organized to stay out of the cache. With such an implementation,
throw() is only going to save you space on disk. Admittedly that space
matters to a small subset of all programs, but not to most of them.

-Dave

Garry Lancaster

unread,
Feb 21, 2002, 6:51:26 AM2/21/02
to
> Garry Lancaster wrote:
> [...]
> > 3. Plus, as with any ES, the compiler has to
> > add the moral equivalent of a catch block,
> > and add a call to unexpected().

Alexander Terekhov:


> That would mean *unwinding* on ES violation,
> prior to "calling" unexpected()...

Yes. Some bits of the standard are a bit vague
about this but It's quite explicit in 15.5.2/1

"If a function with an exception-specification throws an
exception that is not listed in the exception-specification,
the function

void unexpected();

is called (18.6.2) immediately after completing the stack
unwinding for the former function."

> but the
> standard SAYS:
>
> "void unexpected();
> ...
> 2 Effects: Calls the unexpected_handler function in
> effect immediately after evaluating the throw-expression
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> (18.6.2.2), if called by the implementation,"
>
> Perhaps someone could clarify...

Vague. I can think of two meanings:

1. Which unexpected_handler function is called? The
one in effect immediately after evaluating the
throw-expression.

2. When is the unexpected_handler function
called? In effect, immediately after evaluating the
throw-expression.

Since (2) contradicts 15.5.2/1 I assume (1) was
intended.

If we quote the whole of para 18.6.2.4/2 rather
than just the previously quoted snippet, we get:

"Effects: Calls the unexpected_handler function in
effect immediately after evaluating the throw-expression

(18.6.2.2), if called by the implementation, or calls the
current unexpected_handler, if called by the program."

I think it becomes a little clearer that the para is
telling us *which* unexpected_handler is called,
not *when* it is called.

Kind regards

Garry Lancaster
Codemill Ltd
Visit our web site at http://www.codemill.net

Peter Dimov

unread,
Feb 21, 2002, 9:42:53 AM2/21/02
to
"David Abrahams" <david.a...@rcn.com> wrote in message news:<a51gg4$p8l$1...@bob.news.rcn.net>...

It matters a lot due to psychological reasons. You know what happens
when a large scale project executable (with no throw statements)
halves its size when exceptions are turned off. "Code bloat!"

Peter Dimov

unread,
Feb 21, 2002, 9:44:48 AM2/21/02
to
"Garry Lancaster" <glanc...@ntlworld.com> wrote in message news:<OOKc8.69369$YA2.8...@news11-gui.server.ntli.net>...
[...]

> If anything, this suggests a no-throw function
> will itself be, if anything, *less* efficient than an
> equivalent function without an exception
> specification.

Which is a direct consequence of the current standard.

Peter Dimov

unread,
Feb 21, 2002, 9:52:44 AM2/21/02
to
"Garry Lancaster" <glanc...@ntlworld.com> wrote in message news:<WAPc8.70626$YA2.9...@news11-gui.server.ntli.net>...

> Peter Dimov:
> > Yes, MSVC implements the "undefined behavior if not met" ES variation;
> > this allows more optimizations than the "correct" ESs. An interesting
> > case where it's better than the standard. ;-) (It will probably be
> > "fixed" as part of the quest for compliance. Unfortunately.)
>
> You are of course entitled to your opinion,
> and I do note your smiley.

You have explained quite well in another post why standard throw() can
be a pessimization.

> I don't like the current non-standard VC
> implementation of ESs. If VC customers want a
> special optimization that works in a different way to
> the standard ESs, VC already has
> __declspec(nothrow), which seems ideal for
> switching on the optimization [1]. But please, MS,
> fix standard ESs.

Agreed. Please, MS, fix standard ESs by proposing a change to the
standard.

Garry Lancaster

unread,
Feb 21, 2002, 9:53:02 AM2/21/02
to
David Abrahams wrote:
> > Oh, and BTW, the VC++6 documentation doesn't prove anything: they cheat
by
> > not implementing the runtime semantics of exception-specifications. The
> > following program prints "caught" and exits normally:
[snip example of MSVC getting exception specs wrong]

Alexander Terekhov:
[snip example of MSVC getting exception specs right]

The behaviour of MSVC in the face of ES violations
is, as Peter Dimov states, undefined. And undefined
means it might even get it right, sometimes.

I've found the behaviour can even vary for the same
program, depending on whether you run it in the
debugger or not. (Possibly build settings also
have an effect.)

Kind regards

Garry Lancaster
Codemill Ltd
Visit our web site at http://www.codemill.net

Garry Lancaster

unread,
Feb 21, 2002, 6:57:29 PM2/21/02
to
Garry Lancaster:

> > I don't like the current non-standard VC
> > implementation of ESs. If VC customers want a
> > special optimization that works in a different way to
> > the standard ESs, VC already has
> > __declspec(nothrow), which seems ideal for
> > switching on the optimization [1]. But please, MS,
> > fix standard ESs.

Peter Dimov:


> Agreed. Please, MS, fix standard ESs by proposing a
> change to the standard.

That's not agreeing, that's being mischievous ;-)

For what it's worth, when given a choice, I don't
use ESs now and I wouldn't use them if they
worked the way you (and MS, presumably)
want them to work.

I would use only throw() (it's the most
useful ES as it signifies the no-throw
exception safety guarantee) if it were
statically checked. As for the other ESs, if it
were up to me they'd be removed from
the language, but other people seem to
like them...

Kind regards

Garry Lancaster
Codemill Ltd
Visit our web site at http://www.codemill.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Alexander Terekhov

unread,
Feb 21, 2002, 10:15:42 PM2/21/02
to

Garry Lancaster wrote:
[...]

Well, but why would you ever want/need that unwinding
("partial", btw) in most likely (throw() ES violation)
*terminating* program (and things along the lines of
"just ignored" bad_exception handler(s) aside)? You
could always do your inter-process cleanup in your
own terminate_handler, if needed.

On the other hand, for debugging/problem determination,
unwinding is evil, really! So I think that MSVC -O2 -GX
does it RIGHT (unexpected()/throw(something) aside)!!

Also, how about that "search for a handler" bit...

"Whenever an exception is thrown and the search for a

handler (15.3) encounters the outermost block of a


function with an exception-specification, the function
unexpected() is called (15.5.2) if the exception-specification
does not allow the exception."

...taking into account this:

"7 If no match is found among the handlers for a try block,
the search for a matching handler continues in a
dynamically surrounding try block.

"9 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
(15.5.1). " ^^^^^^^^^^^^^^^^^^^^^^

Personally, I would really prefer: stack is NOT unwound,
as the standard requirement with a note: PROGRAMMERS, do
your emergency inter-process cleanup in terminate_handlers!

<?>

regards,
alexander.

Alexander Terekhov

unread,
Feb 22, 2002, 7:43:15 AM2/22/02
to

Garry Lancaster wrote:
[...]

> 1. Which unexpected_handler function is called? The
> one in effect immediately after evaluating the
> throw-expression.
>
> 2. When is the unexpected_handler function
> called? In effect, immediately after evaluating the
> throw-expression.
>
> Since (2) contradicts 15.5.2/1 I assume (1) was
> intended.
>
> If we quote the whole of para 18.6.2.4/2 rather
> than just the previously quoted snippet, we get:
>
> "Effects: Calls the unexpected_handler function in
> effect immediately after evaluating the throw-expression
> (18.6.2.2), if called by the implementation, or calls the
> current unexpected_handler, if called by the program."
>
> I think it becomes a little clearer that the para is
> telling us *which* unexpected_handler is called,
> not *when* it is called.

// Unwinding based ES implementation
// =================================

void a() throw( a_bad_exception,... )
^^^
// Does NOT include c_bad_exception, see below
{
try {

unexpected_activator ua( &throw_a_bad_exception );

b();

}
//...
catch(...) { // ES impl. "injected"

call_unexpected_in_effect_immediately_after_throw_expr_eval();
// calls throw_c_bad_exception() -> BOOM/terminate(), OOPS!!!!

}

}

void b()
{

try {

c();

}
//...
catch( const c_bad_exception& ) {

//...
throw something_else;

}

}

void c() throw( c_bad_exception,... )
{
try {

unexpected_activator ua( &throw_c_bad_exception );

//...

throw exception_in_violation_of_a_ES_only_comma_NOT_c();
// "Remember" throw_c_bad_exception unexpected handler

}
//...
catch <...> { // ES impl. "injected"
//... rethrow exception_in_violation_of_a_ES_only_comma_NOT_b_and_c
}
catch(...) { // ES impl. "injected"
//...
}
}

Or is this just my poor brain being cloudy... after that
late night party (with a lot of Vodka) I had *today*? ;-)

Are there any errors in my logic?

Comments/Help to find errors? Thank you in advance.

regards,
alexander.

P.S. Please also consider the following (just in
case C++ will some day become *threads-aware*):

http://www.opengroup.org/onlinepubs/007904975/xrat/xsh_chap02.html#tag_03_02_09

"It was suggested that the cancelation cleanup handlers
should also be called when the process exits or calls
the exec function. This was rejected partly due to the
performance problem caused by having to call the cancelation
cleanup handlers of every thread before the operation could
continue. The other reason was that the only state expected
to be cleaned up by the cancelation cleanup handlers would
be the intraprocess state. Any handlers that are to
clean up the interprocess state would be registered with
atexit ()."

And, BTW, C++ provides a mechanism for *EMERGENCY* cleanup
as well... that's *terminate_handlers*, AFAIK/AFAICT/IMHO!

Garry Lancaster

unread,
Feb 22, 2002, 7:47:48 AM2/22/02
to
> Garry Lancaster wrote:
[snipped explanation of standard ES violation stack
unwinding]

Alexander Terekhov:


> Well, but why would you ever want/need that unwinding
> ("partial", btw) in most likely (throw() ES violation)
> *terminating* program (and things along the lines of
> "just ignored" bad_exception handler(s) aside)?

Since you now accept that the MSVC behaviour is
not in accordance with the C++ standard (correct
me if I'm wrong), you are really making two points:

1. The current ES system should be
changed or removed. I agree with this, although I'm
open to reasoned argument. However, the MSVC
approach is not one that appeals to me.

2. It's OK for vendors to provide a non-standard
ES system. I'm less sure about this. MSVC's
offence is particularly bad since, I think I'm right
in saying, there is no option to reinstate standard
ES behaviour and the docs don't clearly warn you
about the change.

What I would like to see happen:

1. MS to argue the case for their view of ES
semantics in the C++ newsgroups and in the
standardization forums. (And others to give
them a fair hearing - it would be a shame if
people with something to contribute to the
debate felt excluded by anti-MS bias.)

2. Those responsible for the current ES
semantics to argue their case in the same
places.

3. Everyone else with a reasoned viewpoint
to also contribute to the debate.

4. If a consensus is reached on what should
happen, implement it in the C++0x standard.
If not, C++0x should keep ESs as they are for
backwards compatibility, but deprecate them.

4. In the meantime, MSVC to revert to standard
ES semantics for standard ES syntax. It's
absolutely fine if the current MSVC ES semantics
are preserved by the __declspec(nothrow) syntax.
Less ideally, have a compiler switch to move
between the two behaviours for the standard
ES syntax. MSVC is probably not the only
offender: any other compilers who take similar
liberties with ES should also permit standard
ES behaviour, at least as an option.

We can see, on this and other current NG threads,
that the debate has started, although I don't see
everyone in it I'd like to see. There is, as yet,
no sign of a consensus.

Kind regards

Garry Lancaster
Codemill Ltd
Visit our web site at http://www.codemill.net

Peter Koch Larsen

unread,
Feb 22, 2002, 7:52:14 AM2/22/02
to
"Garry Lancaster" <glanc...@ntlworld.com> wrote in message news:<P0vc8.108997$H37.14...@news2-win.server.ntlworld.com>...
[snip]

>
> int main(int argc, char* argv[])
> {
> try
> {
> cout << "Before ES violation.\n";
> foo(); // marked as "throw()" but throws (PKL)

> cout << "After ES violation.\n";
> }
> catch(...)
> {
> cout << "Caught an exception!\n";
> }
> return 0;
> }
>
[code snips]

> VC6 and VC7 (beta 2) both print
>
> Before ES violation.
> ctor.
> Caught an exception!
>
> I believe they *should* print
>
> Before ES violation.
> ctor.
> dtor.

I agree that Microsoft DID get that one wrong, at least with your
build-settings, but shouldn't the output be:

Before ES violation.
ctor.

The destructor should not be called as terminate should not do
stack-unwinding. Am i wrong? If not i can see all kinds of endless
recursion happening in a corrumpted environment.

Regards
Peter

Peter Dimov

unread,
Feb 22, 2002, 10:05:55 AM2/22/02
to
"Garry Lancaster" <glanc...@ntlworld.com> wrote in message
news:<rV8d8.3460$H43.3...@news11-gui.server.ntli.net>...

> Garry Lancaster:
> > > I don't like the current non-standard VC
> > > implementation of ESs. If VC customers want a
> > > special optimization that works in a different way to
> > > the standard ESs, VC already has
> > > __declspec(nothrow), which seems ideal for
> > > switching on the optimization [1]. But please, MS,
> > > fix standard ESs.
>
> Peter Dimov:
> > Agreed. Please, MS, fix standard ESs by proposing a
> > change to the standard.
>
> That's not agreeing, that's being mischievous ;-)

Yes, of course. The point is that we shouldn't fall in the "MS=bad,
std=good" trap. This is an interesting situation where the
non-conformance isn't actually a bad thing in practice, because it
doesn't affect code portability.

> For what it's worth, when given a choice, I don't
> use ESs now

Because of the performance implications?

> and I wouldn't use them if they
> worked the way you (and MS, presumably)
> want them to work.

Because of? Note that this ES flavor has no negative impact on
performance or correctness; it only enables additional optimizations
and diagnostics, including...

> I would use only throw() (it's the most
> useful ES as it signifies the no-throw
> exception safety guarantee) if it were
> statically checked.

... static checking of throw() specifications.

Alexander Terekhov

unread,
Feb 22, 2002, 11:30:04 AM2/22/02
to

Garry Lancaster wrote:
[...]

> Since you now accept that the MSVC behaviour is
> not in accordance with the C++ standard (correct
> me if I'm wrong),

Well, to begin with, I am just UNABLE to infer/deduce
the RATIONALE of C++ standard writers w.r.t rather
contradictory (IMO) ES behavior/definition. The
POSIX-like "Rationale" volume[1] written/reviewed/
"approved" by EVERY C++ standard committee/working
group member (and *free*, if possible, or just 18
bucks ;-)), would be really helpful to me!

Stroustrup's books/website/faq/whatever ARE helpful
but something more "official" would NOT hurt too,
I think.

Not going into details of "useful" ES, as I see
it, I just want to make the following appeal:

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! C++ Gurus, PLEASE start with SERIOUS considerations !
! and design discussions with respect to *THREADS* !
! integration into C++ language. !
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

I believe, that all that "exceptions"/ES/... related
stuff will become CLEARER in the light of THREADS!

Please, DO SOMETHING!

Boost.Threads is a nice try/beginning, but boost.org
is just the WRONG place to do it (c.p.t-like FULLY-OPEN/
*THREADS* dedicated forum with MANY MT-regulars on it,
would look much better to me). Also, <thread> library
header is NOT really the most difficult part of that
"integration" work... Personally, I would be quite
happy with JUST <pthread.h> -> <cthread> transition,
FIRST!

regards,
alexander.

[1] http://www.opengroup.org/publications/mem-online/c950/c950.pdf
http://www.opengroup.org/publications/mem-online/c951/c951.pdf
http://www.opengroup.org/publications/mem-online/c952/c952.pdf
http://www.opengroup.org/publications/mem-online/c953/c953.pdf
http://www.opengroup.org/publications/mem-online/c610/c610.pdf

Registration and free membership to get access:

http://www.opengroup.org/austin

David Abrahams

unread,
Feb 22, 2002, 3:42:58 PM2/22/02
to

"Peter Dimov" <pdi...@mmltd.net> wrote in message
news:7dc3b1ea.02022...@posting.google.com...

> Yes, of course. The point is that we shouldn't fall in the "MS=bad,
> std=good" trap. This is an interesting situation where the
> non-conformance isn't actually a bad thing in practice, because it
> doesn't affect code portability.

Not true, I fear. People have been known to use ESes for exception
translation (e.g. at API boundaries). I'm not saying there aren't better
ways, but people do it.

-Dave

Garry Lancaster

unread,
Feb 23, 2002, 12:39:33 PM2/23/02
to
Alexander Terekhov:

> Well, to begin with, I am just UNABLE to infer/deduce
> the RATIONALE of C++ standard writers w.r.t rather
> contradictory (IMO) ES behavior/definition. The
> POSIX-like "Rationale" volume[1] written/reviewed/
> "approved" by EVERY C++ standard committee/working
> group member (and *free*, if possible, or just 18
> bucks ;-)), would be really helpful to me!
>
> Stroustrup's books/website/faq/whatever ARE helpful
> but something more "official" would NOT hurt too,
> I think.

I agree a rationale would be helpful and my impression
is that members of the standards committee think so
too. So, possibly it will happen with C++0x.

> Not going into details of "useful" ES, as I see
> it, I just want to make the following appeal:
>
> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
> ! C++ Gurus, PLEASE start with SERIOUS considerations !
> ! and design discussions with respect to *THREADS* !
> ! integration into C++ language. !
> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

That's a separate issue from the one
we were discussing, although I am not blind to
the fact that multi-threading affects just about
everything.

I would be very surprised if multi-threading wasn't
addressed in C++0x, but maybe not in the way,
or to the extent, that you desire.

So, don't just sit there; you obviously feel strongly
about the issue and have relevant
expertise so why not contribute to the standardization
process yourself? The idea that only "C++ Gurus"
(whatever that means) have anything to offer sets
the bar a little too high in my opinion.

Kind regards

Garry Lancaster
Codemill Ltd
Visit our web site at http://www.codemill.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Rob

unread,
Feb 23, 2002, 2:10:33 PM2/23/02
to
"P.M." wrote:
>
> The advice that exception specifications may introduce a performance
> penalty (Exceptional C++ p54) seems to be contradicted in one special
> circumstance.
>
> The VC++ documentation has the following paragraph describing a
> nothrow specification throw() [which, btw, is equivalent to
> __declspec(nothrow)]:
>
> "... the compiler can eliminate the mechanics of tracking the lifetime
> of certain unwindable objects in such a function, and significantly
> reduce the code size."
>
> I wonder if this is true of other compilers as well?
>

Not sure if it's true of other compilers or not.

The VC++ approach you describe is actually a double edged sword.
Yes, you are getting a reduction of code size. That comes
at the expense of correctness (eg if the function with a throw()
spec actually does throw, but the compiler assumes it hasn't,
what happens?). If you *know* the function can never throw,
this optimisation gives a net gain. If you're not sure, you
could end up with all sorts of problems .....

Garry Lancaster

unread,
Feb 23, 2002, 3:25:42 PM2/23/02
to
Garry Lancaster:

> > I believe they *should* print
> >
> > Before ES violation.
> > ctor.
> > dtor.

Peter Koch Larsen:


> I agree that Microsoft DID get that one wrong, at least with your
> build-settings, but shouldn't the output be:
>
> Before ES violation.
> ctor.
>
> The destructor should not be called as terminate should not do
> stack-unwinding. Am i wrong?

'Fraid so - the stack is unwound until the ES is
encountered. See the other messages in this thread
and 15.5.2/1 in the standard.

> If not i can see all kinds of endless
> recursion happening in a corrumpted environment.

Could you give an example?

Kind regards

Garry Lancaster
Codemill Ltd
Visit our web site at http://www.codemill.net

Garry Lancaster

unread,
Feb 23, 2002, 3:26:33 PM2/23/02
to
Peter Dimov:

> Note that this ES flavor has no
> negative impact on performance or correctness;

As some of the test programs bandied about
on this thread show, MSVC's ES implementation
can produce observable behaviour that differs
from what the standard dictates. If that isn't
a negative impact on correctness, I don't know
what is.

> it only enables additional optimizations
> and diagnostics, including...

Garry Lancaster:


> > I would use only throw() (it's the most
> > useful ES as it signifies the no-throw
> > exception safety guarantee) if it were
> > statically checked.

> ... static checking of throw() specifications.

MSVC warns about some easy to spot
violations of ESs. It is not unique in this
and its non-standard approach to ES
run-time behaviour gives it no special
advantage in this area. This "best effort
warning" is not what I mean
when I talk about static checking (what I
do mean could be summarized as
"pedantic erroring" ;-)

Kind regards

Garry Lancaster
Codemill Ltd
Visit our web site at http://www.codemill.net

Francis Glassborow

unread,
Feb 23, 2002, 8:21:00 PM2/23/02
to
In article <F6Pd8.22659$hM6.2...@news6-win.server.ntlworld.com>,
Garry Lancaster <glanc...@ntlworld.com> writes

>I agree a rationale would be helpful and my impression
>is that members of the standards committee think so
>too. So, possibly it will happen with C++0x.

I wonder if people realise just how big a turn-over of membership of
WG21 and J16 there has been over the last five years. Only a handful of
people who were responsible for the ES subclauses are still around. And
when I say responsible here, I just mean that they were actually at the
meetings and not necessarily in the work group drawing up that part of
the Standard.


--
Francis Glassborow
Check out the ACCU Spring Conference 2002
4 Days, 4 tracks, 4+ languages, World class speakers
For details see: http://www.accu.org/events/public/accu0204.htm

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Dylan Nicholson

unread,
Feb 24, 2002, 11:36:25 AM2/24/02
to
Alexander Terekhov <tere...@web.de> wrote in message news:<3C765D08...@web.de>...

>
> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
> ! C++ Gurus, PLEASE start with SERIOUS considerations !
> ! and design discussions with respect to *THREADS* !
> ! integration into C++ language. !
> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
>
> I believe, that all that "exceptions"/ES/... related
> stuff will become CLEARER in the light of THREADS!
>
> Please, DO SOMETHING!
>
Can you clarify? Are you suggesting exceptions should be thrown across
threads?? Or that uncaught exceptions should only cause thread
termination and not necessarily program termination (main thread
only)?
I've been programming multi-threaded applications that deal
extensively in exceptions for many years and I've never had cause to
consider the interaction between the two that I can think of.

Dylan

Alexander Terekhov

unread,
Feb 26, 2002, 9:58:48 AM2/26/02
to
dy...@moldflow.com (Dylan Nicholson) wrote in message
news:<156b8737.02022...@posting.google.com>...

> Alexander Terekhov <tere...@web.de> wrote in message
news:<3C765D08...@web.de>...
> >
> > !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
> > ! C++ Gurus, PLEASE start with SERIOUS considerations !
> > ! and design discussions with respect to *THREADS* !
> > ! integration into C++ language. !
> > !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
> >
> > I believe, that all that "exceptions"/ES/... related
> > stuff will become CLEARER in the light of THREADS!
> >
> > Please, DO SOMETHING!
> >
> Can you clarify?

http://groups.google.com/groups?as_umsgid=3C73CB8B.E764D3C6%40web.de

> Are you suggesting exceptions should be thrown across
> threads??

Read on thread cancellation.

> Or that uncaught exceptions should only cause thread
> termination and not necessarily program termination (main thread
> only)?

Nope.

> I've been programming multi-threaded applications that deal
> extensively in exceptions for many years and I've never had cause to
> consider the interaction between the two that I can think of.

Have you ever used pthread_cancel() and/or pthread_exit()... in *C++*?

regards,
alexander.

Hillel Y. Sims

unread,
Feb 26, 2002, 10:15:48 AM2/26/02
to

"Dylan Nicholson" <dy...@moldflow.com> wrote in message
news:156b8737.02022...@posting.google.com...

> Alexander Terekhov <tere...@web.de> wrote in message
news:<3C765D08...@web.de>...
> >
> > !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
> > ! C++ Gurus, PLEASE start with SERIOUS considerations !
> > ! and design discussions with respect to *THREADS* !
> > ! integration into C++ language. !
> > !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
> >
> > I believe, that all that "exceptions"/ES/... related
> > stuff will become CLEARER in the light of THREADS!
> >
> > Please, DO SOMETHING!
> >
> Can you clarify? Are you suggesting exceptions should be thrown across
> threads?? Or that uncaught exceptions should only cause thread
> termination and not necessarily program termination (main thread
> only)?
> I've been programming multi-threaded applications that deal
> extensively in exceptions for many years and I've never had cause to
> consider the interaction between the two that I can think of.
>

I believe I understand what Alexander is talking about. Neither of these two
issues is particularly contentious, they are handled fairly
straightforwardly. The interactions under concern are between
thread-cancellation mechanisms and exception-handling / exception-specs. On
some systems, thread-cancellation is propagated to the thread via a thrown
exception of a special type. This is useful as it permits automatic stack
unwinding to properly clean up any resources (on systems where a signal is
raised instead, there may not be any way to cleanup resources without a lot
of excess measures). But it must be possible to temporarily disable
thread-cancellation at certain times (esp. dtors and swaps) in order to
provide safe/correct nothrow code. try {...} catch (...) {} is not
sufficient / correct, as it will intercept and prevent thread-cancellation,
/unless/ thread-cancellation is disabled! POSIX (at least) provides
well-defined semantics for temporarily disabling thread-cancellation state,
such that a simple guard object (call it CancelGuard) can be used to protect
critical regions. I created this for our system at work:

class CancelGuard { // warning: experimental
public:
CancelGuard()
{ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &m_origState); }
~CancelGuard()
{ pthread_setcancelstate(m_origState, &m_origState); }
private:
int m_origState;

// no copy
CancelGuard(const CancelGuard&);
CancelGuard& operator=(const CancelGuard&);
};
#define CANCEL_GUARD CancelGuard CG__ ## __LINE __

This needs to be applied manually in all nothrow code that may invoke any of
a large number of cancellation-point system functions (likely any
non-trivial code really):

T::~T()
{
CANCEL_GUARD;
try {
db.close();
}
catch (...) {}
}

It might be a useful feature of ES if it could be used to direct the
compiler to do the cancel-guard automatically (and possible selectively, if
throw() code is trivial)?

T::~T() throw()
{
// implicit CANCEL_GUARD via compiler
try {
db.close();
}
catch (...) {}
}

(or maybe the compiler should simply do it automatically for all dtors even
w/o throw()?)

Few people seem to be aware of this issue (or at least discuss it much
publicly), but many of us program in a multi-threaded world. For example,
following the typical exception-safety conventions recommended by
Meyers/Sutter/etc. for protecting nothrow code via try {...} catch (...) {}
as necessary is not completely valid when thread-cancellation exceptions are
used; it is inappropriate to catch and swallow a thread-cancellation
exception. Thread-cancellation must be disabled in these regions in order
for the catch (...) {} to be correct. (Actually, it should probably even be
catch (std::exception&) {} because you don't really want to catch any
unknown exceptions such as accvio...) This makes it difficult to write
portable code, since it cannot be correct without CancelGuard on
multi-threaded systems. It would definitely be nice if the whole issue could
be more smoothly integrated into the language (or at least common practice)
going forward..

Hillel Y. Sims
hsims AT factset.com

Dylan Nicholson

unread,
Feb 27, 2002, 7:26:38 AM2/27/02
to
tere...@web.de (Alexander Terekhov) wrote in message news:<c29b5e33.0202...@posting.google.com>...

> dy...@moldflow.com (Dylan Nicholson) wrote in message
> news:<156b8737.02022...@posting.google.com>...
>
> > I've been programming multi-threaded applications that deal
> > extensively in exceptions for many years and I've never had cause to
> > consider the interaction between the two that I can think of.
>
> Have you ever used pthread_cancel() and/or pthread_exit()... in *C++*?
>
Ah...nope. I was vaguely following the discussion on boost for a
while.
I've always implemented threads as loops checking for a termination
condition, so I've never had any need for pthread-style cancelling.
And Win32 only has TerminateThread which is pretty nasty (although
it's essentially what happens if your main threads stops without
cleaning shutting down any secondary threads).
But OK I accept that exceptions are probably the best way to deal with
thread cancellation, and perhaps should even have native compiler
support (how this would be done under Win32 I'm at a loss with), but I
don't see the connection with ES.

Dylan

Peter Koch Larsen

unread,
Feb 27, 2002, 4:51:56 PM2/27/02
to
"Garry Lancaster" <glanc...@ntlworld.com> wrote in message
news:<AcPd8.22727$hM6.2...@news6-win.server.ntlworld.com>...

> Garry Lancaster:
> > > I believe they *should* print
> > >
> > > Before ES violation.
> > > ctor.
> > > dtor.
>
> Peter Koch Larsen:
> > I agree that Microsoft DID get that one wrong, at least with your
> > build-settings, but shouldn't the output be:
> >
> > Before ES violation.
> > ctor.
> >
> > The destructor should not be called as terminate should not do
> > stack-unwinding. Am i wrong?
>
> 'Fraid so - the stack is unwound until the ES is
> encountered. See the other messages in this thread
> and 15.5.2/1 in the standard.
>
> > If not i can see all kinds of endless
> > recursion happening in a corrumpted environment.
>
> Could you give an example?

Hi Garry

First of all, let me confess that i did not study the C++ standard
carefully before joining this thread. Making statements about a
subject of which i have only inferior knowledge seems to a habit i
have (in common with not so few politicians ;-)). Also i do not yet
have a copy of the standard: my company really ought to spend those
USD 18,- on that important issue. I do however have a draft, and i
will use that in the following discussion. Correct me if my draft is
wrong in this area.

Let us look at some code (with the same semantics as the previous
post, but slightly shorter).

struct foo
{
void f() throw() { throw 0;}
~foo() { p = 0;} // just do something
};

int main()
{
try
{
p = 1;
foo dummy;
dummy.f();
p = 2;
}
catch (...)
{
p = 0;
}
return 0;
}


First i want to argue, that my original argument still holds:

My draft says:

If a function with an exception-specification throws an exception that
is not listed in the exception-specification, the function
void unexpected();
is called (18.6.2) immediately after completing the stack unwinding
for the former function

So foo.f()'s stack will be unwinded after the throw(); main()'s stack
will not as unexpected() calls terminate() which calls abort() which
will not unwind anything. Thus the foo desctructor will not be called.
As an aside, i can't imagine any way of actually unwinding the stack
in the above scenario - even if i install my own very clever handlers.
The compiler can take advantage of the exception-specification on
f.foo() by (1) replacing the main above with the following code:

int main()
{
p = 1;
foo dummy;
dummy.f();
p = 2;
},

by (2) not creating any "exception-unwind code" for main() and by (3)
marking main() itself as throw() (not of much value here, but if main
was a standard function, this could be used for further
optimizations).

In my first post, i argued that this is the way it should be. Whenever
a function with an empty exception-specification throws, something
"seriously" did happen, and we can not depend on program-state
anymore. This case is fundamentally different from the situation,
where a function with a nonempty exception throws an exception, that
is not included in its specification: this situation IS recoverable by
letting unexpected throw one of the "expected" exceptions.
On further reflection, i now believe that i in my original post
specifically had destructors in mind. I thought then that destructors
had an implicit throw() specification. Having read the draft standard,
i now realize that unfortunately this seems not to be the case. Maybe
this was an idea for C++0x? I think i will delegate that task to a new
post.
I also stated that not behaving as it apparently seems to do could
cause all kinds of havoc. The scenarios i (vaguely, it seems) had in
mind, was that e.g. some containers could forever try to destroy its
elements without being able to make progress, or the same container
could have a stray pointer that could point to some random place
because of some weird exception suddenly takes place (one example:
asynchroneous pthread exceptions or a mapped windows exception such as
a page- or stackfault). I still believe that such faults might cause
an endless recursion but i do also strongly believe that such
exceptions have no place in C++. Thus everything should be okay so
long as exceptions are wellformed but do have a look at the new thread
i intend to start up.

Regards
Peter

PS: I wrote this while i did not have access to this newsgroup. I just
reread your post and believe we actually agree. But this implies that
the output will NOT include the dtor output.

Peter Koch Larsen

unread,
Feb 27, 2002, 4:52:14 PM2/27/02
to
"Garry Lancaster" <glanc...@ntlworld.com> wrote in message
news:<AcPd8.22727$hM6.2...@news6-win.server.ntlworld.com>...

> Garry Lancaster:
> > > I believe they *should* print
> > >
> > > Before ES violation.
> > > ctor.
> > > dtor.
>
> Peter Koch Larsen:
> > I agree that Microsoft DID get that one wrong, at least with your
> > build-settings, but shouldn't the output be:
> >
> > Before ES violation.
> > ctor.
> >
> > The destructor should not be called as terminate should not do
> > stack-unwinding. Am i wrong?
>
> 'Fraid so - the stack is unwound until the ES is
> encountered. See the other messages in this thread
> and 15.5.2/1 in the standard.
>
> > If not i can see all kinds of endless
> > recursion happening in a corrumpted environment.
>
> Could you give an example?

Hi Garry

My draft says:

Regards
Peter

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Hyman Rosen

unread,
Feb 27, 2002, 4:52:32 PM2/27/02
to
Dylan Nicholson wrote:
> But OK I accept that exceptions are probably
> the best way to deal with thread cancellation

I would say that it's the worst way, not the best
way. The entire exception mechanism of C++ is based
on the fact that exceptions are explicitly raised
by code, and are never asynchronously introduced
from the outside. Given all that has been said about
how to write exception-correct code, there is zero
chance that the C++ standards committee would ever
allow thread cancellation to cause an exception.

C/C++ already has a mechanism for asynchronous
notification, namely signal handlers.

Peter Koch Larsen

unread,
Feb 27, 2002, 4:52:50 PM2/27/02
to
"Hillel Y. Sims" <use...@XXXXXphatbassetXXXXX.com> wrote in message
news:<25je8.31973$GL6.4...@news02.optonline.net>...
[snipped Alexander and a commenter]

>
> I believe I understand what Alexander is talking about. Neither of these two
> issues is particularly contentious, they are handled fairly
> straightforwardly. The interactions under concern are between
> thread-cancellation mechanisms and exception-handling / exception-specs. On
> some systems, thread-cancellation is propagated to the thread via a thrown
> exception of a special type. This is useful as it permits automatic stack
> unwinding to properly clean up any resources (on systems where a signal is
> raised instead, there may not be any way to cleanup resources without a lot
> of excess measures). But it must be possible to temporarily disable
> thread-cancellation at certain times (esp. dtors and swaps) in order to
> provide safe/correct nothrow code. try {...} catch (...) {} is not
> sufficient / correct, as it will intercept and prevent thread-cancellation,
> /unless/ thread-cancellation is disabled! POSIX (at least) provides
> well-defined semantics for temporarily disabling thread-cancellation state,
> such that a simple guard object (call it CancelGuard) can be used to protect
> critical regions. I created this for our system at work:
[snipped useful code and lots of other interesting stuff]
Hi Hillel

I must admit that i hate the idea of exceptions popping up from
nowhere. Not only do they invalidate dtors; they also makes a lot of
code which runs under a throw() assumption invalid. Here i do
specifically think of ordinary instructions such as
pointer-manipulations. Would you like to write a std::list
implementation if you could knew that even every assembly-level
instruction could be interrupted. I guess most methods would have to
be guarded and this would not be pretty or efficient (even if the code
could be optimized away in a singlethreaded system).

I would suggest an alternative proposal: an accept-guard-approach
which positively indicated, that you would be prepared to get an
asynchroneous exception. During an acceptguard, you should not be able
to execute any function with a throw() specification.
The acceptguard could be implemented as an exception-specification. If
part of the language (which i do not think it will be anytime soon,
but perhaps a step could be taken?), appearance could be somewhat like
this:

class C
{
...
// foo will accept any asynchroneous exception
void foo() throw(std::async_exception) {...}
}


i have personally used polling to decide if my threads should
terminate: it might be clumsy, but if handled correctly this gives a
stable, low-overhead solution.

Kind regards
Peter

Hillel Y. Sims

unread,
Feb 28, 2002, 7:49:20 AM2/28/02
to
"Hyman Rosen" <hyr...@mail.com> wrote in message
news:3C7D08E...@mail.com...

> Dylan Nicholson wrote:
> > But OK I accept that exceptions are probably
> > the best way to deal with thread cancellation
>
> I would say that it's the worst way, not the best
> way. The entire exception mechanism of C++ is based
> on the fact that exceptions are explicitly raised
> by code, and are never asynchronously introduced
> from the outside. Given all that has been said about
> how to write exception-correct code, there is zero
> chance that the C++ standards committee would ever
> allow thread cancellation to cause an exception.

Thread-cancellation is NOT asynchronous (under pthreads).

(Ok, well it can be, but you have to manually set a special state to do
that, and pretty much everyone will tell you never to do that except under
very limited circumstances where cleanup is not a concern).

By default, cancellation notification is delivered in a synchronous manner
(either via signal or exception on whichever platform), and only under
certain well-defined circumstances -- specific function calls -- /very/
similar to the concept of "function f() may throw exception x" actually.
This seems to fit very closely with the intent and behavior of the standard
C++ exception mechanism. As long as you ensure that the thread-cancel
exception is not thrown from dtors or nothrow code, I see no downside and
lots of upside -- you get automatic stack cleanup, as with any other
exception. I am 99.9% certain at this point the CancelGuard approach is a
completely workable and correct approach to managing thread-cancellation
exceptions w/r/t dtors & nothrow code in C++ (the only downside being that
you have to manually apply it in a lot of places). It would definitely be
useful if this can be better integrated into the language (and we are living
in a multithreaded world, and I am a multithreaded boy...)

>
> C/C++ already has a mechanism for asynchronous
> notification, namely signal handlers.
>

C++ has no language-defined mechanism for asynchronous notification, as far
as I understand; it's all implementation-specific. Asynchronous signal
handlers are the more common alternative method for handling notifications
(though even here, thread cancellation signal is still delivered
synchronously at the above-mentioned cancellation-point functions) -- but
then how do you clean up your stack-based objects from a signal handler?

Hillel Y. Sims
hsims AT factset.com

Alexander Terekhov

unread,
Feb 28, 2002, 7:57:23 AM2/28/02
to

Peter Koch Larsen wrote:
[...]

> PS: I wrote this while i did not have access to this newsgroup. I just
> reread your post and believe we actually agree. But this implies that
> the output will NOT include the dtor output.

Nope. The example was:

void foo() throw()
{
test t;
throw 1;
}

The standard does require *stack-unwinding* from
throw point way up to that silly catch(...)-based
"detection" of ES-violation (remembering unexpected/
terminate handlers active at throw point, see C++
IA64 ABI[1], for example).

Unless someone could explain to me the RATIONALE
behind this design/decision, I will continue to
believe STRONGLY that the standard is SERIOUSLY
BROKEN and it does NOT really make sense to me
at all.

regards,
alexander.

[1] http://www.codesourcery.com/cxx-abi/abi-eh.html

"The fields unexpectedHandler and terminateHandler
contain pointers to the unexpected and terminate
handlers at the point where the exception is thrown.
The ISO C++ Final Draft International Standard
[lib.unexpected] (18.6.2.4) states that the handlers
to be used are those active immediately after
evaluating the throw argument. If destructors
change the active handlers during unwinding,
the new values are not used until unwinding
is complete."

Alexander Terekhov

unread,
Feb 28, 2002, 7:58:45 AM2/28/02
to
Peter Koch Larsen wrote:
[...]
> I must admit that i hate the idea of exceptions popping up from
> nowhere. Not only do they invalidate dtors; they also makes a lot of
> code which runs under a throw() assumption invalid.

In general, DESTRUCTION/CLEANUP of something is
NOT *async-cancel-safe* operation and therefore
should NOT be invoked in async-cancel-regions.

http://www.opengroup.org/onlinepubs/007904975/xrat/xsh_chap02.html#tag_03_02_09_25

"Async-Cancel Safety

A function is said to be async-cancel-safe if it is written
in such a way that entering the function with asynchronous
cancelability enabled will not cause any invariants to be
violated, even if a cancelation request is delivered at any
arbitrary instruction. Functions that are async-cancel-safe
are often written in such a way that they need to acquire no
resources for their operation and the visible variables that
they may write are strictly limited.

Any routine that gets a resource as a side effect cannot be
made async-cancel-safe (for example, malloc()). If such a
routine were called with asynchronous cancelability enabled,
it might acquire the resource successfully, but as it was
returning to the client, it could act on a cancelation request.
In such a case, the application would have no way of knowing
whether the resource was acquired or not.

Indeed, because many interesting routines cannot be made
async-cancel-safe, most library routines in general are
not async-cancel-safe. Every library routine should specify
whether or not it is async-cancel safe so that programmers
know which routines can be called from code that is
asynchronously cancelable."

Personally, I think that threads-aware C++0x SHOULD
add support for Async-Cancel Safety similar to its
current "const-safety" (btw, const with mutable (or
throwing something) stuff most likely/usually is NOT
async-cancel-safe, unless it explicitly sets/restores
cancelability state).

[...]


> I would suggest an alternative proposal: an accept-guard-approach
> which positively indicated, that you would be prepared to get an
> asynchroneous exception. During an acceptguard, you should not be able
> to execute any function with a throw() specification.
> The acceptguard could be implemented as an exception-specification. If
> part of the language (which i do not think it will be anytime soon,
> but perhaps a step could be taken?), appearance could be somewhat like
> this:
>
> class C
> {
> ...
> // foo will accept any asynchroneous exception
> void foo() throw(std::async_exception) {...}
> }
>
> i have personally used polling to decide if my threads should
> terminate: it might be clumsy, but if handled correctly this gives a
> stable, low-overhead solution.

w.r.t "polling", click here:

http://groups.google.com/groups?as_umsgid=c29b5e33.0202271136.23b99325%40posting.google.com

And, again, I do think that something along the
lines of bool "future_std::expected_excpetion<T>()"
WOULD/COULD solve all these problems in a MUCH
better (performance-/usability-wise) way.

regards,
alexander.

Alexander Terekhov

unread,
Feb 28, 2002, 8:02:12 AM2/28/02
to

"Hillel Y. Sims" wrote:
[...]

> Few people seem to be aware of this issue (or at least discuss it much
> publicly), but many of us program in a multi-threaded world. For example,
> following the typical exception-safety conventions recommended by
> Meyers/Sutter/etc. for protecting nothrow code via try {...} catch (...) {}
> as necessary is not completely valid when thread-cancellation exceptions are
> used; it is inappropriate to catch and swallow a thread-cancellation
> exception. Thread-cancellation must be disabled in these regions in order
> for the catch (...) {} to be correct. (Actually, it should probably even be
> catch (std::exception&) {} because you don't really want to catch any
> unknown exceptions such as accvio...)

You don't really want to catch&ignore any of these:

logic_error
domain_error
invalid_argument
length_error
out_of_range
runtime_error
range_error
overflow_error
underflow_error

*known* exceptions TOO (and anything else) IF they
were thrown *UNEXPECTEDLY*. Gee! Why not just terminate
the whole process, leaving that nice dump-info for the
friendly service/change team and try to recover/retry
in a NEW/CLEAN address space from the persistent (non-/
less-volatile storage) program-state info saved (e.g.
incrementally updateable objects store, home grown) at
the last GOOD (as it appeared and most likely *truly*
good) CHECK POINT!? Just consider/treat it as the
unexpected power-off/loss situation. "Good" programs
SHOULD deal with this too, IMHO! ;-)

> This makes it difficult to write
> portable code, since it cannot be correct without CancelGuard on
> multi-threaded systems. It would definitely be nice if the whole issue could
> be more smoothly integrated into the language

Yup. Something along the lines of:

- async-cancel-safety enforced by the compiler;
- bool expected_exception<T>();
- bool unwinding(T*);
- something to code LOCAL context templates/objects
that would have the same local access/visibility
as Java's "finally" clauses do;
- thread-safe static locals for IMMUTABLE lazy
singeltons;
- transition of <pthread.h> into <cthread>;
- some decent <thread> C++ "wrappers" that would
basically do the same as <cthread> stuff, BUT
in MORE EXPRESSIVE/LESS ERROR-PRONE (get rid
of "detach", etc) way then the current C-style
threaded programming.

...and, of course, post-/pre- c/d-tors! ;-)

regards,
alexander.

Francis Glassborow

unread,
Feb 28, 2002, 12:49:06 PM2/28/02
to
In article <3C7DE902...@web.de>, Alexander Terekhov
<tere...@web.de> writes

>Unless someone could explain to me the RATIONALE
>behind this design/decision, I will continue to
>believe STRONGLY that the standard is SERIOUSLY
>BROKEN and it does NOT really make sense to me
>at all.


I do wish you would stop shouting in your posts, it adds nothing to the
content and just irritates readers.

An empty throw spec. is just an ES and is treated the same way as all
others (we try to avoid special casing things). While it is true that
the unexpected_handler cannot do anything to allow the process to
continue because it cannot replace the exception with another, in
general (other than for empty throw specs) this is possible and the
rules (reasonably IMO) state that such a catch is effectively handling
the original exception.

The design may not be ideal but to describe it as emphatically seriously
broken is a serious over-statement.


--
Francis Glassborow
Check out the ACCU Spring Conference 2002
4 Days, 4 tracks, 4+ languages, World class speakers
For details see: http://www.accu.org/events/public/accu0204.htm

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Alexander Terekhov

unread,
Feb 28, 2002, 3:29:57 PM2/28/02
to

Hyman Rosen wrote:
>
> Dylan Nicholson wrote:
> > But OK I accept that exceptions are probably
> > the best way to deal with thread cancellation
>
> I would say that it's the worst way, not the best
> way. The entire exception mechanism of C++ is based
> on the fact that exceptions are explicitly raised
> by code, and are never asynchronously introduced
> from the outside. Given all that has been said about
> how to write exception-correct code, there is zero
> chance that the C++ standards committee would ever
> allow thread cancellation to cause an exception.

Hopefully, "the C++ standards committee" people
(prior to making that "allow"/"forbid" decision)
will do extensive research/study of *existing*
practice for exceptions/cancel/signals/threads/etc..
*and* their interactions. Perhaps this URL could
serve/help as the STARTING point (and the 2nd
one below is quite interesting/relevant too):

http://www.opengroup.org/onlinepubs/007904975/xrat/xsh_chap02.html#tag_03_02_0
9_25
("Thread Cancelation Overview")

> C/C++ already has a mechanism for asynchronous
> notification, namely signal handlers.

Well, click here:

http://www.tru64unix.compaq.com/docs/base_doc/DOCUMENTATION/V51A_PDF/ARH9MBTE.
PDF
(*respectable* calling standard with "nonlocal GOTO in
a multilanguage environment" ;-) exceptions/signals, etc.)

Also, here is my FAVORITE c.p.t URL (given quite
frequent use of it in my various c.p.t articles):

http://sources.redhat.com/pthreads-win32
(see cancel.c)

And, finally (highly recommended reading):

http://groups.google.com/groups?as_q=exception%20butenhof&as_ugroup=comp.progr
amming.threads

regards,
alexander.

Alexander Terekhov

unread,
Feb 28, 2002, 8:33:18 PM2/28/02
to

Francis Glassborow wrote:
[...]

> An empty throw spec. is just an ES and is treated the same way as all
> others (we try to avoid special casing things). While it is true that
> the unexpected_handler cannot do anything to allow the process to
> continue because it cannot replace the exception with another, in
> general (other than for empty throw specs) this is possible and the
> rules (reasonably IMO) state that such a catch is effectively handling
> the original exception.

What "such catch"? I am talking about stack-unwinding
PRIOR to unexpected()/terminate() invocation and "problems"
(as it seems to me) resulting due to this behavior, for
example:

http://groups.google.com/groups?as_umsgid=3C7633AF.4A08EFBF%40web.de

> The design may not be ideal but to describe it as emphatically seriously
> broken is a serious over-statement.

Yeah, e.g. if/when you just can't debug/analyze problems
(abends) once you ship your programs to customer (without
full recreation data/..., if its possible/deterministic
at all/enough), that's just "may not be ideal", and of
course, "broken is a serious over-statement", right! ;-)

regards,
alexander.

Bob Bell

unread,
Feb 28, 2002, 8:37:52 PM2/28/02
to
"Hillel Y. Sims" <use...@XXXXXphatbassetXXXXX.com> wrote in message news:<25je8.31973$GL6.4...@news02.optonline.net>...
> class CancelGuard { // warning: experimental
> public:
> CancelGuard()
> { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &m_origState); }
> ~CancelGuard()
> { pthread_setcancelstate(m_origState, &m_origState); }
> private:
> int m_origState;
>
> // no copy
> CancelGuard(const CancelGuard&);
> CancelGuard& operator=(const CancelGuard&);
> };
> #define CANCEL_GUARD CancelGuard CG__ ## __LINE __
>
> This needs to be applied manually in all nothrow code that may invoke any of
> a large number of cancellation-point system functions (likely any
> non-trivial code really):
>
> T::~T()
> {
> CANCEL_GUARD;
> try {
> db.close();
> }
> catch (...) {}
> }

I believe this is not correct. Suppose we have two classes:

class A {
public:
~A();
};

class B {
public:
~B();
private:
A mA;
};

The destructors are both guarded with CANCEL_GUARD:

A::~A()
{
CANCEL_GUARD;
// etc.
}

B::~B()
{
CANCEL_GUARD;
// etc.
}

What happens when a B is destroyed? The destructor is entered, the
CancelGuard object is constructed, which disables the asynchronous
cancel state, then the rest of the code is executed. Then the body of
the destructor concludes and the CancelGuard is destroyed, which
reenables the asynchronous cancel state.

_Then_ the destructor for the A member is called, which again disables
the asynchronous cancel state. But there is a brief moment of time,
after running B::~B() but before A::~A() where the asynchronous cancel
state is enabled, and a cancellation can occur.

Putting a CancelGuard in a destructor therefore provides an illusion
of safety which is not present. The only way to be truly safe is to
wrap the entire destruction with a CancelGuard:

void F()
{
B* p = new B();

// etc.

{
CANCEL_GUARD;
delete p;
}
}

This is, obviously, tedious and error-prone.

Of course, if C++0x supports threads, it can automate CancelGuard-like
behavior, perhaps wherever there is an empty-throw spec? Or if there
is a standard thread cancel exception, wherever the thread cancel
exception is missing from a throw spec. (Functions without throw specs
would, of course, be able to throw thread cancel exceptions.)

Personally, however, I find the idea of asynchronously throwing
exceptions in this way a pretty bad one; it seems like it complicates
the runtime model quite a bit. Where can these exceptions pop up?

If they can pop up at any time, that seems to invite situations where
operations that are partly complete can be interrupted, leaving the
system in an inconsistent state.

If they can pop up at only particular times (say at sequence points)
that seems to imply a lot of overhead, as the code must somehow be
aware of when it can and can't throw a thread cancel exception.

Bob Bell

Bob Bell

unread,
Feb 28, 2002, 8:39:12 PM2/28/02
to
"Hillel Y. Sims" <use...@XXXXXphatbassetXXXXX.com> wrote in message news:<YBhf8.5420$Iu2.2...@news02.optonline.net>...

> Thread-cancellation is NOT asynchronous (under pthreads).
>
> (Ok, well it can be, but you have to manually set a special state to do
> that, and pretty much everyone will tell you never to do that except under
> very limited circumstances where cleanup is not a concern).
>
> By default, cancellation notification is delivered in a synchronous manner
> (either via signal or exception on whichever platform), and only under
> certain well-defined circumstances -- specific function calls -- /very/
> similar to the concept of "function f() may throw exception x" actually.
> This seems to fit very closely with the intent and behavior of the standard
> C++ exception mechanism. As long as you ensure that the thread-cancel
> exception is not thrown from dtors or nothrow code, I see no downside and
> lots of upside -- you get automatic stack cleanup, as with any other
> exception. I am 99.9% certain at this point the CancelGuard approach is a
> completely workable and correct approach to managing thread-cancellation
> exceptions w/r/t dtors & nothrow code in C++ (the only downside being that
> you have to manually apply it in a lot of places). It would definitely be
> useful if this can be better integrated into the language (and we are living
> in a multithreaded world, and I am a multithreaded boy...)

I'm afraid I just don't get this. If thread A tries to cancel thread
B, how can it be synchronous if it can happen at any time? B can be in
any part of statement in any function when this occurs; having an
exception magically appear at any point during B's execution sounds
asynchronous to me.

The only way A can cancel B in a synchronous manner is if there is
some communication mechanism between the threads, such as B waiting on
a semaphore; when the semaphore gets signaled, a "thread canceled"
exception can be thrown. This implies that there is some agreed-upon
communication protocol between A and B, and it further implies that
there are some functions which will throw thread canceled exceptions
(because these functions participate in the communication protocol
that makes it possible) and some functions that will never throw a
thread canceled exception (because those functions don't participate
in the communication protocol).

Or am I missing something?

Bob Bell
living in a multithreaded world, but not quite a multithreaded boy

Dylan Nicholson

unread,
Mar 1, 2002, 8:28:43 AM3/1/02
to
Hyman Rosen <hyr...@mail.com> wrote in message
news:<3C7D08E...@mail.com>...
> Dylan Nicholson wrote:
> > But OK I accept that exceptions are probably
> > the best way to deal with thread cancellation
>
> I would say that it's the worst way, not the best
> way. The entire exception mechanism of C++ is based
> on the fact that exceptions are explicitly raised
> by code, and are never asynchronously introduced
> from the outside. Given all that has been said about
> how to write exception-correct code, there is zero
> chance that the C++ standards committee would ever
> allow thread cancellation to cause an exception.
>
Well all the proposals so far deal with synchronous cancellation,
basically meaning that there are certain points at which cancellation
exceptions can be thrown. They certainly can't occur half way through
a series of assembly instructions that might be busy pushing arguments
on to the stack (or whatever).

> C/C++ already has a mechanism for asynchronous
> notification, namely signal handlers.
>

Which is an interesting point - is it safe to throw an exception from
a signal handler (*)? Thread-cancellation is essentially no different
from application "cancellation" (hitting Ctrl-C or whatever). It
would be nice to be able to handle this with exceptions too.

Dylan

(*) You certainly can't from the Ctrl+C handler under NT
(SetConsoleCtrlHandler), as NT actually automatically spawns another
thread for you to handle this event!
I tested it with signal(SIGINT, ...) on an SGI and throwing just cause
an immediate abort. Is this mentioned in the standard?

Alexander Terekhov

unread,
Mar 1, 2002, 11:23:44 AM3/1/02
to

Bob Bell wrote:
[...]

> I'm afraid I just don't get this. If thread A tries to cancel thread
> B, how can it be synchronous if it can happen at any time? B can be in
> any part of statement in any function when this occurs; having an
> exception magically appear at any point during B's execution sounds
> asynchronous to me.
>
> The only way A can cancel B in a synchronous manner is if there is
> some communication mechanism between the threads, such as B waiting on
> a semaphore; when the semaphore gets signaled, a "thread canceled"
> exception can be thrown. This implies that there is some agreed-upon
> communication protocol between A and B, and it further implies that
> there are some functions which will throw thread canceled exceptions
> (because these functions participate in the communication protocol
> that makes it possible) and some functions that will never throw a
> thread canceled exception (because those functions don't participate
> in the communication protocol).
>
> Or am I missing something?

Yes, at least the concept of async-cancel-regions/
async-cancel-SAFETY:

http://www.opengroup.org/onlinepubs/007904975/xrat/xsh_chap02.html#tag_03_02_09_25


("Thread Cancelation Overview")

And, BTW, even *JAVA* folks seem to start "getting it":

http://groups.google.com/groups?as_umsgid=c29b5e33.0202271136.23b99325%40posting.google.com

Well, Y'know, I am just giving up and bowing out... because
it seems that *NOTHING* has "changed" in *5+* long years
(and only hell knows how many Net centuries) in the mind
of the "recognized leaders" who appear here quite often! :-(

http://groups.google.com/groups?as_umsgid=32B66F3B.2F1C%40zko.dec.com

---

From: Dave Butenhof (bute...@zko.dec.com)
Subject: Re: c++ trouble...
^^^^^^^^^^^
Newsgroups: comp.programming.threads
View: Complete Thread (8 articles) | Original Format
Date: 1996/12/17
^^^^^^^^^^

Otto Lind wrote:
> > The standard specifically requires that you treat these names as macros
> > expanding so that the push opens a block and the pop closes that block.
> > They MUST always be paired, at the same lexical scope.
>
> Just curious, Is the standard a "C POSIX" standard? Is there anything in
> it to address other language specific issues?

That is exactly the point. POSIX.1 is, specifically and exclusively, a
"C Language" binding. There is also a POSIX.5 series, which is "Ada
Language" bindings, and a POSIX.9 series, which is "FORTRAN Language"
bindings. There are no "C++ Language" bindings to POSIX, nor has there
been any serious proposal of which I'm aware -- certainly none have
gotten so far as a PAR (essentially a POSIX working group charter). Why?
Most likely because C++ is "close enough" to C that POSIX.1 works, even
though a native C++ binding would be easier to use and more flexible.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Using POSIX.1 cleanup handlers in C++ is absurd. Cancellation should be
made to invoke C++ object destructors, at an absolute minimum, instead.
Optionally, one might want to be able to use catch to finalize a Cancel
exception -- allowing the program to recover and continue. (While, in
most cases, a program should always reraise a cancel so the thread can
terminate, there are a few legitimate needs for a true exception-based
implementation.) But ANSI C doesn't have exceptions, or destructors, so
POSIX.1 invented cleanup handlers instead. And, because there's no
requirement (other than logic and reason) that a system's C++ compiler
and POSIX.1c implementation be integrated in any sane way, many C++
programmers (and anyone who wants to write fully portable code)
is stuck with the lowest common denominator.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

---

regards,
alexander.

Hillel Y. Sims

unread,
Mar 1, 2002, 12:58:46 PM3/1/02
to

"Bob Bell" <bel...@pacbell.net> wrote in message
news:c87c1cfb.02022...@posting.google.com...

(pthread) cancellation states exist on a 2x2 matrix:

enabled vs synchronous
disabled asynchronous

The default mode is enabled & synchronous. Synchronous meaning cancellation
is only a request made by the calling thread that can remain pending
possibly indefinitely, and actual exception or signal can only be raised in
the target thread at certain specified function call points. Asynchronous
mode is used very infrequently, because it really is unsafe in 99% of
circumstances. CancelGuard only affects enabled/disabled status of
cancellation, and does not change synch/asynch states. Thus, because there
is no cancellation point between the finish of B::~B() and the start of
A::~A(), there is no race condition in the example above (unless
asynchronous cancellation is specifically active, which is highly unlikely
and indeed would be fairly unsafe here).

>
> Putting a CancelGuard in a destructor therefore provides an illusion
> of safety which is not present. The only way to be truly safe is to
> wrap the entire destruction with a CancelGuard:
>
> void F()
> {
> B* p = new B();
>
> // etc.
>
> {
> CANCEL_GUARD;
> delete p;
> }
> }
>
> This is, obviously, tedious and error-prone.

Unnecessary, given synchronous use of thread-cancellation as per above
explanation.

>
> Personally, however, I find the idea of asynchronously throwing
> exceptions in this way a pretty bad one; it seems like it complicates
> the runtime model quite a bit. Where can these exceptions pop up?
>
> If they can pop up at any time, that seems to invite situations where
> operations that are partly complete can be interrupted, leaving the
> system in an inconsistent state.
>

Correct, that's why asynchronous mode is infrequently used (we don't even
use it anywhere on our system).

> If they can pop up at only particular times (say at sequence points)
> that seems to imply a lot of overhead, as the code must somehow be
> aware of when it can and can't throw a thread cancel exception.

*shrug* It seems to work fairly well for pthreads. I haven't been following
the boost.threads work closely lately, I'm sure they will find a good
solution for these issues too if they haven't already.

Hillel Y. Sims
hsims AT factset.com

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Bob Bell

unread,
Mar 1, 2002, 8:35:13 PM3/1/02
to
"Hillel Y. Sims" <use...@XXXXXphatbassetXXXXX.com> wrote in message
news:<iROf8.19687$Iu2.7...@news02.optonline.net>...

> (pthread) cancellation states exist on a 2x2 matrix:
>
> enabled vs synchronous
> disabled asynchronous
>
> The default mode is enabled & synchronous. Synchronous meaning cancellation
> is only a request made by the calling thread that can remain pending
> possibly indefinitely, and actual exception or signal can only be raised in
> the target thread at certain specified function call points. Asynchronous
> mode is used very infrequently, because it really is unsafe in 99% of
> circumstances. CancelGuard only affects enabled/disabled status of
> cancellation, and does not change synch/asynch states. Thus, because there
> is no cancellation point between the finish of B::~B() and the start of
> A::~A(), there is no race condition in the example above (unless
> asynchronous cancellation is specifically active, which is highly unlikely
> and indeed would be fairly unsafe here).

I understand better. I suppose the only problem would then come up if
there was some way to allow cancellation after executing B::~B() and
A::~A(). The only thing I can think of to cause this is another member
object before the A whose destructor:

a) doesn't include CANCEL_GUARD, and;
b) includes one of those specific function call points that allows
cancelation.

In this case, failing to include the CANCEL_GUARD would have to be
considered a bug.

And again, if the compiler/runtime support threads, the compiler can
guarantee that cancellation is disabled (as if it automatically put a
CANCEL_GUARD in every destructor for you).

> > If they can pop up at only particular times (say at sequence points)
> > that seems to imply a lot of overhead, as the code must somehow be
> > aware of when it can and can't throw a thread cancel exception.
>
> *shrug* It seems to work fairly well for pthreads. I haven't been following
> the boost.threads work closely lately, I'm sure they will find a good
> solution for these issues too if they haven't already.

I guess when it's built in to the OS, it can probably be done
efficiently. I assume that's what's going on with pthreads. What is
more of a concern is what happens on an OS that doesn't support this
kind of functionality, and the C++ thread implementation must emulate
it. pthreads isn't everywhere.

Bob

Bob Bell

unread,
Mar 2, 2002, 4:26:08 PM3/2/02
to
bel...@pacbell.net (Bob Bell) wrote in message news:<c87c1cfb.02030...@posting.google.com>...

> I understand better. I suppose the only problem would then come up if
> there was some way to allow cancellation after executing B::~B() and
> A::~A().

Whoops! This was supposed to read "after executing B::~B() and
_before_ A::~A()."

0 new messages