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

C++ and threads

46 views
Skip to first unread message

Alexander Terekhov

unread,
Feb 26, 2002, 1:42:53 PM2/26/02
to
It did not appear on c.l.c++mod (and I did not
receive moderation queue notification), so I'll
try c.std.c++.

-------- Original Message --------
Message-ID: <3C7A1401...@web.de>
Date: Mon, 25 Feb 2002 11:37:53 +0100
From: Alexander Terekhov <tere...@web.de>
Newsgroups: comp.lang.c++.moderated
Subject: C++ and threads (was: Re: throw() can be good for optimization)
References:
<6db3f637.02021...@posting.google.com><yRK5DeEWnjc8Ew$1...@robinton.ntlworld.com><OOKc8.69369$YA2.8...@news11-gui.server.ntli.net><g7gdpUE9...@robinton.ntlworld.com><9ZQc8.71299$YA2.9...@news11-gui.server.ntli.net><3C7420A7...@web.de><vV3d8.1239$H43.1...@news11-gui.server.ntli.net><3C75280D...@web.de><T1qd8.8301$H43.9...@news11-gui.server.ntli.net><3C765D08...@web.de>
<F6Pd8.22659$hM6.2...@news6-win.server.ntlworld.com>


Garry Lancaster wrote:
[...]
> > 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.

Well, I guess that's not all that complicated/large-scale.
Other than thread(and async-cancel!)-safety throughout
the whole standard library, there are just a few areas
that need to be addressed, I think:

- integrate thread-cancel/exit with C++ exception model;
- static locals (lazy init);
- "static" thread locals (something along the lines of
__declspec(thread));
- word-tearing/false-sharing.

> 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.

Why not? What's the problem with adopting the
MOST portable/reasonable/easy low level threading
model that exists out there -- *Pthreads*!? Also,
consider:

- MS have made their threading "standard" already:

Directory of G:\C#\tr-084\2001tc39-020b\System\Threading

10/02/2001 12:44p 546,816 Interlocked.doc
11/14/2001 05:38p 40,519 Interlocked.pdf
10/02/2001 12:44p 568,832 Monitor.doc
11/14/2001 05:36p 56,873 Monitor.pdf
10/02/2001 12:44p 30,208 SynchronizationLockException.doc
11/14/2001 05:36p 20,093 SynchronizationLockException.pdf
10/02/2001 12:44p 686,080 Thread.doc
11/14/2001 05:36p 138,010 Thread.pdf
10/02/2001 12:45p 26,624 ThreadAbortException.doc
11/14/2001 05:36p 17,664 ThreadAbortException.pdf
10/02/2001 12:44p 26,624 ThreadPriority.doc
11/14/2001 05:36p 18,577 ThreadPriority.pdf
10/02/2001 12:45p 21,504 ThreadStart.doc
11/14/2001 05:37p 13,391 ThreadStart.pdf
10/02/2001 12:44p 34,816 ThreadState.doc
11/14/2001 05:37p 23,926 ThreadState.pdf
10/02/2001 12:45p 32,256 ThreadStateException.doc
11/14/2001 05:37p 21,215 ThreadStateException.pdf
10/02/2001 12:45p 22,016 Timeout.doc
11/14/2001 05:37p 14,015 Timeout.pdf
10/02/2001 12:45p 546,816 Timer.doc
11/14/2001 05:37p 41,480 Timer.pdf
10/02/2001 12:45p 23,552 TimerCallback.doc
11/14/2001 05:37p 14,747 TimerCallback.pdf
10/02/2001 12:43p 530,944 WaitHandle.doc
11/14/2001 05:35p 30,514 WaitHandle.pdf
26 File(s) 3,548,112 bytes

more info:

http://www.cs.umd.edu/~pugh/java/memoryModel/archive/0938.html

Just think of their "Managed Extensions for C++ Specification":


http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmxspec/html/vcManagedExtensionsSpec_Start.asp
("This document describes the Managed Extensions for the
Visual C++ programming language. These extensions encompass
a set of features that will support the common type system
and the common language runtime. The Managed Extensions are
compatible with the C++ ISO Standard."
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Gee! Gee! Gee! Gee! Gee! Gee! Gee!)

- Java is slowly gravitating "back" towards Pthreads:
(well, with less "undefined" behaviors, though ;-))

http://gee.cs.oswego.edu/dl/concurrency-interest/aims.html

> So, don't just sit there; you obviously feel strongly
> about the issue and have relevant
> expertise

Thanks for the compliment. I must confess, however,
that all of my knowledge/expertise is rather limited
by a couple of years of horror nightmares at sleep when
I was programming win32/Java threads only and a nice
relaxation since a year or so ago when I've learned
PTHREADS from the standard documents and Butenhof's PWPT
(*TWO* volumes ;-)) - the second volume is free and is
available on Compaq web site. It is called "Tru64 UNIX/
Guide to the POSIX Threads Library" ;-)

> so why not contribute to the standardization
> process yourself?

Do you mean that I should write a complete proposal
and post it to comp.std.c++? Sorry, I can't do it.

> The idea that only "C++ Gurus"
> (whatever that means) have anything to offer sets
> the bar a little too high in my opinion.

My friend dictionary says ("Guru"):

"A revered teacher or mentor. A recognized
leader: The guru of high finance. [ From
Sanskrit guru-, venerable]"

As for my part, I am always ready to
provide some feedback...quibbles! ;-)

Also, there is some sketchy stuff from
me available here:

http://groups.google.com/groups?as_umsgid=c29b5e33.0202121053.273318c0%40posting.google.com
http://groups.google.com/groups?as_umsgid=c29b5e33.0202140422.203f1141%40posting.google.com

regards,
alexander.

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]

Ian Collins

unread,
Feb 28, 2002, 8:00:55 PM2/28/02
to
Alexander Terekhov wrote:
<snip>

>
> Garry Lancaster wrote:
> [...]
> > > 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.
>
> Well, I guess that's not all that complicated/large-scale.
> Other than thread(and async-cancel!)-safety throughout
> the whole standard library, there are just a few areas
> that need to be addressed, I think:
>
> - integrate thread-cancel/exit with C++ exception model;

How? Who would catch the exception? If a cancellation request resulted
in an exception, the thread could just ignore it.

Maybe some input form the Java or ADA community would help to identify
the pros and cons of built in threads.

Ian

<snip>

Ian Collins
Masuma Ltd,
Christchurch
New Zealand.

Garry Lancaster

unread,
Mar 1, 2002, 10:46:43 AM3/1/02
to
Alexander Terekhov:

> > > 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. !
> > > !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Garry Lancaster:


> > 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.

Alexander Terekhov:


> Well, I guess that's not all that complicated/large-scale.
> Other than thread(and async-cancel!)-safety throughout
> the whole standard library,

I reckon that's a big job.

> there are just a few areas
> that need to be addressed, I think:
>
> - integrate thread-cancel/exit with C++ exception model;

If I understand you correctly, you are proposing
that a thread could inject an exception into
another thread.

I don't like this. At the moment in C++ we can
tell where exceptions are thrown. Async exceptions
have a particularly unpleasant effect on no-throw
code i.e. it isn't no-throw anymore

I believe some C++ implementations already
use asynchrounous exceptions as an extension.
How do they deal with the problems? I guess
they have some kind of mechanism for
switching async exceptions off and on, in which
case I hope it's off by default.

My impression, from a great book on Java
threading I was reading recently, is that Java
initially supported thread termination using
async exceptions, but is moving away from
that as it was just too weird.

> - static locals (lazy init);
> - "static" thread locals (something along the lines of
> __declspec(thread));
> - word-tearing/false-sharing.

All good points.

What about synchronisation primitives? boost::thread
is a good start but it's my impression that integrating
mutexes into the core language a la Java could provide
some benefits. I'm thinking particularly that the code
re-ordering rules could be made sync block aware.

> > 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.
>
> Why not?

For a start because people rarely agree 100% on the
subject.

> What's the problem with adopting the
> MOST portable/reasonable/easy low level threading
> model that exists out there -- *Pthreads*!?

I believe boost::thread is more portable. It uses
pthreads underneath for some platforms and
Win32 threading underneath on, ahem, Win32.

I don't think the non-OO nature of the raw pthreads
interface sits comfortably with the rest of the
standard lib. It needs wrapping at the very least.

> Also, consider:
>
> - MS have made their threading "standard" already:

[snip stuff on .net threading types]

As you know, they have a number of threading "standards",
and that one requires the .net runtime. It's not a reason
to exclude MS implementations from a C++ threading
standard. A C++ threading standard needs to be general
enough that it can wrap each of the most common
threading APIs/object models. Did I mention
boost::thread? ;-)

> > So, don't just sit there; you obviously feel strongly
> > about the issue and have relevant
> > expertise
>
> Thanks for the compliment. I must confess, however,
> that all of my knowledge/expertise is rather limited
> by a couple of years of horror nightmares at sleep when
> I was programming win32/Java threads only and a nice
> relaxation since a year or so ago when I've learned
> PTHREADS from the standard documents and Butenhof's PWPT
> (*TWO* volumes ;-)) - the second volume is free and is
> available on Compaq web site. It is called "Tru64 UNIX/
> Guide to the POSIX Threads Library" ;-)

That sounds like a pretty good grounding in
threads to me, but it's difficult to tell purely
on the basis of how long someone has been
doing something and how much they have
read. Some people are naturals, some just
never get it. The rest of us sit in the middle.
There is always more to learn for anyone.

> > so why not contribute to the standardization
> > process yourself?
>
> Do you mean that I should write a complete proposal
> and post it to comp.std.c++? Sorry, I can't do it.

There are many other ways to contribute. For
example, the ISO mailing lists. Yes, there are
mailing lists separate to comp.std.c++ where
standaridization issues are discussed! The
culture is a little different to the NGs - usually
the standard of debate is higher and the
approach less adversarial (and I'd like it to stay
that way). Sometimes real work is done.

> > The idea that only "C++ Gurus"
> > (whatever that means) have anything to offer sets
> > the bar a little too high in my opinion.
>
> My friend dictionary says ("Guru"):
>
> "A revered teacher or mentor. A recognized
> leader: The guru of high finance. [ From
> Sanskrit guru-, venerable]"

The problem with the idea that everyone
on the standards committee is a guru is
that it encourages an "us and them" view
of things. In the worse case, this leads to
trenchant criticism of aspects of the language
standard emerging *after* standardization,
in forums such as this, rather than *before*
standardization in the ISO forums.

Anyone who spots a problem with the next
C++ standard after it is released will in a
sense be partly to blame for that situation.
They had the chance to spot it beforehand
but they didn't take it. (Not that assigning
blame is the aim - the aim is to make C++0x
as good as possible. First time.)

> As for my part, I am always ready to
> provide some feedback...quibbles! ;-)

And that would be helpful, provided they
are accurate quibbles. But you won't
have access to all the relevant discussions
unless you join in the standardization effort.

If anyone is interested, they should find out
who their ISO contact is for their country of
residence and ask them what it's all about.

Interesting stuff although I find your writing
style very condensed at times, so I don't
always follow. On this group you can assume
a certain fairly high level of C++ knowledge as
a minimum. But the level of multi-threaded
expertise is much wider. If you give more
explanations of your points, more people will
be able to follow you.

Kind regards

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

Alexander Terekhov

unread,
Mar 1, 2002, 1:34:09 PM3/1/02
to

Ian Collins wrote:
[...]

> > Well, I guess that's not all that complicated/large-scale.
> > Other than thread(and async-cancel!)-safety throughout
> > the whole standard library, there are just a few areas
> > that need to be addressed, I think:
> >
> > - integrate thread-cancel/exit with C++ exception model;
>
> How? Who would catch the exception?

Implementation; just like pthread_cancel_e exception; see:

http://www.tru64unix.compaq.com/docs/base_doc/DOCUMENTATION/V51_PDF/ARH9RBTE.PDF

and/or the second link below (I mean *source code*
and source code comments).

> If a cancellation request resulted
> in an exception, the thread could just ignore it.

Right, finalization of cancel-exceptions in the user
code IS in the uncharted territory, AFAIK/AFAICT.
(Java's "interrupt"-model aside, see below)

One "problem"/question is the interactions w.r.t
cancel state and type (thread cancelability) and
"eating"/finalization handler. It is my understanding
that thread's cancelability is DISABLED once the
cancel/unwind process gets under way - cancel-exception
gets thrown/raised (and since it could be initiated
from some async-cancel-region, the type could just
remain ASYNCHRONOUS, suppressed by DISABLE type...
AND/OR even something else; see:

http://sources.redhat.com/pthreads-win32

for example).

On the other hand, you are really (most likely)
want/need to get things back as usual, in-order:

state: ENABLED
type: DEFERRED
============== ================
cancelability: ENABLED/DEFERRED

ON THE EXIT from that finalize/catch-and-just-eat'em
cancel-exception handler, IMHO.

I've asked Butenhof on c.p.t -- no answer yet:

http://groups.google.com/groups?as_umsgid=c29b5e33.0202150920.4c9f991a%40posting.google.com

Also, Butenhof "once" mentioned "exception-aware"
"UNIX 98 ABI for IA-64" draft document:

http://groups.google.com/groups?as_umsgid=kZ7h7.623%24bB1.30401%40news.cpqcorp.net
http://groups.google.com/groups?as_umsgid=3AF00403.C9498C92%40compaq.com

I am now trying to get access/find that "dead"
document... perhaps someone could help.

I guess that it will point to the similar
ideas/concepts that are explained in:

http://groups.google.com/groups?as_umsgid=c29b5e33.0202271136.23b99325%40posting.google.com
(see "nonlocal GOTO in a multilanguage environment" item)

> Maybe some input form the Java or ADA community would help to identify
> the pros and cons of built in threads.

Don't know about ADA, but the URL above does point
to some Java stuff (sort of pros and cons). ;-)

regards,
alexander.

Alexander Terekhov

unread,
Mar 1, 2002, 3:40:34 PM3/1/02
to

Garry Lancaster wrote:
[...]

> I believe some C++ implementations already
> use asynchrounous exceptions as an extension.
> How do they deal with the problems? I guess
> they have some kind of mechanism for
> switching async exceptions off and on, in which
> case I hope it's off by default.

Yep.

> My impression, from a great book on Java
> threading I was reading recently, is that Java
> initially supported thread termination using
> async exceptions, but is moving away from
> that as it was just too weird.

Well, I assume you mean the deprecated
stop/ThreadDeath exception. The whole
concept was indeed broken due to the lack
of *async-cancel-safety*-Pthreads like
thing. However, RTJ spec/extension attempts
to fix/revive it... see the link in my other
post here.

> > - static locals (lazy init);
> > - "static" thread locals (something along the lines of
> > __declspec(thread));
> > - word-tearing/false-sharing.
>
> All good points.
>
> What about synchronisation primitives?

You don't really need anything other than
<cthread>/<pthread.h> locks, barriers and
condvars! ;-)

> boost::thread is a good start

boost::thread is just a thin object layer
on top of Pthreads, nothing else! And
it lacks (currently) A LOT; the whole
concept of thread-/mutex-/condvars-
attributes (runtime polymorphism) have
yet to be integrated into boost.threads,
for example.

boost.threads efforts should transform
into <thread> header... but that does NOT
mean that <cthread> header should not become
a part of future thread-aware C++ too, I think!

> but it's my impression that integrating
> mutexes into the core language a la Java could provide
> some benefits.

AFAICT that's just syntactical sugar...
also consider that JSR-166 intends to provide
Pthread-like locks with explicit methods
(and Pthread-like attributes too)!

> I'm thinking particularly that the code
> re-ordering rules could be made sync block aware.

I never understood that magic. Java's memory model
just mimics Pthreads with some "extensions" (volatiles,
etc) which makes it EXTREMELY complicated, well
to me, at least. And, BTW, both need post-/pre-
c-/d-tors! ;-) ;-) (well, post-ctors only for
poor Java which lacks d-tors/RAI ;-)).

> > > 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.
> >
> > Why not?
>
> For a start because people rarely agree 100% on the
> subject.

The subject (Pthreads) is for the long time around us,
and working just fine for many years already... simply
"emulating" C++ exceptions/cleanup in C using thread_cleanup
handlers! ;-)

> > What's the problem with adopting the
> > MOST portable/reasonable/easy low level threading
> > model that exists out there -- *Pthreads*!?
>
> I believe boost::thread is more portable.

I don't think so. It is just a POOR re-invention
(I would even call/label it as "FORK") of things/
projects like pthread-win32 with a thin object layer
added on top of it. I do not want to go into details,
but for example, think of Apple/MAC and their
"commercial"/official Pthreads impl once it become
fully ready.

> It uses
> pthreads underneath for some platforms and
> Win32 threading underneath on, ahem, Win32.

Win32 has Pthreads too (with more than just
one maintainer/main-contributor, BTW).

> I don't think the non-OO nature of the raw pthreads
> interface sits comfortably with the rest of the
> standard lib. It needs wrapping at the very least.

<cthread> is non-OO (fully compatible with <pthread.h>);

<thread> is OO, less error prone (gets rid of detach,
provides scoped locking, etc) implemented in terms of
<cthread> -- providing the same effects.

What's the problem?

regards,
alexander.

Garry Lancaster

unread,
Mar 2, 2002, 12:08:47 PM3/2/02
to
> Garry Lancaster wrote:
> > My impression, from a great book on Java
> > threading I was reading recently, is that Java
> > initially supported thread termination using
> > async exceptions, but is moving away from
> > that as it was just too weird.

Alexander Terekhov:


> Well, I assume you mean the deprecated
> stop/ThreadDeath exception. The whole
> concept was indeed broken due to the lack
> of *async-cancel-safety*-Pthreads like
> thing. However, RTJ spec/extension attempts
> to fix/revive it... see the link in my other
> post here.

Yeah, I think that's what I meant. (Have mislaid
book though so can't be sure ;-)

Are asynchronous exceptions useful enough
to counteract the problems they pose for
the standardization team (having to retrofit the
concept of async-cancel-safety everywhere - in
fact all library vendors would be under pressure
to do this) and novices? (Yes, I know that people
shouldn't use things they don't understand, but
in reality they do, they do...)

In one of your links (I'm sorry I forget which one)
someone wrote something like "they aren't
useful 99% of the time". If that's a fair assessment
I would say they weren't worth adding to C++.

[snip]

> > boost::thread is a good start
>
> boost::thread is just a thin object layer
> on top of Pthreads, nothing else!

Well, that's not quite true, as you admit yourself
later on, as it wraps Win32 primitives as well.

[snip]

> > but it's my impression that integrating
> > mutexes into the core language a la Java could provide
> > some benefits.
>
> AFAICT that's just syntactical sugar...
> also consider that JSR-166 intends to provide
> Pthread-like locks with explicit methods
> (and Pthread-like attributes too)!

OK. If we are going to take pthreads as the basis
for a C++ threading standard, I guess we
should fiddle with it as little as possible lest
we break it.

> > I'm thinking particularly that the code
> > re-ordering rules could be made sync block aware.
>
> I never understood that magic. Java's memory model
> just mimics Pthreads with some "extensions" (volatiles,
> etc) which makes it EXTREMELY complicated, well
> to me, at least. And, BTW, both need post-/pre-
> c-/d-tors! ;-) ;-) (well, post-ctors only for
> poor Java which lacks d-tors/RAI ;-)).

Why? And once you do that why stop there? People
will start asking for pre-pre-ctors and post-post-dtors.

> > > > 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.
> > >
> > > Why not?
> >
> > For a start because people rarely agree 100% on the
> > subject.
>
> The subject (Pthreads) is for the long time around us,
> and working just fine for many years already... simply
> "emulating" C++ exceptions/cleanup in C using thread_cleanup
> handlers! ;-)

The subject isn't Pthreads, the subject is C++
threading. You may wish them to be one and
the same, but they're not. At least not yet.

> > > What's the problem with adopting the
> > > MOST portable/reasonable/easy low level threading
> > > model that exists out there -- *Pthreads*!?
> >
> > I believe boost::thread is more portable.
>
> I don't think so. It is just a POOR re-invention
> (I would even call/label it as "FORK") of things/
> projects like pthread-win32 with a thin object layer
> added on top of it.

I don't think shouting that it is poor is constructive.
For a start you will gratuitously offend the people
who spent their own time writing it (for no monetary
gain) if they happen to read your post. It is OK to
prefer pthreads though and it is OK to say why.

> I do not want to go into details,
> but for example, think of Apple/MAC and their
> "commercial"/official Pthreads impl once it become
> fully ready.

I'm afraid I'm not familiar with this. Could you
explain what you mean?

> > It uses
> > pthreads underneath for some platforms and
> > Win32 threading underneath on, ahem, Win32.
>
> Win32 has Pthreads too (with more than just
> one maintainer/main-contributor, BTW).

This is a project governed by the LGPL which means
it would be freely available to compiler and std lib
vendors to add to their products. It could fly, although
I notice it is currently not a complete implementation
of pthreads.

[snip]

Kind regards

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

---

John Nagle

unread,
Mar 2, 2002, 2:28:42 PM3/2/02
to
Alexander Terekhov wrote:

> You don't really need anything other than <cthread>/<pthread.h>
> locks, barriers and condvars! ;-)

As a code generation issue, there's a sizable performance gain to
implementing locking as an inline primitive. On many hardware
platforms, locking can be implemented without a system call, yet many
libraries use a system call on every lock/unlock action. This is often
because specific machine instructions are required; you can't write "do
this as an atomic operation" in C or C++.

The view of locking in C++ as expensive has distorted system
architectures.

John Nagle
Animats

Alexander Terekhov

unread,
Mar 3, 2002, 4:09:40 AM3/3/02
to

Garry Lancaster wrote:
[...]

> I don't like this. At the moment in C++ we can
> tell where exceptions are thrown. Async exceptions
> have a particularly unpleasant effect on no-throw
> code i.e. it isn't no-throw anymore
>
> I believe some C++ implementations already
> use asynchrounous exceptions as an extension.
> How do they deal with the problems? I guess
> they have some kind of mechanism for
> switching async exceptions off and on, in which
> case I hope it's off by default.

Apropos async-exceptions/cancel, perhaps the
following "reject: [off-topic]"/"fail to the
the relevance to C++ anymore" post from
c.l.c++.mods IS *on-topic* here, in THIS
"C++ and threads" discussion... Gee!

---
Message-Id: <2002030122...@netlab.cs.rpi.edu>
From: c++-r...@netlab.cs.rpi.edu
To: tere...@web.de
Cc: c++-r...@netlab.cs.rpi.edu
Subject: reject: [off-topic]


Message-ID: <3C7FE9D6...@web.de>
Newsgroups: comp.lang.c++.moderated
From: Alexander Terekhov <tere...@web.de>
Subject: Re: throw() can be good for optimization

{I fail to the the relevance to C++ anymore. At the same time, the
article does not have a "Followup-To:" header set which would redirect
it to some more appropriate forum. Thus, this article is rejected from
comp.lang.c++.moderated. -mod}

"Hillel Y. Sims" wrote:
[...]
> 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.

Yup, however:

http://groups.google.com/groups?as_umsgid=3C2CC94A.503FB8E2%40web.de

"...To me, why bother with
pthread_testcancel() injecting it at some
point(s) inside some long computation loop
that does not invoke any async-cancel-unsafe
operations? Why not just have cancel type set
to PTHREAD_CANCEL_ASYNCHRONOUS inside such
async-cancel-safe region?!"

http://groups.google.com/groups?as_umsgid=3C3073B3.A34EC44D%40web.de

"...Well, why not just treat the whole async-cancel-region
as a single (but rather fatty ;) deferred cancellation
point? I do not see any additional situations here (except
an obvious need for async-cancel-safety to form a region).
If you need some cleanup to release something and/or
restore/fix shared objects invariants just do it; set up
your cleanup prior to setting PTHREAD_CANCEL_ASYNCHRONOUS
cancel type on async-cancel-region entry and dismiss
your cleanup (optionally invoking it) at some point after
you have set cancel type back to PTHREAD_CANCEL_DEFFERED
on async-cancel-region exit."

http://groups.google.com/groups?as_umsgid=3B72FCBE.5F7358FF%40web.de

"you should use asynch. cancellation only in the functions/code
fragments which do not need any cleanup and do not call any
asynch.cancel-unsafe functions (such as _push/_pop) with
asynch.cancellation enabled. however, you could simply
disable asynch. cancellation, acquire resources, setup
cleanup handler(s) and enable asynch. cancellation again;
do some asynch. cancelable operations, disable asynch.
cancellation, release resources/pop handlers and
enable asynch. cancellation again,..."

regards,
alexander.

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


======================================= MODERATOR'S COMMENT:
This article is even less relavent to C++, since it does not mention standardization at all... watch followups!

John Nagle

unread,
Mar 3, 2002, 6:29:15 PM3/3/02
to
It's quite possible to make asynchronous
exceptions work, at least on most current processors.
Any CPU that can back out a page fault can do clean
exception handling. This was done in Ada years ago.

The key idea is that for each address in the
executable program, there must be a unique recovery
action. Given that, all that's needed is a table
of address ranges vs. recovery points. On an
interrupt, the recovery point table is searched for
the appropriate address, and control is then
transferred to the recovery handler for that point.

This is similar to the mechanism used now
to allow code with intermixed declaration and "throw"
statements. It's just more fine-grained.

There are other obvious implementations, involving
periodic checks in the code, but they're slower.

The ability to defer asynchronous exceptions
during critical sections is useful if this is
to work.

John Nagle
Animats

Alexander Terekhov wrote:

> Garry Lancaster wrote:
> [...]
>
>>I don't like this. At the moment in C++ we can
>>tell where exceptions are thrown. Async exceptions
>>have a particularly unpleasant effect on no-throw
>>code i.e. it isn't no-throw anymore
>>
>>I believe some C++ implementations already
>>use asynchrounous exceptions as an extension.
>>How do they deal with the problems?

---

Alexander Terekhov

unread,
Mar 4, 2002, 10:29:19 AM3/4/02
to

John Nagle wrote:
>
> Alexander Terekhov wrote:
>
> > You don't really need anything other than <cthread>/<pthread.h>
> > locks, barriers and condvars! ;-)
>
> As a code generation issue, there's a sizable performance gain to
> implementing locking as an inline primitive. On many hardware
> platforms, locking can be implemented without a system call,

Library call (pthread.h/whatever) != "system call" and could
easily be implemented as "inline primitive", with some "system
call" providing handling of CONTENTION case only, AFAIK.

regards,
alexander.

Garry Lancaster

unread,
Mar 4, 2002, 10:30:22 AM3/4/02
to
John Nagle:

> It's quite possible to make asynchronous
> exceptions work, at least on most current processors.
> Any CPU that can back out a page fault can do clean
> exception handling. This was done in Ada years ago.

I'm not doubting that it's possible, at least on some
processors. More whether it is worth the trouble.

The problems of async exceptions are that

1. To benefit, the programmer must write at least some
async-cancel-safe code. In other words code that
can be interrupted at any point and still be able
to maintain the exception safety guarantee it offers.
(This will be either the strong or basic exception
safety guarantee; no-throw doesn't make sense
here.) That's more work for the programmer,
including, but not limited to, standard library
writers.

2. It is very easy to get this wrong. (Of course the
same argument could be made, indeed has been
made, about synchronous exception safety. In
both cases it is a legitimate demerit of the
approach.) As Allen I. Holub memorably phrased
it, in a different context, you have "enough rope to
shoot yourself in the foot".

3. The language must be altered to support
async-exceptions. That's more work for compiler
writers and the standardisation team.

Even without async exceptions, it is still possible
to cancel a thread. The benefit of async exceptions
is supposed to be that the code dealing with thread
cancellation is cleaner and more responsive.
I'm dubious as to how often, and to what extent,
this benefit is realised. Let me explain...

I believe I'm correct in saying that there are some
code sequences that can never be made async
cancel safe. Even if I'm wrong here, you can bet
that there are some where the efficiency cost of
async cancel safety is so high that it will not be
implemented.

My supposition is that most code you want to
cancel asynchronously will contain a number of
these non-async-cancel-safe blocks, necessitating
a switch to deferred handling at the beginning
of the block and a switch back to true async
handling after the block. These blocks may
or may not be function calls.

The more of this switching you have going on,
the less benefit you are deriving from the
theoretical cleanness and responsiveness of
the async exception cancellation system.

My contention is that the residual benefit is
insufficient to cancel out the demerits I listed
and that, therefore, there is insufficient reason
to adopt async exceptions in C++.

Kind regards

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

---

Alexander Terekhov

unread,
Mar 4, 2002, 10:30:30 AM3/4/02
to

Garry Lancaster wrote:
[...]

> Are asynchronous exceptions useful enough
> to counteract the problems they pose for
> the standardization team (having to retrofit the
> concept of async-cancel-safety everywhere - in
> fact all library vendors would be under pressure
> to do this) and novices? (Yes, I know that people
> shouldn't use things they don't understand, but
> in reality they do, they do...)
>
> In one of your links (I'm sorry I forget which one)
> someone wrote something like "they aren't
> useful 99% of the time". If that's a fair assessment
> I would say they weren't worth adding to C++.

A few days ago I've posted here the rejected
c.l.c++.mod article which I think does address
this "they aren't useful 99% of the time"/"NOT
worth C++ standardization" point.

[...]


> > > boost::thread is a good start
> >
> > boost::thread is just a thin object layer
> > on top of Pthreads, nothing else!
>
> Well, that's not quite true, as you admit yourself
> later on, as it wraps Win32 primitives as well.

Well, what else should it "wrap"... on win32? ;-)

Technical aspects/functionality (current) of
"wrapping" aside, boost.thread just ignores
zillions lines of PORTABLE *C++* Pthread-ed
code (cancellation aside). <cthread> could
and SHOULD address this issue (including
{async}cancel/C++, this time!), I think.
<thread> should basically do the same to
<cthread> as <iostream>/<fstream> did to
<cstdio>:

"The header <iostream> declares objects that
associate objects with the standard C streams
provided for by the functions declared in
<cstdio> (27.8.2)."

"basic_filebuf<charT,traits>* open(
const char* s,ios_base::openmode mode );

2 Effects: If is_open() != false, returns a null pointer. Otherwise,
initializes the filebuf as required. It then opens a file,
if possible, whose name is the NTBS s
("as if" by calling std::fopen( s, modstr)).
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"

"Table 92-File open modes
...
ios_base Flag combination stdio equivalent
^^^^^^^^^^^^^^^^"

etc... I hope you see the point, I just
do not want to repeat that "better way
of coding Pthreads" stuff again (also,
see "e-mail" item below).

[...]


> > And, BTW, both need post-/pre-
> > c-/d-tors! ;-) ;-) (well, post-ctors only for
> > poor Java which lacks d-tors/RAI ;-)).
>
> Why? And once you do that why stop there? People
> will start asking for pre-pre-ctors and post-post-dtors.

I have yet to see a C++ (or Java) class with sort
of init() AND init2(), init3()... methods. Perhaps
we just have a different understanding of what
post-/pre- c-/d-tors should do/what "problems"
should they address...

[...]


> > > I believe boost::thread is more portable.
> >
> > I don't think so. It is just a POOR re-invention
> > (I would even call/label it as "FORK") of things/
> > projects like pthread-win32 with a thin object layer
> > added on top of it.
>
> I don't think shouting that it is poor is constructive.

Boldface formatting "just" adds *emphasis". If you
consider it as "shouting" generally/altogether (or as
just silly annoyance preventing/disturbing "matured"
readers from reading by outline) that's your choice.
But your comment/reaction does indicate that you did
NOT miss the point... ;-) so I'm satisfied. ;-) ;-)

> For a start you will gratuitously offend the people
> who spent their own time writing it (for no monetary
> gain) if they happen to read your post.

a) I spent quite a lot of my own time on boost.org
and even contributed (for no monetary gain ;-))
some of the stuff used in the current boost.thread.

b) well, AFAIK, I am already on the "kill" list
of W.Kempf! ;-)

> It is OK to
> prefer pthreads though and it is OK to say why.

[...]


> > I do not want to go into details,
> > but for example, think of Apple/MAC and their
> > "commercial"/official Pthreads impl once it become
> > fully ready.
>
> I'm afraid I'm not familiar with this. Could you
> explain what you mean?

I mean roughly this:

< from an e-mail I've sent to W.Kempf some time ago >

"> > > Are you saying that pthread_* calls implemented
> > > on top of some other proprietary platform specific
> > > primitives/interfaces are NOT "native"? For example,
> > > is SUN's *Solaris proprietary LWP interfaces* more
> > > native than SUN's Solaris pthread implementation?
> > > What is the principle difference with respect to
> > > MS-WIN proprietary API and some win32 pthread
> > > implemenation?
> >
> > "Native" simply means the API used by the Boost.Threads
> > implementation.
>
> Personally, I strongly believe that you are going
> to make a HUGE mistake here. It is just absolutely
> pointless to even consider zillions of proprietary
> threading API's available out there. Instead,
> you should just concentrate on THE STANDARD
> called PTHREADS making Boost.Threads just a
> better way to code PTHREADS - just like C++ file
> streams is a better way to code standard C file I/O.
> One simple reason is that you could NEVER beat
> professional/supported PTHREAD impls - boost::condition,
> for example, has a chance close to NULL of beating
> pthreads-win32/pthreads-mac(from Apple)/etc PTHREAD
> condition vars! Consider that pthreads-win32 is likely
> to provide a real pthread_mutex_timedlock() function
> (in its next "snapshot"), which will work with all
> PTHREAD mutex types with a speed of MS critical section.
> Can you do this? Even if yes, what is the purpose of
> reinventing the bicycle?!"

And, BTW, the freshly new pthreads-win32 snapshot IS
out now. See http://sources.redhat.com/pthreads-win32
for details...

> > > It uses
> > > pthreads underneath for some platforms and
> > > Win32 threading underneath on, ahem, Win32.
> >
> > Win32 has Pthreads too (with more than just
> > one maintainer/main-contributor, BTW).
>
> This is a project governed by the LGPL which means
> it would be freely available to compiler and std lib
> vendors to add to their products. It could fly, although
> I notice it is currently not a complete implementation
> of pthreads.

Only Microsoft could make it "complete", I mean
FULL implementation of IEEE Std 1003.1-2001.
Also, consider (see "COPYING" in CVS):

"Although pthreads-win32 makes it possible for applications
that use POSIX threads to be ported to Win32 platforms, the
broader goal of the project is to encourage the use of open
standards, and in particular, to make it just a little easier
for developers writing Win32 applications to consider
widening the potential market for their products"

regards,
alexander.

Hyman Rosen

unread,
Mar 4, 2002, 12:16:44 PM3/4/02
to
John Nagle wrote:
> It's quite possible to make asynchronous
> exceptions work, at least on most current processors.
> Any CPU that can back out a page fault can do clean
> exception handling. This was done in Ada years ago.

<http://groups.google.com/groups?hl=en&selm=dewar.899945858%40merv>
from Robert Dewar, an Ada luminary and one of the main GNAT creators
refers to ATC (asynchronous transfer of control) as an abomination.

Garry Lancaster

unread,
Mar 4, 2002, 12:28:39 PM3/4/02
to
Alexander Terekhov:

> > > And, BTW, both need post-/pre-
> > > c-/d-tors! ;-) ;-) (well, post-ctors only for
> > > poor Java which lacks d-tors/RAI ;-)).

Garry Lancaster:


> > Why? And once you do that why stop there? People
> > will start asking for pre-pre-ctors and post-post-dtors.

Alexander Terekhov:


> I have yet to see a C++ (or Java) class with sort
> of init() AND init2(), init3()... methods.

And that, I suggest, is a good thing.

When I see init(), and it is intended for the client
to call (rather than a private function that multiple
ctors forward to) I start to think the class is
designed badly; that the person who designed it
didn't really understand how constructors worked
and/or had never come across the number of
design patterns that support more complex object
creation scenarios (Factory Method, Builder,
Prototype etc.) The presence of init2() and init3()
would tend to confirm that impression.

> Perhaps
> we just have a different understanding of what
> post-/pre- c-/d-tors should do/what "problems"
> should they address...

Quite possibly, as I don't see a need for any of
them. Let me expand on my first "Why?": why do
you want post-ctors, pre-ctors, post-dtors and
pre-dtors and how do you want them to work?

Kind regards

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

---

Matthew Austern

unread,
Mar 4, 2002, 12:46:03 PM3/4/02
to
John Nagle <na...@animats.com> writes:

> It's quite possible to make asynchronous
> exceptions work, at least on most current processors.
> Any CPU that can back out a page fault can do clean
> exception handling. This was done in Ada years ago.

The question isn't whether it's possible to implement asynchronous
exceptions; it's whether such an implementation would be usable.

Writing exception-safe code in C++ requires that you reason about
which exceptions can occur at which points. With asynchronous
exception handling, that's no longer possible; you can no longer
write functions with the nothrow guarantee, or rely on the nothrow
guarantee from functions that someone else wrote. I don't know
how I could write reliable code without having that guarantee.

Garry Lancaster

unread,
Mar 4, 2002, 1:32:56 PM3/4/02
to
Alexander Terekhov:

> Technical aspects/functionality (current) of
> "wrapping" aside, boost.thread just ignores
> zillions lines of PORTABLE *C++* Pthread-ed
> code (cancellation aside).

It's portable between any two pthreads
implementations that share the same de-facto
pthreads-C++ binding, since there is no standard
pthreads-C++ binding.

The whole point of standardization is to achieve
a greater degree of portability than that. OK,
pthreads could be a good starting point, but
let's look at all the options.

Garry Lancaster:


> > > > I believe boost::thread is more portable.

Alexander Terekhov:


> > > I don't think so. It is just a POOR re-invention
> > > (I would even call/label it as "FORK") of things/
> > > projects like pthread-win32 with a thin object layer
> > > added on top of it.
> >
> > I don't think shouting that it is poor is constructive.
>
> Boldface formatting "just" adds *emphasis". If you
> consider it as "shouting" generally/altogether (or as
> just silly annoyance preventing/disturbing "matured"
> readers from reading by outline) that's your choice.

I believe it's commonly known as shouting. If I'm
wrong I apologise.

> But your comment/reaction does indicate that you did
> NOT miss the point... ;-) so I'm satisfied. ;-) ;-)
>
> > For a start you will gratuitously offend the people
> > who spent their own time writing it (for no monetary
> > gain) if they happen to read your post.
>
> a) I spent quite a lot of my own time on boost.org
> and even contributed (for no monetary gain ;-))
> some of the stuff used in the current boost.thread.

OK. You don't seem particularly proud of the thing
you helped create though ;-)

> b) well, AFAIK, I am already on the "kill" list
> of W.Kempf! ;-)

And that is something to be proud of?

> > > I do not want to go into details,
> > > but for example, think of Apple/MAC and their
> > > "commercial"/official Pthreads impl once it become
> > > fully ready.
> >
> > I'm afraid I'm not familiar with this. Could you
> > explain what you mean?
>
> I mean roughly this:
> < from an e-mail I've sent to W.Kempf some time ago >

[snip email]

I meant I don't know about the Mac pthreads.
If it isn't (a) reasonably complete and very robust and
(b) available on a licence that permits compiler
and std lib vendors to sell it on as part of
their products, it cannot have much effect on C++
standardization. The information I found on it
after a quick Google was rather cagey on both
these points.

> > It could fly, although I notice it [Win32 pthreads] is


> > currently not a complete implementation
> > of pthreads.
>
> Only Microsoft could make it "complete", I mean
> FULL implementation of IEEE Std 1003.1-2001.

And why is that?

I'm trying to work out whether pthreads on non-Unix
platforms is like Java on multiple platforms (works
reasonably well) or more like COM on multiple
platforms (exists on non-Windows platforms, but
not widely used for whatever reasons, therefore we
assume it is really tied to the original platform).

Kind regards

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

---

Alexander Terekhov

unread,
Mar 4, 2002, 2:16:14 PM3/4/02
to

Matthew Austern wrote:
>
> John Nagle <na...@animats.com> writes:
>
> > It's quite possible to make asynchronous
> > exceptions work, at least on most current processors.
> > Any CPU that can back out a page fault can do clean
> > exception handling. This was done in Ada years ago.
>
> The question isn't whether it's possible to implement asynchronous
> exceptions; it's whether such an implementation would be usable.
>
> Writing exception-safe code in C++ requires that you reason about
> which exceptions can occur at which points. With asynchronous
> exception handling, that's no longer possible; you can no longer
> write functions with the nothrow guarantee, or rely on the nothrow
> guarantee from functions that someone else wrote. I don't know
> how I could write reliable code without having that guarantee.

How about:

size_type basic_string::find(
const basic_string<charT,traits,Allocator>& str,
size_type pos = 0) const;

?

and just image that you could someday declare it as:

size_type basic_string::find(
const basic_string<charT,traits,Allocator>& str,
size_type pos = 0) const acsafe;

and compiler would help you to enforce the
async-cancel-safety of any "acsafe" function
impl (or async-cancel-region in general -- inside
any cancelable function/operation)...

regards,
alexander.

Alexander Terekhov

unread,
Mar 4, 2002, 2:42:45 PM3/4/02
to

Garry Lancaster wrote:
>
> Alexander Terekhov:
> > Technical aspects/functionality (current) of
> > "wrapping" aside, boost.thread just ignores
> > zillions lines of PORTABLE *C++* Pthread-ed
> > code (cancellation aside).
>
> It's portable between any two pthreads
> implementations that share the same de-facto
> pthreads-C++ binding, since there is no standard
> pthreads-C++ binding.

I think that the "de-facto pthread-C++ binding"
is this:

http://groups.google.com/groups?as_umsgid=37401A7B.222265F0%40zko.dec.com

"From: Dave Butenhof (bute...@zko.dec.com)
Subject: Re: Thread Cancellation and C++ Exceptions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Newsgroups: comp.programming.threads
Date: 1999/05/17
...
As other replies have already said, the interactions
of C++ and POSIX are undefined, because neither formally
recognizes the existance of the other. Due to the close
similarity between C++ and C,
you're usually OK, as long as you stay away from
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
the non-C aspects of C++, like exceptions and objects."
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

And, from my another c.l.c++.mod article:

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."
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

> The whole point of standardization is to achieve
> a greater degree of portability than that. OK,
> pthreads could be a good starting point, but
> let's look at all the options.

OK, fine. What are the options?

> Garry Lancaster:
> > > > > I believe boost::thread is more portable.
>
> Alexander Terekhov:
> > > > I don't think so. It is just a POOR re-invention
> > > > (I would even call/label it as "FORK") of things/
> > > > projects like pthread-win32 with a thin object layer
> > > > added on top of it.
> > >
> > > I don't think shouting that it is poor is constructive.
> >
> > Boldface formatting "just" adds *emphasis". If you
> > consider it as "shouting" generally/altogether (or as
> > just silly annoyance preventing/disturbing "matured"
> > readers from reading by outline) that's your choice.
>
> I believe it's commonly known as shouting. If I'm
> wrong I apologise.

No apologies needed and/or wanted... just a bit more
tolerance and perhaps a couple or so of smiles. ;-)

> > But your comment/reaction does indicate that you did
> > NOT miss the point... ;-) so I'm satisfied. ;-) ;-)
> >
> > > For a start you will gratuitously offend the people
> > > who spent their own time writing it (for no monetary
> > > gain) if they happen to read your post.
> >
> > a) I spent quite a lot of my own time on boost.org
> > and even contributed (for no monetary gain ;-))
> > some of the stuff used in the current boost.thread.
>
> OK. You don't seem particularly proud of the thing
> you helped create though ;-)

Well, sort of.

> > b) well, AFAIK, I am already on the "kill" list
> > of W.Kempf! ;-)
>
> And that is something to be proud of?

Proud/not proud.. what's the point of asking
these questions? I've explained my position/
views, that's it.

[...]


> > > It could fly, although I notice it [Win32 pthreads] is
> > > currently not a complete implementation
> > > of pthreads.
> >
> > Only Microsoft could make it "complete", I mean
> > FULL implementation of IEEE Std 1003.1-2001.
>
> And why is that?

Because a lot of stuff actually need to be implemented
inside "system calls", think of scheduling options,
for example. Also, e.g. "manual" CondVar-win32 sucks
because it requires either locking across context
switches OR making scheduling decisions, etc, etc...

> I'm trying to work out whether pthreads on non-Unix
> platforms is like Java on multiple platforms (works
> reasonably well) or more like COM on multiple
> platforms (exists on non-Windows platforms, but
> not widely used for whatever reasons, therefore we
> assume it is really tied to the original platform).

Well pthreads is a standard portable interface for
threading with bindings available for C/ADA/... that's
it (but a lot of java VMs are simply layered on top
of pthreads, AFAIK).

regards,
alexander.

Alexander Terekhov

unread,
Mar 4, 2002, 4:09:54 PM3/4/02
to

Garry Lancaster wrote:
[...]

> > Perhaps
> > we just have a different understanding of what
> > post-/pre- c-/d-tors should do/what "problems"
> > should they address...
>
> Quite possibly, as I don't see a need for any of
> them. Let me expand on my first "Why?": why do
> you want post-ctors, pre-ctors, post-dtors and
> pre-dtors and how do you want them to work?

For example:

http://groups.google.com/groups?as_umsgid=3AA569BF.D1004E9%40web.de

Also, from some OLD e-mail to J.Kanze:
(I've somehow managed to loose/delete the
response to it; sorry, James)

[...poly/non-poly stuff...]

> It's sufficient to make your constructor parameters a special type;
> the constructor passes the address of the object to the argument
> object, and the destructor of the argument object calls your function.
> If the constructor would not otherwise take a parameter, make it take
> one of this type, and default it to a temporary instance. If it takes
> a parameter, use an implicite convertion to the initializer type.

you probably mean:

inline
GB_FieldArrayInitializer::GB_FieldArrayInitializer( GB_std::string
const& i )
: myInitValue( i )
, myArray( NULL )
{
}

inline
GB_FieldArrayInitializer::~GB_FieldArrayInitializer()
{
myArray->set( myInitValue ) ;
}

GB_FieldArray::GB_FieldArray( GB_FieldArrayInitializer const& init )
{
const_cast< GB_FieldArrayInitializer& >( init ).myArray = this ;
}

well,

a) how about all these pricey temps (performance impact)?
b) how about exception safety?
c) how about error-prone nature with respect to adding more derived
classes?
(on each level, each constructor should have a default argument with
temp instance..)
d) how about adding more levels of post-inits? destructors are called
in the opposite order (with respect to constructors) so that most
derived post-init will go BEFORE base post-init which is probably
(in many cases) would be incorrect..


regards,
alexander.

PS. i've started reading comp.lang.c++.mod really short time ago,
however,
during last several weeks the problem of poly.obj creation/destruction/
vf-dispatch was discussed/questioned number of times - e.g.
comp.lang.c++.mod
threads:

"Dispatching in constructors?"
"Calling virtual methods from constructors"

Alexander Terekhov

unread,
Mar 4, 2002, 6:22:52 PM3/4/02
to

Garry Lancaster wrote:
>
> Alexander Terekhov:
> > > > And, BTW, both need post-/pre-
> > > > c-/d-tors! ;-) ;-) (well, post-ctors only for
> > > > poor Java which lacks d-tors/RAI ;-)).
^^^^

>
> Garry Lancaster:
> > > Why? And once you do that why stop there? People
> > > will start asking for pre-pre-ctors and post-post-dtors.
[...]

Oh, forgot this one (nicely illustrates the
problem from Java perspective too, AFAICT):

http://www.cs.umd.edu/~pugh/java/memoryModel/archive/1015.html

...
In practice, you rarely want to start a thread in a constructor, but I
illustrated in this way to make it fit on one slide. In any case,
aren't examples like this handled by requiring that parent thread make
available all variables to a new child thread? I'm newly confused
about where the problem is here.


The main contexts that demand the use of factory methods are ones
where you'd otherwise have something like:


class X {
final static Vector instances = new Vector();
X() {
instances.add(this);
}
}


class Y extends X {
final Field aField;
Y() {
// implicit super() call here.
aField = ...
}
}


The subclass has no way of controlling exposure. So you'd need to
rework this to use public static factory methods with private
constructors etc, which can itself be error-prone. Since you can't
inherit static methods, each subclass needs to invoke the proper
initialization methods:


class X {
final static Vector instances = new Vector();
protected void init() { instances.add(this); }
private X() {}


public static X newX() {
X x = new X();
x.init();
return x;
}
}


class Y extends X {
final Field aField;
private Y() { aField = ... }


public static Y newY() {
Y y = new Y();
y.init();
return y;
}
}


And there are many cases where you can't take this approach anyway. A
year or so ago, we discussed similar problems with the widespread use
of no-arg constructors plus init() methods in JavaBeans etc. Because
of JavaBeans instantiation conventions (normally using
Class.newInstance()), these classes rarely use factory methods that
would hide the object between default-construction and proper
initialization. Plus, they cannot usually use final fields because
the values only become known in init(). I still don't see any way for
people to deal with this except for synchronizing (at least parts of)
nearly all methods. This is not usually a big performance concern with
JavaBeans (they have other overhead anyway) but still a correctness
concern -- people often don't synchronize them because it is not
obvious that they need to.


-Doug
-------------------------------
JavaMemoryModel mailing list -
http://www.cs.umd.edu/~pugh/java/memoryModel

Garry Lancaster

unread,
Mar 5, 2002, 10:59:23 AM3/5/02
to
Alexander Terekhov:

> > > Perhaps
> > > we just have a different understanding of what
> > > post-/pre- c-/d-tors should do/what "problems"
> > > should they address...

Garry Lancaster:


> > Quite possibly, as I don't see a need for any of
> > them. Let me expand on my first "Why?": why do
> > you want post-ctors, pre-ctors, post-dtors and
> > pre-dtors and how do you want them to work?
>
> For example:
>
> http://groups.google.com/groups?as_umsgid=3AA569BF.D1004E9%40web.de

>From that post:
> > struct A {
> > Thread thread;
> > A() : thread(this) { /*...*/ }
> > ~A() { /*...*/ }
> > virtual void run() = 0;
> > private:
> > post A() { thread.start( /*...*/); /*thread_start_routine calls
A::run*/}
> > pre ~A() { thread.join(); /*stop*/ }
> > };
> > class T {
> > public:
> > T() { /*...*/ }
> > ~T() { /*...*/ }
> > virtual void method() = 0;
> > private:
> > post T() { xyz.register( this ); }
> > pre ~T() { xyz.deregister( this ); }
> > };

For a start that doesn't mention pre-ctors or post-dtors.
Is it just the post-ctor and pre-dtor you want?

Sometimes you don't want the thread to start
immediately. You want to create it in a suspended
state and start it later on. The above class doesn't
permit that, by itself. Nonetheless auto-start is a
useful option, so I suspect you want both options in
your thread management system.

The class above is a bit confusing. Ideally
there would be separation between the class that
represents the worker thread and the one that a parent
thread can use to control the thread. Of course there
needs to be communication between the two, but placing
them in the same object is going too far in my opinion.
Java tries to achieve this separation with Thread and
Runnable. Whilst I don't necessarily advocate copying
the exact Java approach for C++ (it wouldn't work as
well in a non-garbage collected language), I think the
principle is sound.

You also say in the older post that you don't
want to use a wrapper class to solve the "problem"
because you find the solution involving the post-ctor
and pre-dtor simpler. Fair enough, but there are lots
of things that could be made slightly simpler by
extending the language.

It is not enough for a new feature to provide some
benefit, the level of benefit it provides must be
sufficiently compelling to outweigh the negative
effects of further complicating an already complex
language. And, in my opinion, this one isn't.

To return to one of my earlier questions ("why
stop there...?"), if we were to add post ctors, to
take just one example, so that a base class could
make some code run after all derived class ctors
had run, just how long do you think it would be
before someone wanted a post-post-ctor so that
a base class could make some code run after
all derived class post-ctors had run? You have
to stop somewhere. For simplicity, let's stop
exactly where we are now.

I had wondered from your earlier posts whether
pre- and post- ctors and dtors were, for some reason
that I had previously missed, essential for
multi-threading work. That certainly isn't the case.
They are really a separate subject.

Kind regards

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

---

Ray Blaak

unread,
Mar 5, 2002, 3:35:44 PM3/5/02
to
Ian Collins <i...@masuma.com> writes:
> Maybe some input form the Java or ADA community would help to identify
> the pros and cons of built in threads.

Well, I used to be an intense Ada programmer. Now I am an intense Java
programmer.

Pros: you can do threading, can do it portably, and you have language support
while doing so. Any library based thread models can only lag behind in ease of
use and integration with your language features.

Cons: can't think of any, off hand. Complexity to the compiler/runtime
environment, perhaps, but that to me is simply the cost of implementing
threading right once and for all.

Ah, I have one con: if you don't use threads then you would like to be able to
avoid the cost of threading. Ada does this more or less successfully. Java
does not: there is extra memory overhead for every object allocated to cover
the possibility that the object just might participate in synchronization.

--
Cheers, The Rhythm is around me,
The Rhythm has control.
Ray Blaak The Rhythm is inside me,
bl...@telus.net The Rhythm has my soul.

Hans Aberg

unread,
Mar 5, 2002, 5:02:12 PM3/5/02
to
In article <ud6yjx...@telus.net>, Ray Blaak <bl...@telus.net> wrote:
>Ian Collins <i...@masuma.com> writes:
>> Maybe some input form the Java or ADA community would help to identify
>> the pros and cons of built in threads.

>Pros: you can do threading, can do it portably, and you have language support


>while doing so. Any library based thread models can only lag behind in ease of
>use and integration with your language features.

>Cons: can't think of any, off hand. Complexity to the compiler/runtime
>environment, perhaps, but that to me is simply the cost of implementing
>threading right once and for all.

>Ah, I have one con: if you don't use threads then you would like to be able to
>avoid the cost of threading.

Isn't it clear that C++ should have thread support, with a way to avoid
threads and their overhead if not needed?

I have an old (1989) book by Gehani & Roome, "The Concurrent C PL". Could
some thread experts please explain the relevance of that stuff for C++?

Hans Aberg * Anti-spam: remove "remove." from email address.
* Email: Hans Aberg <remove...@member.ams.org>
* Home Page: <http://www.matematik.su.se/~haberg/>
* AMS member listing: <http://www.ams.org/cml/>

Garry Lancaster

unread,
Mar 5, 2002, 11:36:18 PM3/5/02
to
> > Alexander Terekhov:
> > > Technical aspects/functionality (current) of
> > > "wrapping" aside, boost.thread just ignores
> > > zillions lines of PORTABLE *C++* Pthread-ed
> > > code (cancellation aside).

Garry Lancaster:


> > It's portable between any two pthreads
> > implementations that share the same de-facto
> > pthreads-C++ binding, since there is no standard
> > pthreads-C++ binding.

Alexander Terekhov


> I think that the "de-facto pthread-C++ binding"
> is this:

>From another post:


> Due to the close
> similarity between C++ and C,
> you're usually OK, as long as you stay away from
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> the non-C aspects of C++, like exceptions and objects."
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

In which case it's barely C++ at all. Which means
stating that there are "zillions lines of PORTABLE
*C++* Pthread-ed code", is a bit of a
misrepresentation.

Garry Lancaster:


> > > > It could fly, although I notice it [Win32 pthreads] is
> > > > currently not a complete implementation
> > > > of pthreads.

Alexander Terekhov:


> > > Only Microsoft could make it "complete", I mean
> > > FULL implementation of IEEE Std 1003.1-2001.
> >
> > And why is that?
>
> Because a lot of stuff actually need to be implemented
> inside "system calls", think of scheduling options,
> for example. Also, e.g. "manual" CondVar-win32 sucks
> because it requires either locking across context
> switches OR making scheduling decisions, etc, etc...

Win32 has a huge developer base and cannot be
ignored by C++, no matter how much Win32 ignores
C++. (And my impression is that MS are starting to
"ignore" C++ almost as much as they "ignore" Java -
it's C# or bust over at Redmond these days.)
We simply cannot alter the standard to specify a
threading system that cannot be fully implemented
on this platform.

This means that at best only a pthreads subset is suitable
for C++ standardisation. I guess we were already coming
to that conclusion anyway, what with the disagreement
over async exceptions, (at least I was) but this wraps it up.

> > I'm trying to work out whether pthreads on non-Unix
> > platforms is like Java on multiple platforms (works
> > reasonably well) or more like COM on multiple
> > platforms (exists on non-Windows platforms, but
> > not widely used for whatever reasons, therefore we
> > assume it is really tied to the original platform).
>
> Well pthreads is a standard portable interface for
> threading with bindings available for C/ADA/... that's
> it (but a lot of java VMs are simply layered on top
> of pthreads, AFAIK).

I wasn't querying which language bindings are
available, I was querying the usefulness/efficiency/
implementability of pthreads on non-Unix platforms.
You partially addressed that with your response
about the only-partial-implementability of pthreads
on Win32.

Kind regards

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

---

Hans Aberg

unread,
Mar 5, 2002, 11:38:46 PM3/5/02
to
In article <FcMf8.27620$Hg1.4...@news6-win.server.ntlworld.com>, "Garry
Lancaster" <glanc...@ntlworld.com> wrote:
>Alexander Terekhov:
>> ...
...

>If I understand you correctly, you are proposing
>that a thread could inject an exception into
>another thread.

The way it looks to me is that if one should design "safe" code or runtime
objects with respect to your favorite property X, where X = access or
runtime security, etc., then the trick seems to be design objects that
communicate with each other according to safe protocols. As going that the
full way in the form of runtime objects becomes too slow, given the
hardware architecture of todays computers, one can in a language like C++
supply a static analysis stripping out the components (runtime
interfacing) that actually are not used during runtime execution.

Now, if the idea is that one thread should somehow be able to manipulate
another thread in an "imperative" uncontrolled manner (not via a "safe"
protocol of that the thread recognizes), then that is an invitation to
write unsafe code.

In article <dilhenw...@isolde.research.att.com>, Matthew Austern
<aus...@research.att.com> wrote:

>John Nagle <na...@animats.com> writes:

>> It's quite possible to make asynchronous
>> exceptions work, at least on most current processors.
>> Any CPU that can back out a page fault can do clean
>> exception handling. This was done in Ada years ago.

>The question isn't whether it's possible to implement asynchronous
>exceptions; it's whether such an implementation would be usable.

>Writing exception-safe code in C++ requires that you reason about
>which exceptions can occur at which points. With asynchronous
>exception handling, that's no longer possible; you can no longer
>write functions with the nothrow guarantee, or rely on the nothrow
>guarantee from functions that someone else wrote. I don't know
>how I could write reliable code without having that guarantee.

So this is just one instance of the principle I mentioned above. One
encounters it too when trying to lock threads or bigger objects such as
databases, in order to keep the data consistent.

So if this the idea of "asynchronous exceptions", the question is why is
this feature useful? -- I mean, unless one has some very good examples
where the feature absolutely must be used, there is not point in adding
it.

Hans Aberg * Anti-spam: remove "remove." from email address.
* Email: Hans Aberg <remove...@member.ams.org>
* Home Page: <http://www.matematik.su.se/~haberg/>
* AMS member listing: <http://www.ams.org/cml/>

---

Francis Glassborow

unread,
Mar 6, 2002, 12:58:51 PM3/6/02
to
In article <SY1h8.100563$Ah1.13...@news2-win.server.ntlworld.com>,
Garry Lancaster <glanc...@ntlworld.com> writes

>Win32 has a huge developer base and cannot be
>ignored by C++, no matter how much Win32 ignores
>C++. (And my impression is that MS are starting to
>"ignore" C++ almost as much as they "ignore" Java -
>it's C# or bust over at Redmond these days.)

Hmm... so why did the move Stan Lippman from C# to C++? And watch the
headlines for an upcoming very interesting addition to their C++ team.

--
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

Alexander Terekhov

unread,
Mar 6, 2002, 12:59:12 PM3/6/02
to

Garry Lancaster wrote:
>
> > > Alexander Terekhov:
> > > > Technical aspects/functionality (current) of
> > > > "wrapping" aside, boost.thread just ignores
> > > > zillions lines of PORTABLE *C++* Pthread-ed
> > > > code (cancellation aside).
^^^^^^^^^^^^^^^^^^^^

>
> Garry Lancaster:
> > > It's portable between any two pthreads
> > > implementations that share the same de-facto
> > > pthreads-C++ binding, since there is no standard
> > > pthreads-C++ binding.
>
> Alexander Terekhov
> > I think that the "de-facto pthread-C++ binding"
> > is this:
>
> >From another post:
> > Due to the close
> > similarity between C++ and C,
> > you're usually OK, as long as you stay away from
> > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> > the non-C aspects of C++, like exceptions and objects."
> > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>
> In which case it's barely C++ at all. Which means
> stating that there are "zillions lines of PORTABLE
> *C++* Pthread-ed code",

Did you miss "(cancellation aside)" bit above? ;-)

> is a bit of a misrepresentation.

Well, not "misrepresentation", I would classify it
(Butenhof's "stay away" bit) is a mild *exaggeration* ;-)
and/but it has SIGNIFICANT implications in the "real" world
too. For example, see this discussion:

http://sources.redhat.com/ml/pthreads-win32/2001/subjects.html#00143
("pthreads VCE: problem with destructor")

and the outcome:

http://sources.redhat.com/cgi-bin/cvsweb.cgi/pthreads/NEWS?rev=1.2&content-type=text/x-cvsweb-markup&cvsroot=pthreads-win32
("Cleanup code default style. (IMPORTANT)")

> Garry Lancaster:
> > > > > It could fly, although I notice it [Win32 pthreads] is
> > > > > currently not a complete implementation
> > > > > of pthreads.
>
> Alexander Terekhov:
> > > > Only Microsoft could make it "complete", I mean
> > > > FULL implementation of IEEE Std 1003.1-2001.
> > >
> > > And why is that?
> >
> > Because a lot of stuff actually need to be implemented
> > inside "system calls", think of scheduling options,
> > for example. Also, e.g. "manual" CondVar-win32 sucks
> > because it requires either locking across context
> > switches OR making scheduling decisions, etc, etc...
>
> Win32 has a huge developer base and cannot be
> ignored by C++, no matter how much Win32 ignores
> C++. (And my impression is that MS are starting to
> "ignore" C++ almost as much as they "ignore" Java -
> it's C# or bust over at Redmond these days.)

;-)

And, BTW, C#/CLI "write-ups" ;-) are available here:

http://www.ecma.ch/ecma1/STAND/ecma-334.htm
http://www.ecma.ch/ecma1/STAND/ecma-335.htm
http://www.ecma.ch/ecma1/techrep/e-tr-084.htm

> We simply cannot alter the standard to specify a
> threading system that cannot be fully implemented
> on this platform.

Uhmm. Are you saying that MS simply cannot fully
implement it on their platform(s)?!

> This means that at best only a pthreads subset is suitable
> for C++ standardisation.

> I guess we were already coming
> to that conclusion anyway, what with the disagreement
> over async exceptions,

async.exceptions (async.cancel) IS a powerful and a
very useful tool, just take a look at Real-time Java
Spec stuff, for some examples, "justifications":

http://www.rtj.org
http://www.jcp.org/jsr/detail/001.jsp

regards,
alexander.

Alexander Terekhov

unread,
Mar 6, 2002, 1:00:00 PM3/6/02
to

Hans Aberg wrote:
[...]

> I have an old (1989) book by Gehani & Roome, "The Concurrent C PL". Could
> some thread experts please explain the relevance of that stuff for C++?

Well, how about some more (up-to-date and
EXCITING) stuff (in addition to your old C
book ;-))?

ftp://plg.uwaterloo.ca/pub/uSystem/

from "Announcement":

>> ANNOUNCEMENT <<

Version 4.9 of uC++ (pronounced micro-C++) is an extended C++ providing
light-weight concurrency on uniprocessor and multiprocessor computers
running
the UNIX operating system. uC++ extends C++ in somewhat the same way
that C++
extends C. The extensions introduce new objects that augment the
existing
control flow facilities and provide concurrency. uC++ provides both
direct and
indirect communication using the traditional routine call mechanism.
All
aspects of uC++ are statically type-checked. Furthermore, uC++ provides
mechanisms for precisely controlling the order in which requests are
serviced
and for postponing work on already-accepted requests.

Version 4.9 has:

. bug fixes, including improvements to the translator to handle the STL
. real-time support, including periodic, aperiodic and sporadic tasks,
plus
priority inheritance
. debugger support for KDB

There is also a concurrent debugger for uC++ 4.9, called KDB, which
allows
independent control of the user level threads (but it's broken at the
moment).


>> REQUIREMENTS <<

uC++ requires at least:

1. at least version egcs-1.1.2 or gcc-2.95.2. uC++ does NOT compile
using
other compilers.

2. at least version 3.79.1 of gmake, which is available by anonymous ftp
from
any FSF mirror site.


>> FTP SITE <<

Version 4.9 of uC++ can be obtained by anonymous ftp from the following
location (remember to set your ftp mode to binary):

plg.uwaterloo.ca:pub/uSystem/u++-4.9.tar.gz

The README file contains all the installation instructions. Good luck
and
happy concurrency.


>> PORTABILITY <<

uC++ comes configured to run on any of the following platforms:

sun-sparc-svr4 (solaris) (both single and multiple processor)
dec-alpha (both single and multiple processor)
sgi-mips-r4000 (both single and multiple processor)
sgi-mips-r3000 (single processor only)
ibm-rs6000 (single processor only)
hp-pa (single processor only)
pc-i386-linux (both single and multiple processor)


>> PAPERS <<

See http://plg.uwaterloo.ca/~usystem for additional information, papers
and
updates.


>> FUTURE <<

. concurrent exception handling capabilities in the next release
. database toolkit using memory mapping
. thread-level profiler

---

regards,
alexander.

Alexander Terekhov

unread,
Mar 6, 2002, 4:02:41 PM3/6/02
to

Garry Lancaster wrote:
[...]

> For a start that doesn't mention pre-ctors or post-dtors.
> Is it just the post-ctor and pre-dtor you want?

Yep. "post" for ctor(s) and "pre" for dtor.

> Sometimes you don't want the thread to start
> immediately. You want to create it in a suspended
> state and start it later on.

Yep (e.g. create multiple threads and start'em all
AFTER successful creation/registration/... or do NOT
start them at all IF something goes wrong (throws)
in the "init" phase, for example).

> The above class doesn't
> permit that, by itself. Nonetheless auto-start is a
> useful option, so I suspect you want both options in
> your thread management system.

Yep. http://www.terekhov.de/mythread.c

[...]


> To return to one of my earlier questions ("why
> stop there...?"), if we were to add post ctors, to
> take just one example, so that a base class could
> make some code run after all derived class ctors
> had run, just how long do you think it would be
> before someone wanted a post-post-ctor so that
> a base class could make some code run after
> all derived class post-ctors had run?

I guess you could just call some protected virtual
method(s) inside your base class post-ctor/pre-dtor.

> You have
> to stop somewhere. For simplicity, let's stop
> exactly where we are now.
>
> I had wondered from your earlier posts whether
> pre- and post- ctors and dtors were, for some reason
> that I had previously missed, essential for
> multi-threading work. That certainly isn't the case.
> They are really a separate subject.

Well, for some sort of "active objects" it is
not a separate subject, I guess. But, in general,
probably yes.

regards,
alexander.

Garry Lancaster

unread,
Mar 6, 2002, 4:44:43 PM3/6/02
to
> > >From another post:
> > > Due to the close
> > > similarity between C++ and C,
> > > you're usually OK, as long as you stay away from
> > > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> > > the non-C aspects of C++, like exceptions and objects."
> > > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Garry Lancaster:


> > In which case it's barely C++ at all. Which means
> > stating that there are "zillions lines of PORTABLE
> > *C++* Pthread-ed code",

Alexander Terekhov:


> Did you miss "(cancellation aside)" bit above? ;-)

No - I saw it, but it didn't seem relevant.

Or are you saying that cancellation is the only
reason that you can't use exceptions or objects
with pthreads?

Garry Lancaster:


> > We simply cannot alter the standard to specify a
> > threading system that cannot be fully implemented

> > on this [the Win32] platform.


>
> Uhmm. Are you saying that MS simply cannot fully
> implement it on their platform(s)?!

I guess they could if they wanted to. The point is
that there is no sign that they do want to *and* I suspect
the situation would be no different even if the C++ standard
required the changes. All that would happen is
that no-one would be able to fully implement
the C++ standard under Windows and MS C++
would become even more of a branch of standard
C++ than it already is. That would be a Bad
Thing (TM).

Kind regards

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

---

Garry Lancaster

unread,
Mar 6, 2002, 4:53:43 PM3/6/02
to
Garry Lancaster:

> >Win32 has a huge developer base and cannot be
> >ignored by C++, no matter how much Win32 ignores
> >C++. (And my impression is that MS are starting to
> >"ignore" C++ almost as much as they "ignore" Java -
> >it's C# or bust over at Redmond these days.)

Francis Glassborow:


> Hmm... so why did the move Stan Lippman from
> C# to C++?

If he single-handedly saves the day that would be
great, but I'm pessimistic. Time will tell.

My thinking is that if a company the size of MS wanted
a standards compliant C++ compiler enough, they
would have one by now. If their internal compiler
team couldn't manage it, they could have got a fair
way by writing some very large cheques.

Despite everything I *like* the MS compiler, so I really
want them to sort it out. But in the 5 years from v6 to
v7 not a great deal seems to have changed. I've
heard that 7.1 will be much, much better, but I'm
running out of faith in those guys.

Unlike Visual Basic, MS's previous "favourite language",
C# is a serious attempt to do most of the things that
C++ does. If .NET catches on as MS hope it will, in
a few years time they will be in a position to heavily
restrict the running of native code on Windows. Result:
no real C++ anymore. Will this actually happen? Again,
time will tell.

> And watch the
> headlines for an upcoming very interesting addition to their C++ team.

Go on, spill the beans. You know you want to ;-)

Kind regards

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

---

Francis Glassborow

unread,
Mar 6, 2002, 8:18:12 PM3/6/02
to
In article <N2wh8.62770$Hg1.10...@news6-win.server.ntlworld.com>,
Garry Lancaster <glanc...@ntlworld.com> writes

>> And watch the
>> headlines for an upcoming very interesting addition to their C++ team.
>
>Go on, spill the beans. You know you want to ;-)

Like any journalist, there are times when I am trusted with information
on a strictly confidential basis. Read my column in the next issue of C
Vu (actually it will be old news by then)

--
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

---

t...@cs.ucr.edu

unread,
Mar 7, 2002, 12:33:07 PM3/7/02
to
Ray Blaak <bl...@telus.net> wrote:
[...]
: Any library based thread models can only lag behind in ease of

: use and integration with your language features.

Would you care to elaborate?

Tom Payne

Alexander Terekhov

unread,
Mar 7, 2002, 12:45:46 PM3/7/02
to

Garry Lancaster wrote:
[...]

> Alexander Terekhov:
> > Did you miss "(cancellation aside)" bit above? ;-)
>
> No - I saw it, but it didn't seem relevant.
>
> Or are you saying that cancellation is the only
> reason that you can't use exceptions or objects
> with pthreads?

http://groups.google.com/groups?as_umsgid=3C73CB8B.E764D3C6%40web.de
(see "yep (and pthread_exit too)" links as well)

> Garry Lancaster:
> > > We simply cannot alter the standard to specify a
> > > threading system that cannot be fully implemented
> > > on this [the Win32] platform.
> >
> > Uhmm. Are you saying that MS simply cannot fully
> > implement it on their platform(s)?!
>
> I guess they could if they wanted to. The point is
> that there is no sign that they do want to *and* I suspect
> the situation would be no different even if the C++ standard
> required the changes. All that would happen is
> that no-one would be able to fully implement
> the C++ standard under Windows and MS C++
> would become even more of a branch of standard
> C++ than it already is. That would be a Bad
> Thing (TM).

Ever heard of VMware (good old mainframe-class stuff
now available on commodity-class computers too)? Just
put all those "Bad Thing (TM)"s in couple of a virtual
machines/guests hosted by some really Good Thing (TM)
that is also used for the REAL programming/serious IT
stuff, in general. ;-)

regards,
alexander.

Chelly

unread,
Mar 10, 2002, 3:48:16 PM3/10/02
to
A few years ago I wrote a web page with my ideas on the matter of
asynchronous exceptions. I was interested in exploring the idea rather
than coming to conclusions as to its usefulness.

http://www.slack.net/~ant/cpp/asyncexcept.html

I hope this adds something useful to this lively discussion :)

Ray Blaak

unread,
Mar 11, 2002, 12:13:44 PM3/11/02
to
t...@cs.ucr.edu writes:
> Ray Blaak <bl...@telus.net> wrote:
> [...]
> : Any library based thread models can only lag behind in ease of
> : use and integration with your language features.
>
> Would you care to elaborate?

I mean this in only the trivial sense. A language-based thread model is no
worse than a library based one from a programmer's point of view, since it is
equivalent to doing the appropriate routine calls. However, the language-based
one can have custom syntax and can cooperate better with the compiler to be
more powerful in terms of expressivity.

--
Cheers, The Rhythm is around me,
The Rhythm has control.
Ray Blaak The Rhythm is inside me,
bl...@telus.net The Rhythm has my soul.

---

Hans Aberg

unread,
Mar 11, 2002, 12:36:41 PM3/11/02
to
In article <not-for-mail-0...@tcnet01-55.austin.texas.net>,

postmast.ro...@iname.com (Chelly) wrote:
>A few years ago I wrote a web page with my ideas on the matter of
>asynchronous exceptions. I was interested in exploring the idea rather
>than coming to conclusions as to its usefulness.
>
> http://www.slack.net/~ant/cpp/asyncexcept.html
>
>I hope this adds something useful to this lively discussion :)

One idea that comes to my mind is that when throwing an exception, one is
in reality throwing a pair (exception, exception-handler-stack):

If the exception-handler-stack is not explicitly indicated, it is assumed
to be the "root", the one that the program comes with.

If threads are hooked up like on a parent-child tree, then the
exception-handler-stacks are hooked up similarly. If an exception is not
handled at the point where this stack branches, then other threads may be
killed off by the exception. This may sound wrong at first, but if say a
group of threads share memory, and one thread detects a memory error, then
all threads should be killed off sharing the same memory. So possibly, one
may need a mechanism telling which kinds of exceptions each thread should
be able to throw.

Then, under Mach (or so I recall), threads need to not be hooked up on a
parent-child tree, so the exception-handler-stack can be hooked up in even
more complicated manners.

Hans Aberg * Anti-spam: remove "remove." from email address.
* Email: Hans Aberg <remove...@member.ams.org>
* Home Page: <http://www.matematik.su.se/~haberg/>
* AMS member listing: <http://www.ams.org/cml/>

---

Alexander Terekhov

unread,
Mar 11, 2002, 12:36:27 PM3/11/02
to
postmast.ro...@iname.com (Chelly) wrote in message news:<not-for-mail-0...@tcnet01-55.austin.texas.net>...

> A few years ago I wrote a web page with my ideas on the matter of
> asynchronous exceptions. I was interested in exploring the idea rather
> than coming to conclusions as to its usefulness.
>
> http://www.slack.net/~ant/cpp/asyncexcept.html

http://groups.google.com/groups?as_umsgid=user-1009992342430001%40aus-as3-150.io.com

: [...]
: First, let's consider it from the standpoint of throwing an exception
: asynchronously, with regard to the OS and threads.
:
: thread 1 // signal occurs here...
: thread 2 // ... or maybe here
: thread 3
:
: Which thread does exception get thrown in? Is it acceptable to have it
: thrown in what is essentially a random thread?

The short "answer" is:

async-signal-safety != async-cancel(exception)-safety != thread-safety

Details/Info[1]:

http://www.opengroup.org/onlinepubs/007904975/functions/sigwait.html

"RATIONALE

To provide a convenient way for a thread to wait for a
signal, this volume of IEEE Std 1003.1-2001 provides the
sigwait() function. For most cases where a thread has to
wait for a signal, the sigwait() function should be quite
convenient, efficient, and adequate.

However, requests were made for a lower-level primitive
than sigwait() and for semaphores that could be used by
threads. After some consideration, threads were allowed
to use semaphores and sem_post() was defined to be
async-signal and async-cancel-safe.

In summary, when it is necessary for code run in response
to an asynchronous signal to notify a thread, sigwait()
should be used to handle the signal. Alternatively, if the
implementation provides semaphores, they also can be used,
either following sigwait() or from within a signal handling
routine previously registered with sigaction()."

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

"The sigevent structure is used by other POSIX.1 functions
that result in asynchronous event notifications to specify
the notification mechanism to use and other information needed
by the notification mechanism. IEEE Std 1003.1-2001 defines
only three symbolic values for the notification mechanism.
SIGEV_NONE is used to indicate that no notification is required
when the event occurs. This is useful for applications that use
asynchronous I/O with polling for completion. SIGEV_SIGNAL
indicates that a signal is generated when the event occurs.
SIGEV_NOTIFY[2] provides for "callback functions" for asynchronous
notifications done by a function call within the context of a new
thread. This provides a multi-threaded process a more natural means
of notification than signals. The primary difficulty with previous
notification approaches has been to specify the environment of the
notification routine.

One approach is to limit the notification routine to call
only functions permitted in a signal handler. While the list
of permissible functions is clearly stated, this is overly
restrictive.

A second approach is to define a new list of functions or
classes of functions that are explicitly permitted or not
permitted. This would give a programmer more lists to deal
with, which would be awkward.

The third approach is to define completely the environment
for execution of the notification function. A clear definition
of an execution environment for notification is provided by
executing the notification function in the environment of a
newly created thread."

http://www.opengroup.org/onlinepubs/007904975/functions/xsh_chap02_04.html

"Multi-threaded programs can use an alternate event notification
mechanism. When a notification is processed, and the sigev_notify
member of the sigevent structure has the value SIGEV_THREAD, the
function sigev_notify_function is called with parameter sigev_value.

The function shall be executed in an environment as if it were
the start_routine for a newly created thread with thread attributes
specified by sigev_notify_attributes. If sigev_notify_attributes is
NULL, the behavior shall be as if the thread were created with the
detachstate attribute set to PTHREAD_CREATE_DETACHED."

regards,
alexander.

[1] PDFs:

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


[2] That should be "SIGEV_THREAD" (NOT "SIGEV_NOTIFY").

John Nagle

unread,
Mar 12, 2002, 2:06:30 PM3/12/02
to
Assuming you want to do this, let me suggest an approach
that leads to cleaner semantics.

Suppose we take the position that an asynchronous
exception can only occur at the following points:

-- Any non-inline call
-- Any loop-type statement (for, while, do)

In any program of finite size, one of those has to
happen reasonably soon.

As for implementation, there are several
possibilities:

- Flag test - the compiler generates a
test for the thread-termination flag at
every for, while, do, and call. Simple,
easy to do, and carries a performance
penalty.

- Breakpoint - when a thead is interrupted
("interrupted" here means something like a UNIX signal)
and asked to throw an exception, the thread is
stepped forward, as in a debugger, until the next
allowable throw point is reached, at which point
the exception is thrown. A (possibly large) table
of allowed throw points is needed. No speed penalty
until an exception is requested.

- Hardware - Flag test, as above, but with hardware
support. Consider for future CPUs if this feature
proves possible.

Note that this is less intrusive than the Ada
model, which allows machine-level exceptions that have
to happen RIGHT NOW, like hardware fault detection.

John Nagle
Animats

Herb Sutter

unread,
Mar 12, 2002, 2:09:24 PM3/12/02
to
On Wed, 6 Mar 2002 21:53:43 GMT, "Garry Lancaster"
<glanc...@ntlworld.com> wrote:
>My thinking is that if a company the size of MS wanted
>a standards compliant C++ compiler enough, they
>would have one by now.

Microsoft is (finally, and relatively recently) committed to producing a
100% conformant C++ compiler, meaning not only C++98, but also tracking the
evolving C++0x as it begins to form, and specifically support for key
community libraries. It won't all be in 7.1, but significant steps in that
direction will be. It's amazing what replacing one key internal
decision-maker a year or so ago will do to liberate a team that already
wanted to do the right thing.

I'm glad, because conformance to standards is important. You know it, and I
know it. That's why we hang out in this group and attend committee meetings.

>Despite everything I *like* the MS compiler, so I really
>want them to sort it out. But in the 5 years from v6 to
>v7 not a great deal seems to have changed. I've
>heard that 7.1 will be much, much better

I hope so. Time will tell.

Herb

---
Herb Sutter (www.gotw.ca)

Secretary, ISO WG21/ANSI J16 (C++) standards committee (www.gotw.ca/iso)
Contributing Editor, C/C++ Users Journal (www.cuj.com)

Check out "THE C++ Seminar" - Boston, March 2002 (www.gotw.ca/cpp_seminar)

Alexander Terekhov

unread,
Mar 12, 2002, 4:57:14 PM3/12/02
to

John Nagle wrote:
>
> Assuming you want to do this,

To do what? Form async-cancel-regions?

> let me suggest an approach
> that leads to cleaner semantics.

[...]

Personally, I am quite happy with "manual"
"semantics"[1] of

// arrange some clean up (fix up) if needed
// (should have PTHREAD_CANCEL_DEFERRED type
// active while doing this)
//...
pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS,&ct );
... // async-cancel-safe code/operations
pthread_setcanceltype( ct,&ct );

to form async-cancel-regions.

The only thing that I am missing is the compiler enforcement
of async-cancel-safety inside async-cancel-regions...

> Note that this is less intrusive than the Ada
> model, which allows machine-level exceptions that have
> to happen RIGHT NOW, like hardware fault detection.

Uhmm, what do you mean?

http://www-users.cs.york.ac.uk/~andy/lrm95/09_08.htm

"(5) When the execution of a construct is aborted (including
that of a task_body or of a sequence_of_statements), the
execution of every construct included within the aborted
execution is also aborted, except for executions included
within the execution of an abort-deferred operation; the
execution of an abort-deferred operation continues to
completion without being affected by the abort; the
following are the abort-deferred operations: ..."

"(21) If an assignment operation completes prematurely
due to an abort, the assignment is said to be disrupted;
the target of the assignment or its parts can become
abnormal, and certain subsequent uses of the object can
be erroneous, as explained in 13.9.1."

Also, you might want to take a look at pthreads-win32:
(the way how it implements C++ exception-based async-cancel)

http://sources.redhat.com/pthreads-win32

regards,
alexander.

[1]
http://www.opengroup.org/onlinepubs/007904975/functions/pthread_setcancelstate.html

"RATIONALE

The pthread_setcancelstate() and pthread_setcanceltype()
functions control the points at which a thread may be
asynchronously canceled. For cancelation control to be
usable in modular fashion, some rules need to be followed.

An object can be considered to be a generalization of a
procedure. It is a set of procedures and global variables
written as a unit and called by clients not known by the
object. Objects may depend on other objects.

First, cancelability should only be disabled on entry to
an object, never explicitly enabled. On exit from an object,
the cancelability state should always be restored to its
value on entry to the object.

This follows from a modularity argument: if the client
of an object (or the client of an object that uses that
object) has disabled cancelability, it is because the
client does not want to be concerned about cleaning up
if the thread is canceled while executing some sequence
of actions. If an object is called in such a state and
it enables cancelability and a cancelation request is
pending for that thread, then the thread is canceled,
contrary to the wish of the client that disabled.

Second, the cancelability type may be explicitly set
to either deferred or asynchronous upon entry to an
object. But as with the cancelability state, on exit
from an object the cancelability type should always be
restored to its value on entry to the object.

Finally, only functions that are cancel-safe may be
called from a thread that is asynchronously cancelable."

Alexander Terekhov

unread,
Mar 12, 2002, 7:00:44 PM3/12/02
to

Alexander Terekhov wrote:
[...]
> [...poly/non-poly stuff...]

Heck! I just can't resist; "details" are here:

http://groups.google.com/groups?as_umsgid=3C8CBE40.4530411B%40web.de
http://groups.google.com/groups?as_umsgid=3C8DED28.3154017%40web.de

regards,
alexander.

David Butenhof

unread,
Mar 13, 2002, 12:07:33 PM3/13/02
to
Matthew Austern wrote:

> John Nagle <na...@animats.com> writes:
>
>> It's quite possible to make asynchronous
>> exceptions work, at least on most current processors.
>> Any CPU that can back out a page fault can do clean
>> exception handling. This was done in Ada years ago.
>
> The question isn't whether it's possible to implement asynchronous
> exceptions; it's whether such an implementation would be usable.
>
> Writing exception-safe code in C++ requires that you reason about
> which exceptions can occur at which points. With asynchronous
> exception handling, that's no longer possible; you can no longer
> write functions with the nothrow guarantee, or rely on the nothrow
> guarantee from functions that someone else wrote. I don't know
> how I could write reliable code without having that guarantee.

POSIX supports asynchronous cancelability (the ability for one thread to
"asynchronously inject an exception" into another thread) for historical
convenience. Asynchronous cancelability isn't necessary (at best it's a
minor performance boost), and it's almost impossible to use even in
straight POSIX/C. (The only POSIX functions that can legally be called with
async cancelability are the functions that disable async cancelability,
disable cancelability entirely, and post a pending cancel to another
thread. The latter is a "stupid thread trick" that never belonged in the
standard and cannot rationally be justified.)

Cancellation really operates synchronously, and that's the only form truly
and broadly supported by the standard. With synchronous ("deferred")
cancelability (the default for all threads), another thread can do no more
than asynchronously POST a request, much like a pending signal mask in
UNIX. But that's just data, and data that's not explicitly visible in the
standard interfaces. The thread itself, at well defined execution points
(particularly blocking calls) tests the state of that pending cancel
flag and, if necessary and appropriate, raises the cancel exception. That
means all deferred cancellation points, as defined by POSIX/UNIX and any
additional C++ points, would be tagged as being able to throw the cancel
exception.

There's no need for a "C++ POSIX thread binding" to support async
cancelability. In fact, I would encourage the standards committee to
disallow async cancelability. It's a bad idea, it's not well supported or
particularly usable (nor useful) even for POSIX and C, and as pointed out
here distinctly does not fit into the C++ exception model.

/------------------[ David.B...@compaq.com ]------------------\
| Compaq Computer Corporation POSIX Thread Architect |
| My book: http://www.awl.com/cseng/titles/0-201-63392-2/ |
\-----[ http://home.earthlink.net/~anneart/family/dave.html ]-----/

David Butenhof

unread,
Mar 13, 2002, 12:07:56 PM3/13/02
to
Hans Aberg wrote:

> In article <not-for-mail-0...@tcnet01-55.austin.texas.net>,
> postmast.ro...@iname.com (Chelly) wrote:
>>A few years ago I wrote a web page with my ideas on the matter of
>>asynchronous exceptions. I was interested in exploring the idea rather
>>than coming to conclusions as to its usefulness.
>>
>> http://www.slack.net/~ant/cpp/asyncexcept.html
>>
>>I hope this adds something useful to this lively discussion :)
>
> One idea that comes to my mind is that when throwing an exception, one is
> in reality throwing a pair (exception, exception-handler-stack):
>
> If the exception-handler-stack is not explicitly indicated, it is assumed
> to be the "root", the one that the program comes with.
>
> If threads are hooked up like on a parent-child tree, then the
> exception-handler-stacks are hooked up similarly. If an exception is not
> handled at the point where this stack branches, then other threads may be
> killed off by the exception. This may sound wrong at first, but if say a
> group of threads share memory, and one thread detects a memory error, then
> all threads should be killed off sharing the same memory. So possibly, one
> may need a mechanism telling which kinds of exceptions each thread should
> be able to throw.
>
> Then, under Mach (or so I recall), threads need to not be hooked up on a
> parent-child tree, so the exception-handler-stack can be hooked up in even
> more complicated manners.

This isn't an issue for POSIX threads.

POSIX threads do not have parent-child relationships. All threads are
peers, and as nearly equal as possible. (The only real exception is that a
return from most threads is equivalent to pthread_exit() while a return
from the initial thread [main()] is equivalent to exit(), terminating the
process.)

DCE did try to build this sort of hierarchy for RPC relationships; an
exception that terminated an RPC server routine would propagate to the stub
routine in the client (caller). This proved REALLY annoying; but of course
in that case exception support was primitive (C macros) and we were
propagating the exception (sans stack context) across address spaces and
even architectures/operating systems.

DCE also established that converting hardware errors into exceptions was a
mistake; though one that could be at least partly addressed with
better/deeper exception support. The problem is that a SIGSEGV, for
example, indicates a problem with the address space. There's not much hope
that you can really recover from this, but exception conversion implies
that you can. As you say, shutting down the process quickly and with
minimum perturbation is best, so the problem can be analyzed. Eventually
DCE returned to the original idea that a SIGSEGV just dumps core and
terminates.

On the other hand, other operating systems like OpenVMS, which grew up with
exceptions from the start, do report memory errors as exceptions. Process
termination occurs only when/if the memory exception propagates to the base
of the stack... but it still terminates the process rather than an
individual thread. An eventual core dump at that point may be less useful,
though, since the offending stack has probably been unwound.

Finally, a minor note on Mach. Yes, Mach had threads, but they're really
barebones and primitive. (Remember, "Mach" is just the central core of an
operating system, not a whole operating system.) Like nearly every other
widespread threading system, Mach threads lack any parental relationships
(except that each Mach thread is connected to a single Mach task context).
Mach "exceptions" aren't exceptions in the C++/Ada sense; rather, they're a
special kind of IPC message directed to a distinct communications port
that's automatically assigned to each thread.

/------------------[ David.B...@compaq.com ]------------------\
| Compaq Computer Corporation POSIX Thread Architect |
| My book: http://www.awl.com/cseng/titles/0-201-63392-2/ |
\-----[ http://home.earthlink.net/~anneart/family/dave.html ]-----/

---

Alexander Terekhov

unread,
Mar 13, 2002, 3:03:30 PM3/13/02
to

David Butenhof wrote:
[...]

> POSIX threads do not have parent-child relationships. All threads are
> peers, and as nearly equal as possible. (The only real exception is that a
> return from most threads is equivalent to pthread_exit() while a return
> from the initial thread [main()] is equivalent to exit(), terminating the
> process.)

How about future threads-aware C++0x with "void{*} thread::main(...)"
*alternative* (optional) w.r.t the current "exceptional" main()?

> DCE also established that converting hardware errors into exceptions was a
> mistake; though one that could be at least partly addressed with
> better/deeper exception support. The problem is that a SIGSEGV, for
> example, indicates a problem with the address space. There's not much hope
> that you can really recover from this, but exception conversion implies
> that you can.

Nope.
http://groups.google.com/groups?as_umsgid=3C75280D.571C0C0A%40web.de

"'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!"

> As you say, shutting down the process quickly and with
> minimum perturbation is best, so the problem can be analyzed. Eventually
> DCE returned to the original idea that a SIGSEGV just dumps core and
> terminates.
>
> On the other hand, other operating systems like OpenVMS, which grew up with
> exceptions from the start, do report memory errors as exceptions. Process
> termination occurs only when/if the memory exception propagates to the base
> of the stack... but it still terminates the process rather than an
> individual thread. An eventual core dump at that point may be less useful,
> though, since the offending stack has probably been unwound.

So, why do you then "propagate to the base of the stack"?!

Just to find out that there is actually NO matching hander
present?

Uhmm.. Looking at:

http://www.tru64unix.compaq.com/docs/base_doc/DOCUMENTATION/V51A_PDF/ARH9MBTE.PDF

for example, I see nothing that could/would prevent to
find this out PRIOR to "stack-unwinding". So, again,
I guess I'm just missing something. :( :(

regards,
alexander.

Alexander Terekhov

unread,
Mar 13, 2002, 8:59:51 PM3/13/02
to

David Butenhof wrote:
[...]

> POSIX supports asynchronous cancelability (the ability for one thread to
> "asynchronously inject an exception" into another thread) for historical
> convenience.

Uhmm. Rationale section(s) and C953 volume
do NOT put/present it this way, AFAICT.

> Asynchronous cancelability isn't necessary (at best it's a
> minor performance boost), and it's almost impossible to use even in
> straight POSIX/C. (The only POSIX functions that can legally be called with
> async cancelability are the functions that disable async cancelability,
> disable cancelability entirely, and post a pending cancel to another
> thread. The latter is a "stupid thread trick" that never belonged in the
> standard and cannot rationally be justified.)

OK, fine. You probably mean:

"2.9.5.4 Async-Cancel Safety

2356 The pthread_cancel(), pthread_setcancelstate(),
and pthread_setcanceltype() functions are defined to
2357 be async-cancel safe.
2358 No other functions in this volume of IEEE Std 1003.1-2001
are required to be async-cancel-safe."

But, sorry, what does this have to do with *MY* functions?

Are you saying that it's almost impossible to write correct
and valid async-cancel-safe functions (code-region) in POSIX/C,
C++ aside, for a moment?!

[...]


> There's no need for a "C++ POSIX thread binding" to support async
> cancelability. In fact, I would encourage the standards committee to
> disallow async cancelability.

Well, if I could, I would encourage the standards committee to
think a bit in the direction of const-like async-cancel-safety
*enforcement* mechanism added to the C++0x language...

> It's a bad idea, it's not well supported or
> particularly usable (nor useful) even for POSIX and C, and as pointed out
> here distinctly does not fit into the C++ exception model.

What exactly "does not fit into the C++ exception model"?

I fail to see the point/reason... perhaps I'm just missing
something. :(

regards,
alexander.

David Butenhof

unread,
Mar 14, 2002, 8:40:45 AM3/14/02
to
Alexander Terekhov wrote:

> But, sorry, what does this have to do with *MY* functions?
>
> Are you saying that it's almost impossible to write correct
> and valid async-cancel-safe functions (code-region) in POSIX/C,
> C++ aside, for a moment?!

Yes. At least, anything "interesting". If you create or hold resources, you
can write "async-cancel safe" code only by DISABLING either cancellation
(entirely) or async cancelability across that region of code.

You can't call malloc() or free(), for example, with async cancelability
enabled. If you did, you wouldn't be able to tell whether your memory had
been allocated -- or whether it had truly been freed. Unless of course THEY
were made async-cancel safe... which could be done only by disabling async
cancel so they could determine reliably what resources THEY owned.

Interrupting execution at arbitrary points, asynchronously, makes recovery
difficult.

The only INTENDED purpose was so you could write a compute-bound loop, that
held no resources, without making constant calls to pthread_testcancel(),
and still have it responsive to cancel requests. OK, great; but, honestly,
"big deal". It's rarely useful, mostly misused, and complicates everything
about using and describing cancellation. We'd be better off without it.

We did it in CMA not because we saw a real need, but "because we could".
Experience teaches, if nothing else, that "because we could" is a really
bad excuse for just about anything...

> [...]
>> There's no need for a "C++ POSIX thread binding" to support async
>> cancelability. In fact, I would encourage the standards committee to
>> disallow async cancelability.
>
> Well, if I could, I would encourage the standards committee to
> think a bit in the direction of const-like async-cancel-safety
> *enforcement* mechanism added to the C++0x language...

Sure, I would encourage them to think about it enough to realise that it's
impossible in general and impractical and pointless even in specific cases
where it's possible.

>> It's a bad idea, it's not well supported or
>> particularly usable (nor useful) even for POSIX and C, and as pointed out
>> here distinctly does not fit into the C++ exception model.
>
> What exactly "does not fit into the C++ exception model"?
>
> I fail to see the point/reason... perhaps I'm just missing
> something. :(

Because, as someone said, C++ is designed with the expectation that
exceptions arise synchronously and under full language control, via throw.
There are no asynchronous exceptions. Adding them is not trivial. I argue
that it's not useful. I'm NOT going to argue that this shouldn't be
considered; just that I know what the outcome should be. ;-)

/------------------[ David.B...@compaq.com ]------------------\
| Compaq Computer Corporation POSIX Thread Architect |
| My book: http://www.awl.com/cseng/titles/0-201-63392-2/ |
\-----[ http://home.earthlink.net/~anneart/family/dave.html ]-----/

---

Hans Aberg

unread,
Mar 14, 2002, 11:38:35 AM3/14/02
to
In article <bZIj8.903$fL6....@news.cpqcorp.net>, David Butenhof
<David.B...@compaq.com> wrote:

>POSIX supports asynchronous cancelability (the ability for one thread to

>"asynchronously inject an exception" into another thread)...
...


>Cancellation really operates synchronously, and that's the only form truly
>and broadly supported by the standard. With synchronous ("deferred")
>cancelability (the default for all threads), another thread can do no more
>than asynchronously POST a request, much like a pending signal mask in
>UNIX. But that's just data, and data that's not explicitly visible in the
>standard interfaces. The thread itself, at well defined execution points
>(particularly blocking calls) tests the state of that pending cancel
>flag and, if necessary and appropriate, raises the cancel exception. That
>means all deferred cancellation points, as defined by POSIX/UNIX and any
>additional C++ points, would be tagged as being able to throw the cancel
>exception.

This is also my intuition of what makes up "safe" programming:

A runtime object of any kind should have an interface around which it was
designed. Other objects may send it requests, to which it may respond via
this interface. It will then respond according how it was designed.

Of course, one can require all object to have a certain type request,
which could for example be "kill": The view of that I would take of that,
is that the "kill" request is still sent to the object in question.
Implementation wise, it could be that it is the OS that handles the kill
request.

But one should never allow some other runtime object to inject changes in
runtime object without this interface: If one allows that, one looses the
ability to do the proper checks, which may be compile time or runtime.

>There's no need for a "C++ POSIX thread binding" to support async
>cancelability. In fact, I would encourage the standards committee to
>disallow async cancelability. It's a bad idea, it's not well supported or
>particularly usable (nor useful) even for POSIX and C, and as pointed out
>here distinctly does not fit into the C++ exception model.

So the this is also the picture I arrive at:

If one should allow another thread to inject an exception of some kind
into a thread, then one must stick to this interface model: The exception
will not be an interrupt that takes over this thread, but a piece of data
labelled "exception". The receiving object (which in reality may be say
the OS) has a look at this piece of data, and then decides what to do
about it.

When I worked on this model (in a non-threaded environment), I used an
evaluator loop that decided if the request was of a general type or not:
If was of a general type, then a piece of generic code could be executed
instead of the code that belongs to the object itself. I figure that if
one has static information about what requests that may be passed to an
object, unneeded runtime code may be optimized away.

Some of the ideas I used comes from physics, how say photons are passed
around in GR (General Relativity); this may sound a bit exotic, but it a
description of the physical reality that works. :-) (A computer is similar
to GR in the sense that it attempts to eliminate QM (Quantum Mechanical)
effects.)

Hans Aberg * Anti-spam: remove "remove." from email address.
* Email: Hans Aberg <remove...@member.ams.org>
* Home Page: <http://www.matematik.su.se/~haberg/>
* AMS member listing: <http://www.ams.org/cml/>

---

Alexander Terekhov

unread,
Mar 14, 2002, 11:46:20 AM3/14/02
to

David Butenhof wrote:
>
> Alexander Terekhov wrote:
>
> > But, sorry, what does this have to do with *MY* functions?
> >
> > Are you saying that it's almost impossible to write correct
> > and valid async-cancel-safe functions (code-region) in POSIX/C,
> > C++ aside, for a moment?!
>
> Yes. At least, anything "interesting". If you create or hold resources, you
> can write "async-cancel safe" code only by DISABLING either cancellation
> (entirely) or async cancelability across that region of code.

But once you have acquired this or that resource, the
subsequent use of it (if any) COULD be made async-cancel-
safe (if it's worth doing that). The alternative is a rather
silly injection of "pthread_testcancel()" sync.cancel points
that do nothing "interesting" indeed... other than speeding
up the process of global warming! ;-)

> You can't call malloc() or free(), for example, with async cancelability
> enabled. If you did, you wouldn't be able to tell whether your memory had
> been allocated -- or whether it had truly been freed. Unless of course THEY
> were made async-cancel safe... which could be done only by disabling async
> cancel so they could determine reliably what resources THEY owned.

Yes, and Gee! I am NOT asking for async-cancel-safe
malloc()/free(), new/delete, throw-expression, whatever.

I just want to use that async-cancel "tool" for more
"efficient" programming. Well, feel free to call/label it
as "pointless optimization" if you like, but, to me, the
alternative way of injecting sync.cancel points in some
lengthy computation/search/whatever *async-cancel-safe*
regions, is, well, a rather SILLY approach that I strongly
disagree with and dislike altogether!

To some folks, exceptions are just "optimization"
of "manual" unwinding using return/status codes...
I disagree with that position/view too.

> Interrupting execution at arbitrary points, asynchronously, makes recovery
> difficult.

They are not "arbitrary points", I a way, the whole
async-cancel-region is a deferred/sync.cancel point:

http://groups.google.com/groups?as_umsgid=3C800479.CD2D66A2%40web.de
http://groups.google.com/groups?as_umsgid=3C7DE447.9A619991%40web.de

And, BTW, what do you think about the idea
of "future_std::expected_excpetion<T>()" and
interactions w.r.t thread cancelability/
finalization of cancel exception? ... some
of your loyal OpenVMS customer(s) awaiting
the response from you too! ;-)

http://groups.google.com/groups?as_umsgid=0MGc8.168872%24th4.44869006%40news02.optonline.net
http://groups.google.com/groups?as_umsgid=3C73CB8B.E764D3C6%40web.de
http://groups.google.com/groups?as_umsgid=3C7FC6E4.3AA9B580%40web.de
(hopefully you will find a few spare minutes to click&read these ;-))

> The only INTENDED purpose was so you could write a compute-bound loop, that
> held no resources, without making constant calls to pthread_testcancel(),
> and still have it responsive to cancel requests. OK, great; but, honestly,
> "big deal".

Yes, it IS *big deal* in the sense if being REALLY important, IMHO.

> It's rarely useful, mostly misused, and complicates everything

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

Ha! Isn't that the TRUE/REAL reason for your strongly
anti-async-cancel stance/position/advice? ;-)

Perhaps you've just got bored with all those countless
misuses (due to plain misunderstanding and/or LACK of
safety enforcement mechanisms in the C/C++ languages)
and have finally reached the point where you just do
not want to hear anything at all about that "nasty"
beast? ;-) ;-)

> about using and describing cancellation. We'd be better off without it.

Well, I've heard many times similar arguments against
exceptions (&cancel) in general... I strongly disagree!

[...]
> >> There's no need for a "C++ POSIX thread binding" to support async
> >> cancelability. In fact, I would encourage the standards committee to
> >> disallow async cancelability.
> >
> > Well, if I could, I would encourage the standards committee to
> > think a bit in the direction of const-like async-cancel-safety
> > *enforcement* mechanism added to the C++0x language...
>
> Sure, I would encourage them to think about it enough to realise that it's
> impossible in general and impractical and pointless even in specific cases
> where it's possible.

OK, maybe. But I have yet to see that "prove: 'impractical
and pointless even in specific cases where it's possible'"
(w.r.t async.cancel) paper! Java's RTJ/JSR-001 goes in the
OPPOSITE direction, for example, AFAICT!

[...]


> Because, as someone said, C++ is designed with the expectation that
> exceptions arise synchronously and under full language control, via throw.
> There are no asynchronous exceptions. Adding them is not trivial. I argue
> that it's not useful. I'm NOT going to argue that this shouldn't be
> considered; just that I know what the outcome should be. ;-)

Obviously, your expertise and opinion counts/weighs MORE
than anybody else's I know around in that "MT" domain,
but I (sort of newbie myself) just do NOT buy it... yet/thus
far! ;-)

Why "give up" without even trying to make it work
in some LESS error-prone/misuse-prone; MORE safe&easy
way?!

regards,
alexander.

David Butenhof

unread,
Mar 14, 2002, 2:25:42 PM3/14/02
to
Alexander Terekhov wrote:

>
> David Butenhof wrote:
> [...]
>> POSIX threads do not have parent-child relationships. All threads are
>> peers, and as nearly equal as possible. (The only real exception is that
>> a return from most threads is equivalent to pthread_exit() while a return
>> from the initial thread [main()] is equivalent to exit(), terminating the
>> process.)
>
> How about future threads-aware C++0x with "void{*} thread::main(...)"
> *alternative* (optional) w.r.t the current "exceptional" main()?

The exception for main() is solely to allow existing UNIX/POSIX/ANSI C
programs to run under a thread-enabled POSIX implementation. Changing the
behavior of main() at this point would be a total disaster. That's the same
reason that signals with a side effect (suspend, resume, terminate) are
required to act on the entire process rather than a specific thread:
without that, POSIX job control would be completely busted. We strove to
make incremental additive changes to the standard rather than suddenly and
completely replace it with something entirely different.

Even if ANSI C++ were to add an "alternative" to main(), it would need to
define what main() now means, and that had better remain the same as it
was. While there might be a few cases where people would prefer to use an
alternative with more usual thread-like behaviors, remember that helps only
for people writing main programs, not shared libraries. Perhaps enough
people would find it useful to justify the work, but that's by no means
certain. I don't think it's a big deal; calling pthread_exit() isn't so
difficult that you really need to invent a new type of main to avoid it!

>> DCE also established that converting hardware errors into exceptions was
>> a mistake; though one that could be at least partly addressed with
>> better/deeper exception support. The problem is that a SIGSEGV, for
>> example, indicates a problem with the address space. There's not much
>> hope that you can really recover from this, but exception conversion
>> implies that you can.
>
> Nope.
> http://groups.google.com/groups?as_umsgid=3C75280D.571C0C0A%40web.de
>
> "'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!"

In the original DCE exception package, which was a set of C macros (over
setjmp/longjmp), any frame with a TRY/CATCH would unwind even if it had no
interest in the exception being propagated. That is, it had to unwind just
to find out.

With real exceptions, a frame can usually test the current exception
without unwinding first. But that's not guaranteed, therefore the
"implementation defined" in your quote. Even in modern Tru64 UNIX and
OpenVMS, we continue to provide binary support for ancient DCE thread
programs using the old C macros interleaved with native exception scopes,
and that means that sometimes you need to unwind along the way.
Functionally, it's fine. It's a hassle when you need to analyze a core
file, though, because the original exception site and information is lost.

Even with nothing but true exception support, so it's never NECESSARY to
unwind, there's still a difference between "handling" and "finalizing" an
exception. I don't believe that there's a portable C++ guarantee that
destructors can/will be run without an unwind. (In fact, in general it
might be difficult to do it without unwinding.) There's also
catch/[re-]throw for more generic non-finalization handling. In the end,
the result is the same: the stack will have been unwound even though the
exception eventually reaches terminate() "unhandled" (really, not
finalized).

I suspect that as a general rule, any attempt to handle SIGSEGV is flawed,
simply because it implies uncertainty (at best) about the state of the
process address space.

In any case, though, I don't see how your response related to my comment. I
said that dealing with "a broken address space" (SIGSEGV, in particular) as
an exception has inherent uncertainty problems. You responded that it was
better not to unwind on an unhandled exception. Either you're obliquely
agreeing with me, or your response was a total nonsequitur. ;-)

>> As you say, shutting down the process quickly and with
>> minimum perturbation is best, so the problem can be analyzed. Eventually
>> DCE returned to the original idea that a SIGSEGV just dumps core and
>> terminates.
>>
>> On the other hand, other operating systems like OpenVMS, which grew up
>> with exceptions from the start, do report memory errors as exceptions.
>> Process termination occurs only when/if the memory exception propagates
>> to the base of the stack... but it still terminates the process rather
>> than an individual thread. An eventual core dump at that point may be
>> less useful, though, since the offending stack has probably been unwound.
>
> So, why do you then "propagate to the base of the stack"?!
>
> Just to find out that there is actually NO matching hander
> present?

You have to search the call stack to find out whether there's a frame with
interest in the exception. If that search involves unwinding, you're
already unwound and you can't "rewind".

The only way you can avoid the call stack search is if that "exception"
cannot be handled. And, if so, why not mandate the default UNIX behavior:
core dump immediately when the signal arrives, leaving all information
intact for analysis.

> Uhmm.. Looking at:
>
>
http://www.tru64unix.compaq.com/docs/base_doc/DOCUMENTATION/V51A_PDF/ARH9MBTE.PDF
>
> for example, I see nothing that could/would prevent to
> find this out PRIOR to "stack-unwinding". So, again,
> I guess I'm just missing something. :( :(

The DCE "exception" macros can't tell whether there's a CATCH() clause for
an exception without unwinding. So by the time you know nobody wants to
handle it, it's too late. One again, for the last time (in this posting!)
the only way to avoid that is to make the "exception" inelegible for
handling. The easiest way to do that is to not make it an exception in the
first place. ;-)

/------------------[ David.B...@compaq.com ]------------------\
| Compaq Computer Corporation POSIX Thread Architect |
| My book: http://www.awl.com/cseng/titles/0-201-63392-2/ |
\-----[ http://home.earthlink.net/~anneart/family/dave.html ]-----/

---

Jim Rogers

unread,
Mar 14, 2002, 3:51:46 PM3/14/02
to
Ian Collins wrote:

> Alexander Terekhov wrote:

>>Well, I guess that's not all that complicated/large-scale.
>>Other than thread(and async-cancel!)-safety throughout
>>the whole standard library, there are just a few areas
>>that need to be addressed, I think:
>>
>>- integrate thread-cancel/exit with C++ exception model;
>>
>
> How? Who would catch the exception? If a cancellation request resulted
> in an exception, the thread could just ignore it.
>
> Maybe some input form the Java or ADA community would help to identify
> the pros and cons of built in threads.


I can speak about the Ada concurrency model.

My first comment is that the Ada concurrency model does not integrate
thread-cancel/exit with its exception model. Cancellation or exiting
a thread is not generally considered an exceptional action.

There are some interesting scoping issues related to task
(Ada's name for threads) termination. Ada has some very strict
scoping rules. Objects cannot exist outside their specified scope.
A task object cannot cease to exist before its task has terminated.
(Temination normally occurs when the task reaches the end of its
sequence of statements.) If departure from a particular scope would
cause a task to cease to exist, then that departure cannot take
place until the object's task terminates.

There are also several task states. A task may be in progress,
abnormal, completed, or terminated. A task becomes abnormal when
an abort statement naming it is executed. A task becomes completed
when it is done performing all the actions it will ever perform and
is waiting for its dependent tasks to terminate. A task becomes
terminated when the task is completed and all its dependent tasks
are also terminated.

As far as the pros and cons of built in threads in Ada, I find
the ability very positive. Ada's tasking model is a fairly high
level abstraction of thread libraries. This allows Ada tasking
to be ported by compiler developers to the underlying thread
library of the target operating system. From an Ada
application coding point of view this provides great portability.

I have created Ada applications with complex task designs on a
PC running Win95. I could perform initial unit testing on the
host system and then cross compile to a real time operating
system (PharLap) and perform integration testing without any
changes in the source code. There was not even a hint of a
conditional compilation.

The only issue sometimes encountered in the Ada tasking model
is that some programmers may use a heavyweight solution when
a lightweight solution is more appropriate. This problem usually
occurs when a programmer is unfamiliar with some of the newer
features in the Ada 95 standard. They may continue to use the
older Ada 83 approach. Yes, some ongoing defense projects are
still using ancient compilers for maintenance of ancient code.

Along with tasks, which were defined in the 1983 standard for
Ada, the 1995 standard added protected types. When Ada uses the
term "protected" it means protected from inappropriate mutual
access by tasks. Protected types can have three kinds of
operations; functions, procedures, and entries. Protected
functions are designed to provide read-only access to the
protected object. Multiple tasks may simultaneously access a
protected object through functions. Protected procedures are
designed to be writers to protected objects. Protected
procedures are given exclusive access to the protected object.
Protected entries are designed to be writers to the protected
object, like procedures. The difference is that a protected
entry implements a monitor. The entry maintains a boundary
condition. If that condition is false the entry is blocked,
and no task may execute the entry until the condition becomes
true. Tasks waiting on a blocked entry are automatically queued
for that entry. When the condition becomes true the queue is
serviced before any new tasks are added to the queue.

It has been shown that the Ada protected type mechanism is very
efficient. For instance, you can implement a binary semaphore
using a protected object. This semaphore has been shown to be
more efficient than kernel semaphores due to lower context
switching overhead.

You want to discuss not only the creation and termination of
threads in C++. You want to discuss the semantics of communication
between threads. Ada provides two communication models between
tasks. Protected objects are used for asynchronous communication.
The Rendezvous is used for synchronous communications.
Think of the actions where one thread is sending a message to
another thread. If that communication needs to be fully
synchronized it must happen at well defined locations in each
of the thread's main control sequences. Synchronization occurs
because the first task to reach the synchronization point waits
for the other task to reach the same point. When both tasks
are at the same point they complete the communication then
continue with their independent execution. Note that this is
a bit more sophisticated than the Java join() method. The
Java join() causes one task to suspend until another has
completed.

Jim Rogers

Alexander Terekhov

unread,
Mar 14, 2002, 7:06:35 PM3/14/02
to

David Butenhof wrote:
[...]

> Even if ANSI C++ were to add an "alternative" to main(), it would need to
> define what main() now means,

ANSI C++ already does it, AFAICT.

> and that had better remain the same as it was.

Yep.

> While there might be a few cases where people would prefer to use an
> alternative with more usual thread-like behaviors, remember that helps only
> for people writing main programs, not shared libraries.

Well, I think that by the time ANSI C++ will finally acknowledge
the existence (optional) of these beasts (i.e. shared libs),
that will be C++YZ replacing C++0X (at earliest) with Y >= X ;-)

> Perhaps enough
> people would find it useful to justify the work, but that's by no means
> certain. I don't think it's a big deal; calling pthread_exit() isn't so
> difficult that you really need to invent a new type of main to avoid it!

One simple reason to "invent a new type of main" that I have in mind is
just all those countless posts to c.p.t demonstrating failures to either
join threads prior to return from main() or just pthread_exit it (i.e.
main/initial thread). Also, for example, "main()" in Java is LESS
exceptional in this respect too:

http://java.sun.com/docs/books/jls/second_edition/html/execution.doc.html#44858

"12.8 Program Exit

A program terminates all its activity and exits
when one of two things happens:

- All the threads that are not daemon threads terminate.

- Some thread invokes the exit method of class Runtime
or class System and the exit operation is not forbidden
by the security manager."

> >> DCE also established that converting hardware errors into exceptions was
> >> a mistake; though one that could be at least partly addressed with
> >> better/deeper exception support. The problem is that a SIGSEGV, for
> >> example, indicates a problem with the address space. There's not much
> >> hope that you can really recover from this, but exception conversion
> >> implies that you can.
> >
> > Nope.
> > http://groups.google.com/groups?as_umsgid=3C75280D.571C0C0A%40web.de
> >
> > "'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!"

Terminology: the 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.'"

> In the original DCE exception package, which was a set of C macros (over
> setjmp/longjmp),

OK, you probably mean something along the lines of:

ftp://ftp.opengroup.org/pub/dce122/dce/src/threads.tar.gz

"exc_handling.h¦ 46591¦05/10/96¦14:35
exc_handling.c¦ 37966¦05/10/96¦14:35"

> any frame with a TRY/CATCH would unwind even if it had no
> interest in the exception being propagated. That is, it had to unwind just
> to find out.

Question:

Are you saying that it is practically impossible (or perhaps
just pointless/silly) to try to "arrange" the C-macros/
jmp-based exception handling such that when the control
enters the try block/scope you have already "registered"
the "identity" of catch{_all} handlers in the "try context
block" structure as well... so that it could be easily
checked (i.e the presence of next matching handler
somewhere on the "stack") at *throw/re-throw* point(s),
PRIOR to "unwinding"/longjmp-ing to the next frame?

> With real exceptions, a frame can usually test the current exception
> without unwinding first. But that's not guaranteed, therefore the
> "implementation defined" in your quote. Even in modern Tru64 UNIX and
> OpenVMS, we continue to provide binary support for ancient DCE thread
> programs using the old C macros interleaved with native exception scopes,
> and that means that sometimes you need to unwind along the way.
> Functionally, it's fine. It's a hassle when you need to analyze a core
> file, though, because the original exception site and information is lost.

Yes, I am just not convinced/unsure that "implementation defined",
"not guaranteed" is really Good Thing (TM) w.r.t "unexpected"
exceptions (not to mention *required* unwinding on ES-violation)!

[...]


> In any case, though, I don't see how your response related to my comment. I
> said that dealing with "a broken address space" (SIGSEGV, in particular) as
> an exception has inherent uncertainty problems. You responded that it was
> better not to unwind on an unhandled exception. Either you're obliquely
> agreeing with me, or your response was a total nonsequitur. ;-)

Well, I just strongly believe that any exception "model"/approach
that does NOT allow to avoid "inherent uncertainty problems"
with respect to throwing UNEXPECTED exceptions, IS a "total
nonsequitur" (whatever it means... I guess: "nonsense" ;-)).

(And, BTW, I responded with "Nope" w.r.t "general"
meaning of "...but exception conversion implies
that you can." bit -- the problems/uncertainties
of "implementation defined" and/or required-on-
ES-violation stack-unwinding aside)

> >> As you say, shutting down the process quickly and with
> >> minimum perturbation is best, so the problem can be analyzed. Eventually
> >> DCE returned to the original idea that a SIGSEGV just dumps core and
> >> terminates.

But there are zillions of other exceptions (including the
ones defined in the C++ standard library, for example) which
all deserve the same treatment (in most situations, generally)!
So, why not instead do something to "fix" the whole "model"?!

[...]


> The DCE "exception" macros can't tell whether there's a CATCH() clause for
> an exception without unwinding.

I know. But is this really something that can NOT be "improved"...
to solve the problem of:

> So by the time you know nobody wants to handle it, it's too late.

<?!>

regards,
alexander.

Oh, Ah! P.S.
http://groups.google.com/groups?as_umsgid=3C77AFCB.481D2587%40web.de

David Butenhof

unread,
Mar 15, 2002, 2:32:29 PM3/15/02
to
Alexander Terekhov wrote:

No, that's not true. At least, the POSIX/C definition of async
cancelability is inherently designed to allow (and even encourage)
arbitrary interrupts. In general, it actually happens on the next clock
tick. In principle, though, this may occur at any interruptable point. On a
VAX, for exmaple, it could happen in the middle of an interruptible
instruction.

Of course, "threaded C++" could limit the unpredictability of "async"
cancelability, making it "slightly synchronous". For example, perhaps only
at program sequence points. But that means you've got to have some sort of
"throttle" mechanism, like a compiler-inserted pthread_testcancel() at the
appropriate points. The only way you gain wide-open, full throttle computes
with "free" cancellation checks is to be completely arbitrary.

> And, BTW, what do you think about the idea
> of "future_std::expected_excpetion<T>()" and
> interactions w.r.t thread cancelability/
> finalization of cancel exception? ... some
> of your loyal OpenVMS customer(s) awaiting
> the response from you too! ;-)
>
>
http://groups.google.com/groups?as_umsgid=0MGc8.168872%24th4.44869006%40news02.optonline.net
> http://groups.google.com/groups?as_umsgid=3C73CB8B.E764D3C6%40web.de
> http://groups.google.com/groups?as_umsgid=3C7FC6E4.3AA9B580%40web.de
> (hopefully you will find a few spare minutes to click&read these ;-))

I see concern about cancellation of constructors and destructors. That's a
valid issue, because they're both like and unlike malloc/free. For all the
reasons that malloc and free aren't cancellation points, they shouldn't be:
if cancellation were to strike, you couldn't tell whether you'd constructed
or destructed completely, and the program state would be a mess. On the
other hand, constructors and destructors are arbitary and possibly
prolonged chunks of user code without any of the "completability"
assurances that are expected of malloc and free, and thus SHOULD be subject
to cancellation along the same reasoning as applied to any other routine of
possibly extended duration, such as select().

I don't claim to know the right answer here. One might be to have C++ make
constructors and destructors noncancelable unless the programmer explicitly
overrides that with an attribute. "future_std::expected_exception<cancel>"
or whatever. That would run the risk of uninterruptible hanging code, but
that "should be uncommon", and most programmers wouldn't need to deal with
properly handling cancellation in those methods. This could especially be
considered a "binary compatibility" provision, because it'd be unreasonable
to require all existing pre-thread C++ code to be rewritten to deal
properly with cancellation.

None of this really has anything to do with asynchronous cancelability,
though. Even the most trivial and basic extension of POSIX semantics into
the C++ language would make it illegal to construct/destruct an object with
async cancelability enabled. To attempt anything else would be foolish.

>> The only INTENDED purpose was so you could write a compute-bound loop,
>> that held no resources, without making constant calls to
>> pthread_testcancel(), and still have it responsive to cancel requests.
>> OK, great; but, honestly, "big deal".
>
> Yes, it IS *big deal* in the sense if being REALLY important, IMHO.

Have you measured the actual cost of the pthread_testcancel() calls? You
certainly wouldn't want to call it in the inner loop of a massive array
computation, where it'd be called a million times to compute a million
values. However, in general, it's not likely to be an expensive routine.
Sure, no cost is better than moderate cost. Sometimes.

You might also consider that POSIX places no latency guarantees, or even
recommendations on async cancel. ("When cancelability is enabled and the
cancelability type is PTHREAD_CANCEL_ASYNCHRONOUS, new or pending
cancellation requests may be acted upon at any time.") ANY time. Frankly,
it'd be quite legal to ignore the async setting and defer the cancel
request to the next regular cancellation point. And, yes, that means the
cancel may never be delivered in a compute-bound loop. "Asynchronous and
arbitrary". In POSIX/C, that would be written off as a "quality of
implementation" issue. (Though which end of the scale represents "quality"
is very much in the eye of the beholder.) In C++, it might as easily be
labelled a useful feature. ;-)

>> It's rarely useful, mostly misused, and complicates everything
> ^^^^^^^^^^^^^^
>
> Ha! Isn't that the TRUE/REAL reason for your strongly
> anti-async-cancel stance/position/advice? ;-)

Sure sounds like a good reason to me.

> Perhaps you've just got bored with all those countless
> misuses (due to plain misunderstanding and/or LACK of
> safety enforcement mechanisms in the C/C++ languages)
> and have finally reached the point where you just do
> not want to hear anything at all about that "nasty"
> beast? ;-) ;-)
>
>> about using and describing cancellation. We'd be better off without it.
>
> Well, I've heard many times similar arguments against
> exceptions (&cancel) in general... I strongly disagree!

I've listened to thousands of arguments for and against all sorts of
things. That I agree with one side in a given argument has no bearing on
whether I do, or should, agree with any given side in a different argument.
I like exceptions and cancellation. I dislike asynchronous cancellation. If
you'd like to pretend that there's some inconsistency here, go ahead, but
don't expect to convince me.

> [...]
>> >> There's no need for a "C++ POSIX thread binding" to support async
>> >> cancelability. In fact, I would encourage the standards committee to
>> >> disallow async cancelability.
>> >
>> > Well, if I could, I would encourage the standards committee to
>> > think a bit in the direction of const-like async-cancel-safety
>> > *enforcement* mechanism added to the C++0x language...
>>
>> Sure, I would encourage them to think about it enough to realise that
>> it's impossible in general and impractical and pointless even in specific
>> cases where it's possible.
>
> OK, maybe. But I have yet to see that "prove: 'impractical
> and pointless even in specific cases where it's possible'"
> (w.r.t async.cancel) paper! Java's RTJ/JSR-001 goes in the
> OPPOSITE direction, for example, AFAICT!

Yes, Java and C++ have substantially more latitude in these areas than
POSIX. We couldn't change the language. We couldn't, for example, require
memory coherency without explicit synchronization, because we could only
control the semantics of the functions we defined. In many cases this is an
advantage, because going too far in that direction (as Java has), while
convenient for some programmers, can have severe performance impact on code
that doesn't require the overhead needed to maintain the language
guarantees. I'd prefer that C++ doesn't go in that direction. But then, I'm
merely contributing my opinions to the discussion. I'm not involved in the
standards committee for C++, nor do I currently intend to become involved;
so the decision is up to others.

> [...]
>> Because, as someone said, C++ is designed with the expectation that
>> exceptions arise synchronously and under full language control, via
>> throw. There are no asynchronous exceptions. Adding them is not trivial.
>> I argue that it's not useful. I'm NOT going to argue that this shouldn't
>> be considered; just that I know what the outcome should be. ;-)
>
> Obviously, your expertise and opinion counts/weighs MORE
> than anybody else's I know around in that "MT" domain,
> but I (sort of newbie myself) just do NOT buy it... yet/thus
> far! ;-)
>
> Why "give up" without even trying to make it work
> in some LESS error-prone/misuse-prone; MORE safe&easy
> way?!

Deferred cancellation is far safer. Many would argue that, if you really
want "safety", you won't even use deferred cancelability. In any case, it
would be hard to make it less "misuse prone" without making it useless for
you. For example, the runtime might defer the async cancel request to the
end of an "unsafe" region... but although the source code might look
"cleaner" than sprinkling appropriate pthread_testcancel() calls into a
deferred cancelability region, the binary code wouldn't be much different
and therefore you'd get little if any performance advantage.

Async cancel is useful, to the extent that it IS useful, precisely because
it's a "no blade guards" kind of thing. Making it safe would be much like
requiring that all knifes have dull rounded edges to avoid the risk that
some poor fool might cut himself with it.

/------------------[ David.B...@compaq.com ]------------------\
| Compaq Computer Corporation POSIX Thread Architect |
| My book: http://www.awl.com/cseng/titles/0-201-63392-2/ |
\-----[ http://home.earthlink.net/~anneart/family/dave.html ]-----/

---

Alexander Terekhov

unread,
Mar 15, 2002, 6:17:34 PM3/15/02
to

// "safe" async-cancel/bool expected_exception< cancel_e >()
"illustration"

void operation();
blabla another_operation( ...blablabla... ) async_cancel_safe;
bool yet_another_operation() throw();

void operation()
{

// cancel points DO throw here

async_cancel { // cancel type = ASYNC

// only async-cancel-safe operations
// are allowed here. Error/ill-formed
// otherwise. for example:
another_operation(); // OK

sync_cancel { // cancel type = SYNC/DEFERRED

// Back to usual

no_cancel {

// cancel points do NOT throw here

}

// Back to usual

}

// only async-cancel-safe operations
// are allowed here. Error/ill-formed
// otherwise.

no_cancel { // cancel *TYPE* = SYNC/DEFERRED

// cancel points do NOT throw here

}

// only async-cancel-safe operations
// are allowed here. Error/ill-formed otherwise.

}

// cancel points DO throw here

no_cancel {

// cancel points do NOT throw here

}

// cancel points DO throw here

}

blabla another_operation( ...blablabla... ) async_cancel_safe;
// ^^^^^^^^^^^^^^^^^
// async_cancel_safe implies "async_cancel {" scope
// restrictions/safety for body (on entry) AND
// "blablabla" parameters PLUS "blabla-return-value" too!
{

// only async-cancel-safe operations
// are allowed here. Error/ill-formed
// otherwise.

sync_cancel { // cancel type = SYNC/DEFERRED

// Back to usual

no_cancel {

// cancel points do not throw here

}

// Back to usual

}

// only async-cancel-safe operations
// are allowed here. Error/ill-formed
// otherwise.

no_cancel { // cancel *TYPE* = SYNC/DEFERRED

// cancel points do NOT throw here

}

// only async-cancel-safe operations
// are allowed here. Error/ill-formed
// otherwise.

return ...;

}

bool yet_another_operation() throw()
{

// cancel points do NOT throw here

// Does this make sense?
enable_cancel { // overrides throw(...) ES with throw( ...,cancel_e )
ES here

// cancel points DO throw here

async_cancel { // cancel type = ASYNC

// only async-cancel-safe operations
// are allowed here. Error/ill-formed
// otherwise.

sync_cancel { // cancel type = SYNC/DEFERRED

// Back to usual

no_cancel { // cancel state = DISABLE

// cancel points do not throw here

}

// Back to usual

}

// only async-cancel-safe operations
// are allowed here. Error/ill-formed
// otherwise.

}

// cancel points DO throw here

}
// required here due to enable_cancel
catch( const cancel_e& ) {

// cancel points do NOT throw here

try {

// SHOULD cancel points throw here (given catch below)?

}
// Does this make sense?
catch( const cancel_e& ) {

// cancel points do NOT throw here

}

// cancel points do NOT throw here

return false;

}

// cancel points do NOT throw here

return true;

}

regards,
alexander.

Alexander Terekhov

unread,
Mar 15, 2002, 6:26:37 PM3/15/02
to

Memory model (and Java's "not-out-of-thin-air"
partial-"safety"[1]) aside, please see the article
attached below (and http://www.rtj.org/rtsj-V1.0.pdf TOO).

regards,
alexander.

[1]
http://groups.google.com/groups?threadm=3C9086CD.96B74AE7%40genuity.com

Please respond to Jim Rogers <jimmaure...@worldnet.att.net>
Subject: Re: Ada Tasking Example


Upon starting to read the JSR/piece I noticed the name Ben Brogsol.
Ben was deeply invlolved in the creation of the Ada 95 standard.
Ben understands the Ada concurrency model very well. Some of his
influence can be seen in the table of contents:

Chapters 6, 7, and 9 as listed in the table of contents have a
distinctly Ada look to them.

Chapter 6 Synchronization:

Ada already has the ability to specify priority policies, including
a priority ceiling policy.

Chapter 7 Time

Ada already has a high resolution timer, as well as the ability to
define absolute and relative time.

Chapter 9 Asynchrony

Ada already has mechanisms for both asynchronous communication,
as well as asynchronous task control.

Jim

Alexander Terekhov wrote:

>
> Take a look at this (IBM-lead ;-)) JSR/piece:
>
> http://www.rtj.org/rtsj-V1.0.pdf
>
> "Earlier versions of the Java language supplied mechanisms for achieving
> these
> effects: in particular the methods stop() and destroy() in class Thread.
> However,
> since stop() could leave shared objects in an inconsistent state, stop()
> has been
> deprecated. The use of destroy() can lead to deadlock (if a thread is
> destroyed while
> it is holding a lock) and although it has not yet been deprecated, its
> usage is
> discouraged. A goal of the RTSJ was to meet the requirements of
> asynchronous thread
> termination without introducing the dangers of the stop() or destroy()
> methods."
>
> "This specification extends the effect of
> Thread.interrupt() and adds an overloaded version in RealtimeThread,
> offering
> a more comprehensive and non-polling asynchronous execution control
> facility. It is
> based on throwing and propagating exceptions that, though asynchronous,
> are
> deferred where necessary in order to avoid data structure corruption."
>
> Also:
>
> http://gee.cs.oswego.edu/dl/concurrency-interest/aims.html
>
> regards,
> alexander.

Garry Lancaster

unread,
Mar 15, 2002, 8:59:19 PM3/15/02
to
Garry Lancaster:

> >My thinking is that if a company the size of MS wanted
> >a standards compliant C++ compiler enough, they
> >would have one by now.

Herb Sutter:


> Microsoft is (finally, and relatively recently) committed to producing a
> 100% conformant C++ compiler, meaning not only C++98, but also tracking
the
> evolving C++0x as it begins to form, and specifically support for key
> community libraries. It won't all be in 7.1, but significant steps in that
> direction will be. It's amazing what replacing one key internal
> decision-maker a year or so ago will do to liberate a team that already
> wanted to do the right thing.

Hi Herb

(I may be putting two and two together here and getting five,
but what the hell...)

Would I be right in thinking that we have discovered the
identity of Francis's mystery new MS compiler team member?

(It's really most unsporting of Francis not to break the
confidence he's been entrusted with - he is a journalist
after all!)

Kind regards

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

---

Herb Sutter

unread,
Mar 16, 2002, 11:11:13 AM3/16/02
to
On Sat, 16 Mar 2002 01:59:19 GMT, "Garry Lancaster"
<glanc...@ntlworld.com> wrote:
>Would I be right in thinking that we have discovered the
>identity of Francis's mystery new MS compiler team member?

Yep. Info at the link below.

Herb

---
Herb Sutter (www.gotw.ca)

Secretary, ISO WG21/ANSI J16 (C++) standards committee (www.gotw.ca/iso)

Contributing editor, C/C++ Users Journal (www.gotw.ca/cuj)
C++ community liaison, Microsoft (www.gotw.ca/microsoft)

Check out "THE C++ Seminar" - Boston, March 2002 (www.gotw.ca/cpp_seminar)

---

Alexander Terekhov

unread,
Mar 17, 2002, 7:08:42 PM3/17/02
to

Herb Sutter wrote:
>
> On Sat, 16 Mar 2002 01:59:19 GMT, "Garry Lancaster"
> <glanc...@ntlworld.com> wrote:
> >Would I be right in thinking that we have discovered the
> >identity of Francis's mystery new MS compiler team member?
>
> Yep. Info at the link below.
>
> Herb
>
> ---
> Herb Sutter (www.gotw.ca)
>
> Secretary, ISO WG21/ANSI J16 (C++) standards committee (www.gotw.ca/iso)
> Contributing editor, C/C++ Users Journal (www.gotw.ca/cuj)
> C++ community liaison, Microsoft (www.gotw.ca/microsoft)

http://www.codeproject.com/interview/herbsutter3032002.asp

"Do you really believe that .NET is good for C++? Why?

I do. ....

Here's a for-instance: Within the standards committee and the
standards process, I've seen several key recurring questions
and wishes since C++98 was passed. Three of them are:
a) "what about standardizing thread support?";
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
....
If we could get better convergence between "Managed C++" and
"Standard C++," which is something that the team as a whole
and Stan Lippman in particular are working on right now, this
whole thing could get really interesting and there might be
some useful standard technology to offer here someday if
the C++ committee wants any parts of it
....
But C++ developers on all platforms wants these kinds of
things - threads, running on a VM, a managed GUI library.
^^^^^^^
Windows-based developers have a lot of it today, and I
know that the Visual Studio .NET team are working hard
to make it better so that you don't have to give up so
much of Standard C++ to get those benefits."

And I know what I should do with this thread now.

Thank you, Herb. But, frankly, did I somehow manage
to miss your response to:

http://groups.google.com/groups?as_umsgid=3C7BD6B3.1EAE29D0%40web.de

"- MS have made their threading "standard" already:
....
more info:

http://www.cs.umd.edu/~pugh/java/memoryModel/archive/0938.html"
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

I mean:

"....They designed to ignore much of the advice I gave them.
....Plus, the spec is ambiguous in the extreme and has holes
you could drive a truck through. "

<?>

Well, OK, but hopefully, I won't miss your response to my recent
"H.Sutter/S.Lippman/Microsoft/MS.Net (was Re: C++ and threads)"
comp.lang.c++ posting... TIA!

regards,
alexander.

Hillel Y. Sims

unread,
Mar 18, 2002, 1:46:44 AM3/18/02
to

"David Butenhof" <David.B...@compaq.com> wrote in message
news:cCnk8.1010$fL6....@news.cpqcorp.net...

> Alexander Terekhov wrote:
>
> > David Butenhof wrote:
> >>
> >
>
http://groups.google.com/groups?as_umsgid=0MGc8.168872%24th4.44869006%40news

Here is my viewpoint based on some experience (and hopefully a bit more
since writing some of the prior messages referenced above) using OpenVMS 7.3
and Compaq C++ 6.2-6.5, which seems to be a particularly well-implemented
platform in terms of meshing C++ and threads (I believe the behavior is
similar on Tru64 Unix). I personally am only interested in synchronous
cancellability, which basically raises a /synchronous/ 'thread-cancel'
exception on this C++ implementation (as opposed to async-cancel, which most
everyone agrees would be quite a barrel of monkeys, at the least, and I
personally would probably not be too terribly concerned if it was not
synthesized into C++0x.). This synchronous exception seems to be raiseable
/only/ at the same type of well-defined points from which any typical
standard C++ exception can be thrown (unless I am slightly misunderstanding
the implementation).* (Also, exceptions are thread-specific and thread-safe
on this implementation, which is a good thing; though accvios do terminate
the whole process...)

There seems to be no need to be concerned about cancellation in
constructors, since it is perfectly valid for constructors to throw
exceptions in C++ anyhow. Since operator new() would likely not be a
cancellation point (by the same rationale as malloc), there would be no
concern about not being able to know whether memory had been allocated yet
or not (it would have, as for any other ctor-based exception).

Destructors are not allowed to throw exceptions, so cancellation must be
prevented from being triggered during a destructor. In practice, for many
trivial destructors (such as deleting some pointer or automatically
destructing subobjects) it's not even an issue since no cancel-points are
directly involved. For non-trivial destructors (I'd say pretty much calling
any other functions, eg even fclose() is a potential cancel-point) as well
as any other nothrow code regions, I am advocating within my company the use
of a stack-based guard object I call CancelGuard which simply wraps an
automatic pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,
&origState)/pthread_setcancelstate(origState, 0) pair around the main
destructor body to prevent cancellation from being triggered (and thus no
thread-cancel exception can be thrown during the destructor!). Although I
can see how this could be a bit of a concern about infinitely preventing
cancellation, this seems safer than worse undefined behavior of catch(...)
{} potentially swallowing a thread-cancel... Also, in case of some kind of
runaway code in deferred cancellation mode, it seems a fair possibility that
cancellation may never be triggered by the runaway thread anyhow, even if
its not disabled. Lastly (as I mentioned above), not just destructors, but
any region of nothrow code that could potentially trigger cancel-points
needs to be protected with CancelGuard, so it might not be totally helpful
for the implementation to just implicitly disable cancellability in all
destructors.

(http://www.slack.net/~ant/cpp/asyncexcept.html previously suggested
"no_throw_region" concept, which is sort of similar to CancelGuard, though
mine is only for synchronous exceptions and far simpler and workable with
standard C++ -- CancelGuard is only required to protect standard nothrow
code regions from cancellation, vs no_throw_region is apparently required
almost everywhere for async safety!)

>
> I've listened to thousands of arguments for and against all sorts of
> things. That I agree with one side in a given argument has no bearing on
> whether I do, or should, agree with any given side in a different
argument.
> I like exceptions and cancellation. I dislike asynchronous cancellation.
If
> you'd like to pretend that there's some inconsistency here, go ahead, but
> don't expect to convince me.

> >> Because, as someone said, C++ is designed with the expectation that


> >> exceptions arise synchronously and under full language control, via
> >> throw. There are no asynchronous exceptions. Adding them is not
trivial.
> >> I argue that it's not useful. I'm NOT going to argue that this
shouldn't
> >> be considered; just that I know what the outcome should be. ;-)

It seems to me that is that it is possible to arrive at a C++ implementation
where exceptions and /synchronous/ cancellation are fully compatible idioms,
with minimal impact on the code, based on my experience with OpenVMS 7.3 and
Compaq C++ 6.2-6.5 implementation -- BUT, there are two main rules which
must be followed throughout the code:
1) always re-"throw;" from catch(...) (except in dtors, but see 2) to
prevent thread-cancel from being "swallowed" -- however, hopefully everyone
already follows this paradigm in general practice, so it shouldn't be too
much of an issue.
2) Proper consideration must be given for deferring cancels in nothrow code,
to avoid the hairy problem of triggering an exception which is not allowed
to be swallowed. In case of code which potentially triggers a cancel-point
function, I believe CancelGuard is a fairly easy-to-use and effective
technique:

T::~T()
{
CANCEL_GUARD; // convenience macro that expands to 'CancelGuard
CANCELGUARD_<LINE#>'
fclose(m_pFile);
}

Of course, this requires yet additional consideration on the part of
developers in an already-complicated language. I might wonder if, for
convenience and safety, thread-aware C++ implementation could possibly
implicitly implement CANCEL_GUARD around sections of nothrow/noncancellable
code, if it can actually determine that the code may not be cancelled... but
how -- via throw()? probably not...

In any case, my viewpoint is that C++ exceptions and thread-cancellation CAN
live together in harmony (and Compaq implementation proves it) as long as
both of these techniques are followed -- all existing exception-safe code
can be made synchronous-cancel-safe (under this type of throwing
implementation) by properly adhering to both of these rules. (and well, none
of the C++ system libraries on this platform use CancelGuard technique
(yet), as far I can tell... ;-)

Hope this is helpful in some way...

thanks for your consideration,
Hillel Y. Sims
hsims AT factset.com

* Although the 'thread-cancel exception' is not actually a C++ exception, it
does cause stack-unwinding to proceed normally, though on this platform it
does not trigger catch(...) blocks [may be a bug or oversight in the
implementation?]... which really I don't even care so much, because a) I
don't want catch(...) to be able to swallow the thread-cancel event, b) I am
strongly against use of any exceptions other than inherited from
std::exception hierarchy in order to avoid collision with any other random
system events that may trigger catch(...), such as accvio, where I want the
exception to remain fully unhandled anyhow and no stack-cleanup to be
performed so I get a clean stack trace [well technically that's
implementation-defined behavior too, but it seems everyone agrees that only
a poor implementation actually cleans up the stack on an unhandled
exception; on this platform it does not unwind the stack if exception is
unhandled]. On the other hand, it does seem like it probably should be
integrated with catch(...) for total convergence; but as long as everyone
always does re-"throw;" from catch(...) it won't be a problem anyhow (except
for dtors, but that's not a problem either if CANCEL_GUARD is used)

David Butenhof

unread,
Mar 18, 2002, 6:16:11 PM3/18/02
to
Hillel Y. Sims wrote:

> * Although the 'thread-cancel exception' is not actually a C++ exception,
> it does cause stack-unwinding to proceed normally, though on this platform
> it does not trigger catch(...) blocks [may be a bug or oversight in the
> implementation?]...

Not really either. That is, C++ was correctly and reasonably designed to
run destructors on native (non C++) platform exceptions, and so it works
for POSIX thread exceptions -- exit and cancel. That was automatic and
transparent both to the thread library and C++. That's obviously the way it
OUGHT to work, and so everyone was pretty much happy.

At one point, we tried to work with the C++ team to develop a way for C++
programmers to catch() cancel and exit by name -- for various reasons, that
never shipped.

> which really I don't even care so much, because a) I
> don't want catch(...) to be able to swallow the thread-cancel event, b) I
> am strongly against use of any exceptions other than inherited from
> std::exception hierarchy in order to avoid collision with any other random
> system events that may trigger catch(...), such as accvio, where I want
> the exception to remain fully unhandled anyhow and no stack-cleanup to be
> performed so I get a clean stack trace [well technically that's
> implementation-defined behavior too, but it seems everyone agrees that
> only a poor implementation actually cleans up the stack on an unhandled
> exception; on this platform it does not unwind the stack if exception is
> unhandled].

Not entirely true, actually. On OpenVMS, the POSIX thread library has to
'catch' all exceptions in any call frame with a TRY/CATCH block, and
RERAISE those it doesn't want, since the VMS C language lacks the Tru64
extensions for native exception support. However, unless your C++ code is
interleaved with C code using the TRY/CATCH macros (or improper C++ code
that uses those C-specific macros!), this wouldn't be a problem for you.

> On the other hand, it does seem like it probably should be
> integrated with catch(...) for total convergence; but as long as everyone
> always does re-"throw;" from catch(...) it won't be a problem anyhow
> (except for dtors, but that's not a problem either if CANCEL_GUARD is
> used)

Right.

/------------------[ David.B...@compaq.com ]------------------\
| Compaq Computer Corporation POSIX Thread Architect |
| My book: http://www.awl.com/cseng/titles/0-201-63392-2/ |
\-----[ http://home.earthlink.net/~anneart/family/dave.html ]-----/

---

Hillel Y. Sims

unread,
Mar 19, 2002, 11:42:45 AM3/19/02
to
Just another thought, maybe a qualifier keyword (ha!) that could be applied
to a function or block ("nocancel {}"?) to protect it from cancel would be
better than "CANCEL_GUARD"? It could easily be implemented in the same way,
probably even more efficiently based on various circumstances (should be
really easy to optimize away to nothing, if it can determine that no cancels
can occur or on non-threaded systems). Ok, of course I am thinking mainly of
pthread-like semantics about this whole issue, I don't really know Windows
threading, so if it can't even support injecting thread-specific exceptions
on a /synchronous/ basis for thread cancellation, that could be a problem
for the feasibility of it on a 'standard' basis I guess.

My experience today with CANCEL_GUARD was that I went back and applied it as
needed for full cancel safety in a database manipulation package I wrote a
few months back (~6500 lines of code) before I thought of this technique,
and only ended up needing to insert one CANCEL_GUARD to protect the entire
library. I am a big fan of C++ exceptions (one of the areas where C++ truly
crushes Java and C# :-).

"David Butenhof" <David.B...@compaq.com> wrote in message

news:5Gml8.1103$fL6....@news.cpqcorp.net...


> Hillel Y. Sims wrote:
>
> > * Although the 'thread-cancel exception' is not actually a C++
exception,
> > it does cause stack-unwinding to proceed normally, though on this
platform
> > it does not trigger catch(...) blocks [may be a bug or oversight in the
> > implementation?]...
>
> Not really either. That is, C++ was correctly and reasonably designed to
> run destructors on native (non C++) platform exceptions, and so it works
> for POSIX thread exceptions -- exit and cancel. That was automatic and
> transparent both to the thread library and C++. That's obviously the way
it
> OUGHT to work, and so everyone was pretty much happy.

It has been working correctly for me. It seems to be one of the best
implementations in that aspect.

>
> At one point, we tried to work with the C++ team to develop a way for C++
> programmers to catch() cancel and exit by name -- for various reasons,
that
> never shipped.

There seems to be a difference between pthread exceptions though and other
os exceptions (such as accvio) on VMS, that even catch(...) blocks will
usually be triggered for the others but seems to never trigger on the
pthread exception -- but dtors are always run for every frame on a pthread
exception, whereas they are only run for the other os type exception if it
is handled somewhere (including FINALLY/CATCH blocks). I seem to have gotten
some indication that it may work slightly differently on Tru64(catch(...) is
activated and dtors are run?). In either case, I am happy so long as my
dtors are run in non-fatal situations, and I can easily work around
catch(...).

>
> > which really I don't even care so much, because a) I
> > don't want catch(...) to be able to swallow the thread-cancel event, b)
I
> > am strongly against use of any exceptions other than inherited from
> > std::exception hierarchy in order to avoid collision with any other
random
> > system events that may trigger catch(...), such as accvio, where I want
> > the exception to remain fully unhandled anyhow and no stack-cleanup to
be
> > performed so I get a clean stack trace [well technically that's
> > implementation-defined behavior too, but it seems everyone agrees that
> > only a poor implementation actually cleans up the stack on an unhandled
> > exception; on this platform it does not unwind the stack if exception is
> > unhandled].
>
> Not entirely true, actually. On OpenVMS, the POSIX thread library has to
> 'catch' all exceptions in any call frame with a TRY/CATCH block, and
> RERAISE those it doesn't want, since the VMS C language lacks the Tru64
> extensions for native exception support. However, unless your C++ code is
> interleaved with C code using the TRY/CATCH macros (or improper C++ code
> that uses those C-specific macros!), this wouldn't be a problem for you.

yup, I understand (and we've just discovered the effects of that interaction
a few days ago), but C++ tries to do the best it can - though we do have
lots of "improper C++ code" with TRY/CATCH throughout our system. We're
thinking to try to workaround it in our custom VMS exception-handler by
storing the stack trace of the first call into the exception handler if it
is a C++ exception and then following it back through the stack and
substituting the original trace if it turns out to be the root cause of
abnormal exit. Improper C++ code, haha.. I get just a bit of a douche chill
every time I see the part of the docs that basically says "TRY/CATCH is not
valid for C++. Use that platform's native exception handling facility
instead." Much of this code comes from ~1992/1993, did they even have
exceptions back then? (fortunately we didn't use too many custom exception
types so we can live with it, most of it is for handling threads or fatal
errors) Though there is probably benefit to not having started too early
with exceptions and polluted our code with poor designs.

>
> > On the other hand, it does seem like it probably should be
> > integrated with catch(...) for total convergence; but as long as
everyone
> > always does re-"throw;" from catch(...) it won't be a problem anyhow
> > (except for dtors, but that's not a problem either if CANCEL_GUARD is
> > used)
>
> Right.

very cool.

thanks,
hys

--


Hillel Y. Sims
hsims AT factset.com

---

John Nagle

unread,
Mar 20, 2002, 11:16:55 AM3/20/02
to
Suppose that raising an exception in another
thread only took effect when the thread made some
test. That's one commonly used approach that
doesn't seem to require anything in the standard.
But it does, because the libraries can block.

Everything in the libraries that can block
for a nontrivial period thus needs to test for
an external exception request.

That's the justification for standardization.

This would be quite useful.
Often, stuck threads are stalled waiting
for some event or input that will never come, and there's
no standard way to break them out of such things
cleanly. There should be.

John Nagle
Animats

Hillel Y. Sims

unread,
Mar 21, 2002, 8:36:38 AM3/21/02
to

"John Nagle" <na...@animats.com> wrote in message
news:3C98417...@animats.com...

> Suppose that raising an exception in another
> thread only took effect when the thread made some
> test. That's one commonly used approach that
> doesn't seem to require anything in the standard.
> But it does, because the libraries can block.
>
> Everything in the libraries that can block
> for a nontrivial period thus needs to test for
> an external exception request.
>
> That's the justification for standardization.
>
> This would be quite useful.
> Often, stuck threads are stalled waiting
> for some event or input that will never come, and there's
> no standard way to break them out of such things
> cleanly. There should be.

A limited set of library routines (at minimal some threading facilities such
as join/wait/testcancel) could be defined to be standard synchronous
cancellation-points (ala the pthreads model), with additional flexibility
for implementation-specific cancel-points (may be non-C++ apis, etc.) - a
variety of the C-based standard library functions (fopen/fclose/printf/...)
already are considered "standard" cancel-points on certain platforms. A
synchronous exception should be raised in the target thread by the runtime
implementation at any of these appropriate points if a cancel request has
been posted by an alternate thread (or even itself?).

A mechanism needs to be available for code to specify that certain regions
are noncancellable (basically any nothrow zones, dtors), even if it needs to
call a potentially cancelling function. "throw()" is not likely to cut
it(?). One way of achieving this might be for the target thread to just be
able to swallow the thread-cancel exception by typename, but that would have
fairly undesirable side-effects (although the caller thread can never really
force the target thread to exit, it deserves at least a fighting chance just
because it happened to post the request at a bad time, which could be a
common scenario on multithreaded servers). thread-cancel exception type
should probably be defined outside std::exception heirarchy, so users of
standard exceptions would not accidentally intercept it (in my view, only
catch (...) should really even be able to see it, this would be safest since
it must always rethrow anyway). The pthreads model provides
cancellation-deferral mode (can be nested), to prevent thread-cancel
exception from being raised while it is active (which can easily be wrapped
in a C++ guard object such as my simple example) which can then selectively
be applied by programmers to properly enforce cancel/nothrow-safety (for
both library and application code). A C++ library facility could provide
similar support (and the entire standard library can probably be retrofitted
to be cancel-safe) -- it would simply evaluate to noops on platforms that
don't support threading.

Aspects of this could probably come out somewhat simpler with better
integrated syntax support, but all of it can work reasonably well even with
plain C++98. Are there any current threaded platforms for which this is
simply infeasible?

thank you,


hys
--
Hillel Y. Sims
hsims AT factset.com

---

Hans Aberg

unread,
Mar 21, 2002, 11:43:18 AM3/21/02
to
In article <3C98417...@animats.com>, John Nagle <na...@animats.com> wrote:
>Suppose that raising an exception in another
>thread only took effect when the thread made some
>test. That's one commonly used approach that
>doesn't seem to require anything in the standard.
>But it does, because the libraries can block.
>
> Everything in the libraries that can block
> for a nontrivial period thus needs to test for
> an external exception request.
>
>That's the justification for standardization.

This corresponds to the model I have in my mind, as follows:

Threads communicate with each other by sending "signals", or handing over
references to objects. Incoming objects to a thread is put in a priority
queue.

>From this point of view, exceptions are merely objects with a higher priority.

>This would be quite useful.
>Often, stuck threads are stalled waiting
>for some event or input that will never come, and there's
>no standard way to break them out of such things
>cleanly. There should be.

In order for the compiler to be able to make a static analysis of what a
thread can compute, one still must have a C++ static language description
of what exceptions the thread can accept.

This can include special universal objects like "kill" which are executed
by the thread at a high priority. Of course, even though that the semantic
model is that the "kill" object is being sent to a thread in question,
there is nothing that prevents that the actual implementation is by the OS
examining objects before they are being sent to the threads, intercepting
the "kill" objects for proper handling.

In this picture, it is still not possible for a thread to send any
exception or signal to any other thread: In order to get around this
problem, working with runtime objects (even though not threaded at this
point in time in my implementation), runtime objects must have a generic
or default objects for in coming references for which there is no special
treatment. This generic action differs from the type or class of types
that the executing object belongs to.

If I should translate this idea to the current context then it would be
like say all exceptions must be derived from a class, say std::exception,
and in all those that are considered signals are derived from say a class
std::signal.

The execution might be that if the thread receives a reference
std::exception derived object which its C++ static exception declaration
does not recognize, then the exception is merely ignored. Thus, for
example, a thread which is marked as not throwing exceptions will never
throw any exceptions, no matter what exceptions are sent to it at runtime.

A reference to a std::signal derived object will be sent first to the OS
to examine whether it is a kill or something. If the OS does not handle
it, then it is sent to the thread, which will handle it according how to
it is being implemented. Of course, the thread may have a C++ static
declaration that reveals that it does not handle std::signal derived
objects, in which case the signal is merely being ignored.

Hans Aberg * Anti-spam: remove "remove." from email address.
* Email: Hans Aberg <remove...@member.ams.org>
* Home Page: <http://www.matematik.su.se/~haberg/>
* AMS member listing: <http://www.ams.org/cml/>

---

David Butenhof

unread,
Mar 22, 2002, 11:55:20 AM3/22/02
to
Hillel Y. Sims wrote:

>
> "John Nagle" <na...@animats.com> wrote in message
> news:3C98417...@animats.com...
>> Suppose that raising an exception in another
>> thread only took effect when the thread made some
>> test. That's one commonly used approach that
>> doesn't seem to require anything in the standard.
>> But it does, because the libraries can block.
>>
>> Everything in the libraries that can block
>> for a nontrivial period thus needs to test for
>> an external exception request.
>>
>> That's the justification for standardization.
>>
>> This would be quite useful.
>> Often, stuck threads are stalled waiting
>> for some event or input that will never come, and there's
>> no standard way to break them out of such things
>> cleanly. There should be.
>
> A limited set of library routines (at minimal some threading facilities
> such as join/wait/testcancel) could be defined to be standard synchronous
> cancellation-points (ala the pthreads model), with additional flexibility
> for implementation-specific cancel-points (may be non-C++ apis, etc.)

No standard can specify the behavior of functions that aren't in the
standard. So there's always that much wiggle room for
"implementation-specific" behavior, cancelation points or anything else.

HOWEVER, the standard cannot allow for unrestricted implementation-defined
behavior in STANDARD functions.

POSIX has two lists of cancelation points. One is of functions that the
standard REQUIRES to be cancellation points. If a cancel request is
pending, and cancelability is enabled when a thread makes one of these
calls, and cancellation doesn't occur, then the implementation is broken.
Period.

The second list is of OPTIONAL cancellation points. The distinction is,
roughly, between what are conventionally syscalls into the kernel
(required) vs what are conventionally user-space C runtime functions that
MIGHT use those syscalls. The intent was that library calls which might use
the syscalls be allowed to check for cancellation only when they actually
make the kernel calls; e.g., when printf() flushes its buffer by calling
write(). In practice, the optional list is more often used as an excuse to
avoid recoding the library to deal with cancellation, instead disabling it
around the syscall. This is all quite convenient for implementations, of
course, but bad for applications. All applications that use any of these
functions must be prepared to react correctly should cancellation occur at
any such call... but at the same time may not portably DEPEND on
cancellation occurring.

ALL other functions defined by POSIX are REQUIRED to NOT be cancellation
points. Any application may safely call these functions without preparing
for cancelation. Think what it'd mean if unrestricted implementation
defined behavior was allowed. EVERY function defined by the standard MIGHT
deliver a cancel... so the application needs to defend itself... but it can
never DEPEND on any being cancellable.

The Single UNIX Specification extends both lists with SUS functions that
aren't in POSIX (such as select()), but maintains the same rule. Any
standard function that's not on either list CANNOT be a cancellation point.

ANSI C++, if it recognizes or support cancellation at all, must follow
these guidelines unless it really intends to require that all applications
defend against cancellation on every standard method invocation. And that
would be a really bad idea. There must be a particular list of cancellation
points. It may also add a second list of optional cancellation points; but
those lists should be relatively small compared to the full suite of
functions in the standard, to be manageable for application developers.
And, most importantly, the standard must absolutely require that no other
standard function be cancellable. (Because of course any function that
violates this rule is really on the "may be cancellable" list, and if that
includes most of the standard then application developers will have a
really hard time using it.)

> - a
> variety of the C-based standard library functions
> (fopen/fclose/printf/...) already are considered "standard" cancel-points
> on certain platforms. A synchronous exception should be raised in the
> target thread by the runtime implementation at any of these appropriate
> points if a cancel request has been posted by an alternate thread (or even
> itself?).

Cancellation is anonymous (to the receiver, anyway). Yes, a thread can (and
sometimes does) cancel itself. It makes no difference to the posting or
delivery of the cancel request. (And pthread_cancel() isn't a cancellation
point.)

For what it's worth, the specific C functions you mention are all on the
optional cancellation point list. So they ARE "standard cancel-points", but
whether they actually cause cancellation delivery is indeed implementation
(and maybe context) specific. Thus, again, you can't count on it, but you
always need to defend yourself. And to again overemphasize the point,
that's convenient lattitude for the platform developers but really annoying
for the application programmers.

> A mechanism needs to be available for code to specify that certain regions
> are noncancellable (basically any nothrow zones, dtors), even if it needs
> to call a potentially cancelling function. "throw()" is not likely to cut
> it(?). One way of achieving this might be for the target thread to just be
> able to swallow the thread-cancel exception by typename, but that would
> have fairly undesirable side-effects (although the caller thread can never
> really force the target thread to exit, it deserves at least a fighting
> chance just because it happened to post the request at a bad time, which
> could be a common scenario on multithreaded servers).

"Swallowing" a cancel, except by deliberate application intent, is bad.
It's OK to hold it pending indefinitely, but that's different. The
cancellation pond is "catch and release" fishing. You don't have to fish:
but if you choose to fish, you need to release everything you catch back
into the water.

There are exceptions, and building cancellation on, well, exceptions,
allows a clean implementation in such cases. The original application was
DCE. DCE was designed to propagate a cancel across the RPC link from server
to client, unwinding the "virtual call stack". (Not necessarily a good idea
since they're in different address spaces, but that's beside the point.)
However, although the server routine might be cancelled, the server thread
would remain to run other instances. Therefore, the "wrapper" would catch
(and finalize) any exception (including cancel) raised by the RPC server
routine, and propagate it through the comm link to the client, while
continuing to run normally. There are very few examples where a scheme like
this makes any sense.

> thread-cancel
> exception type should probably be defined outside std::exception
> heirarchy, so users of standard exceptions would not accidentally
> intercept it (in my view, only catch (...) should really even be able to
> see it, this would be safest since it must always rethrow anyway).

I disagree. It is and should be a normal exception, and inside the standard
class hierarchy. NOBODY has any business "finalizing" (catching and not
releasing) an exception they don't know by name. If you can't identify the
exception, you can't know why it was raised or what it means. And if you
don't understand what it means, you cannot possibly know that it doesn't
need to be handled further down the stack. Therefore, if you really need to
catch it for some reason, you MUST re-throw. This should apply to any
exception not caught using a "leaf" name. Anything caught by superclass
designation (much less anything as generic as std::exception or catch(...))
should always be re-thrown. (Except in "weird" upside down call structures
like the RPC client.)

Nevertheless, the standard should specify mechanism, not policy. To say
"thou shalt not catch cancel because I don't think you should" is mandating
the committee's collective consensus of "proper behavior", and that's at
best inappropriate. There are always "exceptions". If some standard
application really does need to catch and finalize "all exceptions",
there's absolutely no justification for making cancel or thread exit any
different from all the others. They SHOULD be in the std::exception
hierarchy, though perhaps in a separate "thread" subclass hierarchy.

/------------------[ David.B...@compaq.com ]------------------\
| Compaq Computer Corporation POSIX Thread Architect |
| My book: http://www.awl.com/cseng/titles/0-201-63392-2/ |
\-----[ http://home.earthlink.net/~anneart/family/dave.html ]-----/

---

Garry Lancaster

unread,
Mar 22, 2002, 1:55:09 PM3/22/02
to
Hillel Y. Sims:

> > thread-cancel
> > exception type should probably be defined outside std::exception
> > heirarchy, so users of standard exceptions would not accidentally
> > intercept it (in my view, only catch (...) should really even be able to
> > see it, this would be safest since it must always rethrow anyway).

David Butenhof:


> I disagree. It is and should be a normal exception, and inside the
standard
> class hierarchy. NOBODY has any business "finalizing" (catching and not
> releasing) an exception they don't know by name. If you can't identify the
> exception, you can't know why it was raised or what it means. And if you
> don't understand what it means, you cannot possibly know that it doesn't
> need to be handled further down the stack. Therefore, if you really need
to
> catch it for some reason, you MUST re-throw. This should apply to any
> exception not caught using a "leaf" name. Anything caught by superclass
> designation (much less anything as generic as std::exception or
catch(...))
> should always be re-thrown. (Except in "weird" upside down call structures
> like the RPC client.)

First of all, I would like to say how interesting and
insightful I have found this discussion.

However, I don't think you are quite right with regard
to catching normal exceptions. We are given guarantees
about the state of a system in the face of exceptions
in the form of one of the exception-safety-guarantees
(strong, basic or no-throw).

Say we have a class that offers the strong exception
safety guarantee for all its member functions. We then
know that if any of these functions throw, then the state
of the class will be just as it was before the function
call that caused the throw. In some situations, this
is all we are interested in.

class UserAlerter
{
public:
// Can throw. Offers strong exception safety guarantee.
void AlertUser(const char* msg);
};

void foo()
{
UserAlerter ua;

try
{
ua.AlertUser( "This is an optional message." );
}
catch(...) {}
}

The catch(...) is OK, since it is not a fatal error if
AlertUser fails for our optional message. Instead of
ignoring the failure, we could fall back on an
alternative alerting technique. In either case, the state of
the system is known (because of the strong exception
safety guarantee in effect) and there is no absolute
requirement to rethrow the exception.

In the absence of an exception safety guarantee
I would agree that exceptions need to be thrown
all the way up to a handler that has intimate
knowledge of the exact kind of exception. But with
the exception safety guarantee this is unnecessary.

> Nevertheless, the standard should specify mechanism, not policy. To say
> "thou shalt not catch cancel because I don't think you should" is
mandating
> the committee's collective consensus of "proper behavior", and that's at
> best inappropriate. There are always "exceptions". If some standard
> application really does need to catch and finalize "all exceptions",
> there's absolutely no justification for making cancel or thread exit any
> different from all the others. They SHOULD be in the std::exception
> hierarchy, though perhaps in a separate "thread" subclass hierarchy.

I agree this is a solution to keep most people
happy.

namespace std
{
class exception {...};
class thread_cancel_exception : public exception {...};
class base_for_other_exceptions : public exception {...};
....
};

or some such thing.

Kind regards

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

---

Alexander Terekhov

unread,
Mar 23, 2002, 8:25:00 AM3/23/02
to

David Butenhof wrote:
[...]

> I disagree. It is and should be a normal exception, and inside the standard
> class hierarchy.

Yup (but please see below ;-)).

> NOBODY has any business "finalizing" (catching and not
> releasing) an exception they don't know by name. If you can't identify the
> exception, you can't know why it was raised or what it means. And if you
> don't understand what it means, you cannot possibly know that it doesn't
> need to be handled further down the stack. Therefore, if you really need to
> catch it for some reason, you MUST re-throw. This should apply to any
> exception not caught using a "leaf" name. Anything caught by superclass
> designation (much less anything as generic as std::exception or catch(...))
> should always be re-thrown. (Except in "weird" upside down call structures
> like the RPC client.)

C++ exception specs (ES) allow to "safely" catch everything
EXPECTED (listed in the ES, and hopefully, mother-of-all-
exceptions and alike won't be listed there ;-)) using
base-class(es) and even catch(...) (with an opportunity
to translate something known but unexpected in the
unexpected() handler... or just terminate() the whole
thing on ES violation, which is GOOD (silly unwinding
"up-to-catch(...)-detectable-violation-point" aside),
I think).

Actually, I don't think that there is much sense to
catch/re-throw anything, in general... Why should the
local context cleanup/fixup be depended on the actual
nature of propagating exception? I think that, instead,
everything should be done in local objects destructors,
and/or perhaps in some "better" version of "finally"
clauses (I mean full access to local context but
templatized, if that would/could be possible):

http://groups.google.com/groups?threadm=3C73AB86.99B8CBE0%40web.de

Uhmm, BTW, could someone please clarify for me THIS:

"C::C(int ii, double id)
try
: i(f(ii)), d(id)
{
// constructor function body
}
catch (...)
{
// handles exceptions thrown from the ctor initializer
// and from the constructor function body
}
—end example]"

a) why do you folks (I mean committee) just insist on
unwinding (even WITHOUT some proper handler meant to
perform a real RECOVERY; not some local context cleanup/
fixup), I mean:

"The exception being handled is rethrown if control
reaches the end of a handler of the function-try-block
of a constructor or destructor."

<?> Thanks!

b) even with that auto-re-throw, what am I supposed to do
(useful, in general, I mean) given that:

"The fully constructed base classes and members of an
object shall be destroyed before entering the handler
of a function-try-block of a constructor or destructor
for that object."

<?> Thanks!

Also, if someone really need it, I think that C++ could
provide something like "const T* unhandled_exception< T >()"
to allow exception-type sensitive local cleanup/fixup
WITHOUT that silly "catch/re-throw" technique, IMHO.
(in addition to "fixing" things like unwinding on ES
violation and function-try-block "handlers" of
c-tors/d-tors... unless I am missing something BIG,
which somehow would explain to me the USEFULNESS
and RATIONALE behind these "ideas").

> Nevertheless, the standard should specify mechanism, not policy. To say
> "thou shalt not catch cancel because I don't think you should" is mandating
> the committee's collective consensus of "proper behavior", and that's at
> best inappropriate.

Ok. But the committee should then define some "mechanism"
to bring things back "in-order":

http://groups.google.com/groups?as_umsgid=3C7FC6E4.3AA9B580%40web.de

"On the other hand, you are really (most likely)
want/need to get things back as usual, in-order:

state: ENABLED
type: DEFERRED
============== ================
cancelability: ENABLED/DEFERRED"

AFTER finalizing that standard cancel exception.

Or is that just as simple as calling pthread_setcancelstate
with "ENABLED"? How about cancel-type? In POSIX/C cancel-
exception could be raised from some async-cancel-region...
(and hopefully we will get a better (less error-prone)
support for async-cancel in the next/threaded C++ ;-))

Should I also call pthread_setcanceltype with DEFERRED
(prior to state change), in order to be "safe"? Or am I
totally missing something with respect to cancel
"finalization" in the user code?

Anyway, how about pthread_exit? Do you think that
it should be allowed (be NOT "undefined") to catch
and finalize THAT exception too? How about exit
"value" then? Who is going to take care of it? What
happens on another pthread_exit (next after
"finalizing" the previous one)? Should it just
"override" whatever exit value was "saved" on
the previous "exit"?! ...

> There are always "exceptions". If some standard
> application really does need to catch and finalize "all exceptions",
> there's absolutely no justification for making cancel or thread exit any
> different from all the others.

Well, personally, I would have NO problems with that...
but only if/when the "questions"/"problems" above would
become "cleared". ;-)

> They SHOULD be in the std::exception
> hierarchy, though perhaps in a separate "thread" subclass hierarchy.

Yup... and in the new <cthread> header together with everything
else from the current <pthread.h> and perhaps <sched.h> too! ;-)

Oh, and another brand new <thread> header SHOULD add things
along the lines of

thread_ptr< thread >
joinable_thread_ptr< thread,result >
current_thread{_ptr}
current_thread::exit< T >( const T& );
thread_attr::new_thread< thread,thread_routine,...args.... >
new_thread< thread,thread_routine,...args... >
pthread_mutex_t* mutex::c_mutex();

etc... ;-) ;-)

Well, Okay, [dreams off] ;-(

regards,
alexander.

John Nagle

unread,
Mar 23, 2002, 5:11:13 PM3/23/02
to
David Butenhof wrote:

> Hillel Y. Sims wrote:
>>"John Nagle" <na...@animats.com> wrote in message
>>news:3C98417...@animats.com...
>>
>>>Suppose that raising an exception in another
>>>thread only took effect when the thread made some
>>>test.

.....

>>>
>>> Everything in the libraries that can block
>>> for a nontrivial period thus needs to test for
>>> an external exception request.


I think we're closing in on the right semantics here.


> POSIX has two lists of cancelation points. One is of functions that the
> standard REQUIRES to be cancellation points. If a cancel request is
> pending, and cancelability is enabled when a thread makes one of these
> calls, and cancellation doesn't occur, then the implementation is broken.
> Period.
>
> The second list is of OPTIONAL cancellation points.

....


> ALL other functions defined by POSIX are REQUIRED to NOT be cancellation
> points. Any application may safely call these functions without preparing
> for cancelation. Think what it'd mean if unrestricted implementation
> defined behavior was allowed. EVERY function defined by the standard MIGHT
> deliver a cancel... so the application needs to defend itself... but it can
> never DEPEND on any being cancellable.
>
> The Single UNIX Specification extends both lists with SUS functions that
> aren't in POSIX (such as select()),


An important one to be cancellable.


> ANSI C++, if it recognizes or support cancellation at all, must follow

> these guidelines ...


Reasonable.


> "Swallowing" a cancel, except by deliberate application intent, is bad.
> It's OK to hold it pending indefinitely, but that's different.


Definitely. Thus, there should be a way to lock
out cancels, but this is a critical section lock against
asynchrony that defers them, rather than losing them.
It's like preventing interrupts, which defers them,
it doesn't lose them.

When you think of it that way, an issue that
appears with interrupts becomes apparent - what do
you do about multiple interrupts while interrupts
are prevented? Or, in this case, what if multiple
cancels are received during a period when cancels
are being held? What if you have the equivalent
of multiple exceptions pending? This is quite
possible, in fact likely.

This is a tough problem, because exception
handlers are very different from interrupt handlers.
Interrupt handlers return; exception handlers unwind.
It's sufficient for an interrupt handler to prevent
interrupts until it returns. There's no analogous
action an exception handler should take.

You usually don't want an asynchronous cancel
exception to cancel an asynchronous cancel exception
handler. So it's reasonable to argue that
cancels have to be deferred until the "catch"
exits. In fact, that's probably a good general
rule - async cancels should be deferred during exception
processing. Allowing them would require that
exception handlers be written in a very paranoid style.

It's important that service loops like the one below
not be forced to exit just because lots of
async cancels were queued up.

while (1)
{ try
{ // perform some cancellable operation that blocks
}
catch (AsyncCancel foo)
{ // clean up
}
}

This should work, since there's nothing cancellable between
the end of the "catch" and the beginning of the "try".
Extra cancels will cause extra exceptions, but they
won't cause the loop to exit. It might be useful to
put a cancellable operation that does nothing
(and such a thing should be available) early in the
"try" block, so any extra cancels get used up before
the try block gets going on useful work.

These are beginning to look like usable,
understandable semantics.

John Nagle

Animats

Hillel Y. Sims

unread,
Mar 23, 2002, 6:46:42 PM3/23/02
to
Well, aside from philosophical reasons why you should probably never do what
you have just written, the safest way to avoid trouble solely w/r/t
thread-cancels is to disable cancellation before entering the no-throw
block:

"Garry Lancaster" <glanc...@ntlworld.com> wrote in message
news:8sKm8.4584$bh1.3...@news11-gui.server.ntli.net...
>
> void foo()
> {
> UserAlerter ua;
>
CANCEL_GUARD; // *** special helper utility to temporarily prevent
thread-cancellation


> try
> {
> ua.AlertUser( "This is an optional message." );
> }
> catch(...) {}
> }
>

CANCEL_GUARD (or feel free to call it whatever you like) is a pthread-based
stack-guard helper I have written which disables cancellation delivery in
the current thread while it is in scope. Thus, there is no way for the
AlertUser() code to trigger a thread-cancel exception; it is totally removed
from the picture for the current stack-frame. So even the catch(...) will
not cause a thread-cancellation exception to disappear (which as previously
stated would be a bad thing under almost all circumstances, except certain
very special cases where that procedure is very carefully controlled). Any
pending thread-cancellation requests which happened to be sent by another
thread at the moment we are in this code will remain pending and not be
delivered until some later point after cancellation is reenabled when the
next cancel-point is triggered in the target thread. So you don't have to
worry about breaking the exception-safety behavior in this respect.

Now, as to your assertion that "catch(...) {}" is ok...

> The catch(...) is OK, since it is not a fatal error if
> AlertUser fails for our optional message. Instead of
> ignoring the failure, we could fall back on an
> alternative alerting technique. In either case, the state of
> the system is known (because of the strong exception
> safety guarantee in effect) and there is no absolute
> requirement to rethrow the exception.

This is just an invalid assumption, you do not know that it is not a fatal
error, and you do not know that the state of the system is known. When you
use "catch(...)", for all you really (don't) know you might even intercept
some implementation-defined fatal exception such as segfault/accvio caused
by program error in the AlertUser() function -- and what is to be made of
"knowing the state of the system" in light of an access violation fault? You
almost certainly cannot recover from unknown program logic error at runtime;
the best bet is probably to shut down as quickly and simply as possible,
trying to avoid corrupting any non-volatile user data. (And then perhaps if
you are a server, the server-monitor process will see that its child process
has died, and relaunch a new instance, etc...) You must always rethrow from
catch(...), or better yet avoid it like the plague and just allow
"implementation-defined behavior" of unhandled exceptions out of main() to
function properly (hopefully generating a stack-trace / core dump / etc.)

This is similar to the notion of "unexpected()" - except that maybe it's
more important for the caller to determine what is expected/unexpected, not
the callee. The caller does this by only listing known expected types in its
catch() handlers -- if AlertUser() function is properly expected to throw
only UserAlerterException-inherited types (think of what exception-specs are
basically intended to represent), then the code should more correctly be
written:

void foo()
{
UserAlerter ua;

CANCEL_GUARD; // in case AlertUser may trigger cancel-point
try {
ua.AlertUser("Optional Message");
}
catch(const UserAlerterException&) {} // only catch expected exceptions
from UserAlerter class!
}
(and just for extra measure, consider what good would catch(...) {} be at
all anyhow if AlertUser() were defined as follows:
class UserAlerter {
...
void AlertUser(const char*) throw(UserAlerterException);
};
?)

As with any other typename-based exception handlers, CANCEL_GUARD is needed
only in some cases where it cancellation is an otherwise-possible condition
that must be avoided, and is really unnecessary except in nothrow code where
the functions/operations being called can trigger cancel-points (let's
assume in this case that AlertUser() may potentially trigger a
future-standard-defined cancel-point). In the logic of foo(), just as it
sets up particular catch handler(s) to deal with certain types of
exceptions, it also specifies explicitly that it recognizes that
thread-cancellation is an "expected" exceptional situation, but one that
needs to be handled slightly differently from typical exceptions - since it
cannot validly swallow the exception, instead it can cause it to be deferred
until some later point when it will be valid.

Ok, and what about certain templated code where we cannot necessarily have
any idea what type(s) of exceptions might be expected from operations
involving type T? Well, by using Standard C++ exception hierarchies
properly, we still have a valid defense against this situation. Although it
could likely be argued that even this is too loose, in light of using
catch(...) I believe this has to be at least one order of magnitude safer:

(this is a somewhat contrived example, because I am finding it hard to come
up with a real-life example of where something like this would really even
be appropriate anyway)
template <class T>
void foo(const T& t) // cannot throw
{
CANCEL_GUARD;
try {
t.AlertUser("Optional Message");
}
catch(const std::exception&) {}
}

By properly defining ALL program-based exceptions to inherit from /at
minimum/ std::exception (or even better, a subclass of std::exception), we
can always rely on catch(const std::exception&) to catch all possible
software-defined exceptions that are known within the realm of C++! By
strictly /avoiding use of any types not inherited from std::exception/,
catch(...) is reserved only for implementation-specific exceptions that are
likely not even knowable directly to C++ -- but we don't want to ever
intercept these anyhow, so std::exception base is maximally sufficient for
catching any possible C++ exception!

>"David Butenhof" <David.B...@compaq.com> wrote in message

news:hUFm8.1374$fL6....@news.cpqcorp.net...


> > I disagree. It is and should be a normal exception, and inside the
standard
> > class hierarchy. NOBODY has any business "finalizing" (catching and not
> > releasing) an exception they don't know by name. If you can't identify
the
> > exception, you can't know why it was raised or what it means. And if you
> > don't understand what it means, you cannot possibly know that it doesn't
> > need to be handled further down the stack. Therefore, if you really need
to
> > catch it for some reason, you MUST re-throw. This should apply to any
> > exception not caught using a "leaf" name. Anything caught by superclass
> > designation (much less anything as generic as std::exception or
catch(...))
> > should always be re-thrown. (Except in "weird" upside down call
structures
> > like the RPC client.)
> >

> > Nevertheless, the standard should specify mechanism, not policy. To say
> > "thou shalt not catch cancel because I don't think you should" is
> mandating
> > the committee's collective consensus of "proper behavior", and that's at
> > best inappropriate. There are always "exceptions". If some standard
> > application really does need to catch and finalize "all exceptions",
> > there's absolutely no justification for making cancel or thread exit any
> > different from all the others. They SHOULD be in the std::exception
> > hierarchy, though perhaps in a separate "thread" subclass hierarchy.
>
> I agree this is a solution to keep most people
> happy.
>
> namespace std
> {
> class exception {...};
> class thread_cancel_exception : public exception {...};
> class base_for_other_exceptions : public exception {...};
> ....
> };

I disagree, it is not exactly a "normal" exception. The above suggestion is
basically mandating that all classes that previously inherit from
std::exception need to be changed to inherit from base_for_other_exceptions,
in order to keep thread_cancel_exception separate from everything else; I
suggest it makes more sense to simply keep the current meaning of
std::exception and put thread-cancel exception outside of that (since C++
can still throw /any/ type of object, this is still perfectly valid for
standard exception handling processing). I believe it also might make sense
to keep thread-cancel exception type disjoint from std::exception because
99.99% of the time where it needs to be explicitly considered by the code it
will be dealt with via CANCEL_GUARD as opposed to
catch(thread-cancel-typename) - because if you won't be able to rethrow it,
you must defer it, and if you are able to rethrow it, you probably didn't
need to intercept it by name anyhow. (The implementation-defined
thread-handler routine at the base of the stack would always catch and
finalize thread-cancel exception by typename in order to properly unwind the
entire call stack.) Thread-cancel _could_ be defined underneath
std::exception, but for any time a huge blanket statement like catch(const
std::exception&) {} would be used, almost certainly CANCEL_GUARD will be in
effect anyhow (or no cancel-points are present), effectively filtering out
thread-cancel exception from that list anyhow. For the special 0.01%
scenario where thread-cancel exception needs to be explicitly handled by a
catch() in user code (such as DCE), it will be certainly specified by exact
typename anyhow (thus it doesn't matter whether it is a subclass of
std::exception).

Given the slightly different semantics of thread-cancel exception, it seems
to me it might make sense to have it be a special case outside of
std::exception hierarchy. It could still be caught explicitly by name, but
this would also help lower the possibility of misuse somewhat - for example,
catch(const std::exception&) {} without CANCEL_GUARD.

thanks,


hys
--
Hillel Y. Sims
hsims AT factset.com

cancelguard.h for pthreads:
----
#include <pthread.h>
class CancelGuard : boost::noncopyable {
int m_origState;
public:
CancelGuard() { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,
&m_origState); }
~CancelGuard() { pthread_setcancelstate(m_origState, 0); }
};
#define CANCEL_GUARD CancelGuard TEXT_JOIN_MACRO(cancelguard_, __LINE__)

(ps, Windows API does not seem to support deferred cancellation concept
directly, but there is an implementation of pthreads for win32 that supports
this -- see http://sources.redhat.com/pthreads-win32/ - it appears to even
throw exceptions for cancellation in C++ too.)

Alexander Terekhov

unread,
Mar 25, 2002, 10:55:52 AM3/25/02
to

Garry Lancaster wrote:
[...]

> class UserAlerter
> {
> public:
> // Can throw. Offers strong exception safety guarantee.
> void AlertUser(const char* msg);
> };
>
> void foo()
> {
> UserAlerter ua;
>
> try
> {
> ua.AlertUser( "This is an optional message." );
> }
> catch(...) {}
> }
>
> The catch(...) is OK, since it is not a fatal error

In the "ideal" world -- maybe. In the "real" world,
however, if its suddenly throws something like
"std::logic_error" (something you are most likely/
generally just NOT supposed to be able to RECOVER
from within a program istelf -- just an indication
of some coding error and/or already totally broken
program state by the time you call it) -- NO WAY,
IMHO.

> if AlertUser fails for our optional message. Instead of
> ignoring the failure, we could fall back on an alternative
> alerting technique.

Then why don't you just let the UserAlerter's author
come up with something along the lines of:

void AlertUser(const char* msg);
void AlertUser(const char* msg,const std::nothrow_t&) throw();

or, even better:

void AlertUser(const char* msg);
bool AlertUser(const char* msg,const std::nothrow_t&) throw();

void foo()
{
UserAlerter ua;

if ( !ua.AlertUser( "This is an optional message.",std::nothrow ) {
// alternative alerting technique
// ...
}

}

And let the UserAlerter either do NOT throw internally
anything at all and/or just catch&ignore/eat'm internally
anything RECOVERABLE and EXPECTED, which could be thrown
inside its impl?!

The author of UserAlerter could easily do it for you
WITHOUT requiring you to write those totally silly things
like catch(...) {} and/or catch(const std::exception&),
IMHO.

> In either case, the state of
> the system is known (because of the strong exception
> safety guarantee in effect)

Again, that's your "ideal" world; NOT my "real" world,
sorry.

> and there is no absolute
> requirement to rethrow the exception.

Unless your local context cleanup/fixup is somehow
dependent on the exact nature/kind of exception (and,
btw: example, please!) there is NO "requirements"
whatsoever to catch/rethrow anything (function-try-
blocks for c-tors/d-tors and alike silly stuff aside,
IMHO), I think.

> In the absence of an exception safety guarantee
> I would agree that exceptions need to be thrown
> all the way up to a handler that has intimate
> knowledge of the exact kind of exception. But with
> the exception safety guarantee this is unnecessary.

A handler with an "intimate knowledge of the exact
kind of exception" has NOTHING to do with exception
safety -- basic/strong guarantees; it does have
something to do with RECOVERY from an errors - you
just need to know what happened in order to recover.
If you are not going to recover then the best you
could do is just leave that core-dump/whatever with
COMPLETE PROGRAM STATE AT THROW POINT to your
service/change team. Okay, you may want to catch
something EXPECTED/KNOWN and report it nicely before
you bail out without abort() or alike, but
that's completely different story, I think.

regards,
alexander.

P.S. In a multi-threaded environment silly
unwinding on "unexpected" errors (even prior
to termination -- "implementation-defined"
standard bit) is even worse... because it
could happen/result in broken exception safety
guarantees (broken state) EXPOSED to other
"innocent" threads (due to scoped-locking)
adding MUCH MORE harm/damage...

David Butenhof

unread,
Mar 25, 2002, 10:59:54 AM3/25/02
to
John Nagle wrote:

> David Butenhof wrote:
>
> Definitely. Thus, there should be a way to lock
> out cancels, but this is a critical section lock against
> asynchrony that defers them, rather than losing them.
> It's like preventing interrupts, which defers them,
> it doesn't lose them.
>
> When you think of it that way, an issue that
> appears with interrupts becomes apparent - what do
> you do about multiple interrupts while interrupts
> are prevented? Or, in this case, what if multiple
> cancels are received during a period when cancels
> are being held? What if you have the equivalent
> of multiple exceptions pending? This is quite
> possible, in fact likely.

Like standard UNIX signals (not queued realtime signals), there's a single
"pending cancel" bit. Multiple requests can only "set again" that single
bit, and there remains only a single cancel request. Supporting "queued"
cancel requests would be a bit like the mystery spoof "murder by death",
where the victim was killed simultaneously by some absurd number of
attempts. A thread has but one life to give for its process, or something
like that. Dead is as dead does. Etc.

Now, the C++ committee might elaborate and complicate that model if it
really wants to, as the ability to "catch and continue" could be conceived
to make the concept of multiple outstanding cancel requests meaningful.
However, although it might become meaningful I don't see how it could be
useful. If the application really intends to cancel the "resumed" thread
after it catches and continues, then the application can introduce
appropriate synchronization to ensure it happens that way. The second
cancel requestor can't possibly yet know that it will still be necessary to
cancel the thread after recovery. (Though "clairvoyant threading" does have
possibilities if we can work out the minor issues of implementation!)

> This is a tough problem, because exception
> handlers are very different from interrupt handlers.
> Interrupt handlers return; exception handlers unwind.
> It's sufficient for an interrupt handler to prevent
> interrupts until it returns. There's no analogous
> action an exception handler should take.
>
> You usually don't want an asynchronous cancel
> exception to cancel an asynchronous cancel exception
> handler. So it's reasonable to argue that
> cancels have to be deferred until the "catch"
> exits. In fact, that's probably a good general
> rule - async cancels should be deferred during exception
> processing. Allowing them would require that
> exception handlers be written in a very paranoid style.

The current (C-based) POSIX rule is that cancelability is DISABLED when a
cancel is delivered. That is, cleanup handlers cannot be cancelled unless
one explicitly enables cancelability. (Which isn't disallowed; but the
handler that enables cancelability should also DISABLE it before returning,
lest it disrupt the invariant expected by later cleanup handlers.)

Since there's no way under POSIX to "finalize" a cancelation short of
thread termination, there's no explicit provision for "resetting" the
cancelability state "afterwards". To be honest, we didn't find it
convenient to deal with that issue in our exception-based implementations,
and we simply leave it to the application. IF someone wants to finalize a
cancellation and continue, that handler is responsible for setting the
desired state. I expect that the "right" thing to for C++ to do would be to
disable asynchronous cancelability and enable general cancelability when a
cancel exception is caught. (A re-throw would start all over again.)

> It's important that service loops like the one below
> not be forced to exit just because lots of
> async cancels were queued up.
>
> while (1)
> {try
> { // perform some cancellable operation that blocks
> }
> catch (AsyncCancel foo)
> {// clean up
> }
> }
>
> This should work, since there's nothing cancellable between
> the end of the "catch" and the beginning of the "try".
> Extra cancels will cause extra exceptions, but they
> won't cause the loop to exit. It might be useful to
> put a cancellable operation that does nothing
> (and such a thing should be available) early in the
> "try" block, so any extra cancels get used up before
> the try block gets going on useful work.

The POSIX standard specifies pthread_testcancel() which is exactly "a
cancellable routine that does nothing (else)".

On the other hand, code like that argues against any point to allowing
"multiple cancels". If the code wants, it can ignore any number.
Conversely, if the code wants, it can ensure that a single pending cancel
isn't accidentally ignored. The added complication of queued cancel
requests doesn't solve anything.

> These are beginning to look like usable,
> understandable semantics.

"Usable and understandable" are both important, so that's good.

/------------------[ David.B...@compaq.com ]------------------\
| Compaq Computer Corporation POSIX Thread Architect |
| My book: http://www.awl.com/cseng/titles/0-201-63392-2/ |
\-----[ http://home.earthlink.net/~anneart/family/dave.html ]-----/

---

Hillel Y. Sims

unread,
Mar 26, 2002, 10:54:09 AM3/26/02
to
How do we reconcile exception specifications with the use of an exception to
represent thread-cancellation? Aside from code that is specifically
"throw()" and would explicitly need to rely on some other mechanism
(CANCEL_GUARD or builtin) to disable cancellation anyway, any other code
that can safely cleanup from exceptions is almost always cancel-safe. But
what happens in the following case:

class SomeClass {
public:
..
void func() throw(SomeClassExc);
};

SomeClass::func() is technically cancel-safe, but that would dump to
std::unexpected()! I don't use ES locally, but I can imagine this may be of
importance to some people.

I can think of four possibilities for addressing this issue:
1) Anyone who uses exception specs and wishes their code to be properly
cancel-safe must manually augment all exception spec lists with
[named-thread-cancel] type. Anyone who uses exception specs and wishes their
code to be properly cancel-safe must manually disable cancellation in all
other functions which do not include [named-thread-cancel] type in their
exception spec lists.
2) Thread-cancel exception is "special" and does not cause std::unexpected()
to be triggered (except maybe for "throw()", where it should be physically
impossible anyway).
3) Threads (if even treated by C++) are not directly cancelable by
language/library-defined means.
4) ES is deprecated and std::unexpected() is never triggered by anything.
;-)

"David Butenhof" <David.B...@compaq.com> wrote in message

news:0rGn8.1449$fL6....@news.cpqcorp.net...


> John Nagle wrote:
>
> > These are beginning to look like usable,
> > understandable semantics.
>
> "Usable and understandable" are both important, so that's good.
>

thanks,


hys
--
Hillel Y. Sims
hsims AT factset.com

---

Alexander Terekhov

unread,
Mar 26, 2002, 1:05:57 PM3/26/02
to

Alexander Terekhov wrote:
>
> Hans Aberg wrote:
> [...]
> > I have an old (1989) book by Gehani & Roome, "The Concurrent C PL". Could
> > some thread experts please explain the relevance of that stuff for C++?
>
> Well, how about some more (up-to-date and
> EXCITING) stuff (in addition to your old C
> book ;-))?
>
> ftp://plg.uwaterloo.ca/pub/uSystem/

Also, FYI:

ftp://ftp.dsg.cs.tcd.ie/pub/doc/dsg-86.ps.gz
(Ciaran McHale, Synchronisation in Concurrent,
Object-oriented Languages: Expressive Power,
Genericity and Inheritance, 1994)

http://www.doc.ic.ac.uk/~sjg/thesis-mybg.ps.gz
(Michael Ya'akov Ben-Gershon, An Object-Oriented
Approach to Concurrency and Synchronisation, 2000)

regards,
alexander.

Garry Lancaster

unread,
Mar 26, 2002, 1:06:18 PM3/26/02
to
Hillel Y. Sims:

> Well, aside from philosophical reasons why you should
> probably never do what you have just written,

I don't have a problem with it in the situation I
showed. I'd be interested to hear what your
philosophical objections are.

> the safest way to avoid trouble solely w/r/t thread-cancels
> is to disable cancellation before entering the no-throw
> block:

Note that I was addressing a point David Butenhof
made about normal exception handling in today's
C++. Therefore, thread cancellation was not directly
relevant.

Kind regards

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

---

Garry Lancaster

unread,
Mar 26, 2002, 3:15:23 PM3/26/02
to
> Garry Lancaster wrote:
> > class UserAlerter
> > {
> > public:
> > // Can throw. Offers strong exception safety guarantee.
> > void AlertUser(const char* msg);
> > };
> >
> > void foo()
> > {
> > UserAlerter ua;
> >
> > try
> > {
> > ua.AlertUser( "This is an optional message." );
> > }
> > catch(...) {}
> > }
> >
> > The catch(...) is OK, since it is not a fatal error

Alexander Terekhov:


> In the "ideal" world -- maybe. In the "real" world,
> however, if its suddenly throws something like
> "std::logic_error" (something you are most likely/
> generally just NOT supposed to be able to RECOVER
> from within a program istelf -- just an indication
> of some coding error and/or already totally broken
> program state by the time you call it) -- NO WAY,
> IMHO.

I disagree.

The exception safety guarantees don't distinguish
amongst different exception types. std::logic_error,
std::bad_alloc, anything, the type makes no difference
as far as the guarantees are concerned. If an exception
safety guarantee does not extend to *all* exception
types, it isn't one I recognize.

I admit the standard definition of std::logic_error
is a little unfortunate [19.1.1/1]

"The class logic_error defines the type of objects
thrown as exceptions to report errors presumably
detectable before the program executes, such as
violations of logical preconditions or class invariants."

Once you accept the wisdom of programming with
the exception safety guarantees you can no longer
accept that it's generally acceptable to throw an
exception when class invariants are violated. (This
is the sort of thing assertions are for, IMHO.)

However, it's worth pointing out that the standard
is descriptive, not prescriptive. It tells you what is
in the language, not necessarily how to use it in a
sensible way.

For details of the exception safety guarantees from
David Abrahams, the guy who first proposed them,
check out:

http://www.boost.org/more/generic_exception_safety.html

To be blunt, anyone who doesn't have a thorough
understanding of this topic is most unlikely to be
able to write exception-safe code. And nowadays
writing non-exception-safe code is becoming less
and less acceptable.

Kind regards

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

---

Hillel Y. Sims

unread,
Mar 27, 2002, 12:17:54 AM3/27/02
to

"Garry Lancaster" <glanc...@ntlworld.com> wrote in message
news:GCXn8.542$0U4.2...@news2-win.server.ntlworld.com...

> Hillel Y. Sims:
> > Well, aside from philosophical reasons why you should
> > probably never do what you have just written,
>
> I don't have a problem with it in the situation I
> showed. I'd be interested to hear what your
> philosophical objections are.
>

Well I tried to answer it already, but I'm willing to try to clarify... some
of it is hopefully even more pragmatic than just philosophical...

[original code in question, does not want to propagate any exceptions out of
AlertUser():]
> > void foo()
> > {
> > UserAlerter ua;


> >
> > try
> > {
> > ua.AlertUser( "This is an optional message." );
> > }
> > catch(...) {}
> > }

You are assuming "catch(...) {}" will prevent any possible exceptions, /even
anything not necessarily expected by "contract" from AlertUser()/, from
causing foo() to exit abnormally. I believe this is a flawed hope, as well
as this design being a misuse of the exception facility to achieve your
goals. First, what exception(s) are defined that may be thrown from
AlertUser() (exceptions are part of the "contract" when calling any
function)..? Are they being thrown to indicate some non-fatal status, or
only to indicate otherwise fatal error anyhow (out of memory?) If only being
used for status indication, consider maybe even just using return value
instead of exceptions, but if not, why not _just_ catch the exceptions that
AlertUser() is intended to throw anyhow? Shouldn't that be sufficient to
prevent all unhandled exit from this case anyhow? (Also, consider what will
likely happen to your program stability if you swallow std::bad_alloc
exception from AlertUser() in this case.)

Next, please consider the following scenario:

class UserAlerter {
public:
...
class UserAlerterExc : public std::runtime_error { ... };
void AlertUser(const char*) throw(UserAlerterExc, std::bad_alloc,
std::thread_cancel);
};
(I'm sure 99% of people don't actually do that anywhere, I don't even use it
myself, but just imagine what would happen if it was using ES...)

Now assume AlertUser is passed an invalid pointer by its caller. The attempt
to dereference it may raise an implementation-defined segfault/accvio
exception on some platforms, which may be a private typename and cannot be
caught directly but will fall into "catch(...)". But, rather than even being
"trapped" and swallowed by "catch(...) {}" (which would be horrible enough,
unless you prefer to continue running "normally" with a corrupted memory
state...), std::unexpected() will be called instead because this violates
the exception spec. list (assuming ES works "right" on that platform). So
you will have busted out of your super catch-all exception eater and crashed
your program anyhow. (Which is probably better in this case than allowing
you to swallow the segfault exception and continuing to run in ignorance of
this fatal problem...) Perhaps this is a somewhat redeeming quality of the
intent behind ES, than in theory it would help prevent users from trying to
catch more than they really ought to be allowed. (Even if the mechanism to
formally express that contract currently seems to be less than fully
satisfactory.. how would you even declare "accvio exception" to prevent
std::unexpected() from being triggered, for example?)

At the absolute minimum, even in completely unknown /valid/ no-throw
situation, where you are calling some templated function and all you know is
that some random C++ exception of any type may be triggered and you must
ignore it, consider using catch(std::exception&) instead, at least then you
will know you are dealing with an actual C++ exception caused by a throw()
vs an OS issue caused by who-knows-what... (and then consider really only
actually even trying to catch std::runtime_error at the least, and then
consider in specific cases only actually trying to catch the minimum set of
actual /expected/ exceptions at any point -- keep in mind what would happen
in these cases philosophically if ES were being used to define a formal
"exception contract" for the called functions, even if it isn't actually
physically being used...)

> > the safest way to avoid trouble solely w/r/t thread-cancels
> > is to disable cancellation before entering the no-throw
> > block:
>
> Note that I was addressing a point David Butenhof
> made about normal exception handling in today's
> C++. Therefore, thread cancellation was not directly
> relevant.
>

Thread cancellation is directly relevant to the above scenario as well.
Let's assume that AlertUser() may be a cancel-safe routine (as it seems any
kind of "throw up an informational modal message box at the user and wait
for random user response" really ought to be) and can be cancelled (and even
includes std::thread_cancel in its ES list so it doesn't cause
std::unexpected()..). "catch(...) {}" in your foo() improperly eats the
thread-cancel exception and prevents it from reaching its target handler
further down the stack (and the caller thread probably has no way to find
out directly that the cancel failed, since the cancel is just an
asynchronous request from its point of view). My first question is, is it
really valid for foo() to prevent thread cancellation? It does not seem
vital or appropriate that foo() not be cancelled during display of a modal
user message... So that is another probable objection to "catch(...) {}" in
foo(). But let's assume you are talking about a valid "no-throw/no-cancel"
situation, such as a destructor...

In any scope where it is vital to block all expected exceptions including
thread-cancellation, such as destructors, and some function(s) may be called
which can be cancelled in and of themselves, the best way to handle the
situation is to /prevent, not handle/ cancellation during that scope; any
cancel requests by another thread will be safely deferred (and merged into a
single request) until a later point when it is safe for this target thread
to handle it, and thread-cancel exception will not be thrown at all in the
current scope, thus avoiding the unfortunate issue of potentially having to
"catch() {}" it and block it from ever being cancelled.

>The exception safety guarantees don't distinguish
>amongst different exception types. std::logic_error,
>std::bad_alloc, anything, the type makes no difference
>as far as the guarantees are concerned. If an exception
>safety guarantee does not extend to *all* exception
>types, it isn't one I recognize.

[...]


>For details of the exception safety guarantees from
>David Abrahams, the guy who first proposed them,
>check out:
>
>http://www.boost.org/more/generic_exception_safety.html

My beef with this and other major players' advice is that they do not seem
to formally treat the notion of expected vs. unexpected exceptions at all in
their definitions of exception safety, which I believe is a very valid and
important "semantic contract" issue w/r/t proper use of exceptions (of
course I may have overlooked a part in the above doc which actually does
discuss the issue, but even searching for "expected" and "exception spec"
did not turn up any results). I do not believe it is ever really valid to
use (or suggest to be used) blanket catch-all statements such as "catch(...)
{}" even in code which must be no-throw to ensure program stability. Program
stability is only really meaningful when expected events are occurring to
which there are valid programmatic responses. In any situation, you should
try to catch only what is expected, and ignore all expected exceptions if
you need to, but you should simply not even try to catch anything that is
unexpected -- because if it were raised it would indicate a serious
violation of program logic integrity (explicit or implicit) anyhow, either
in your own code or the function you have called, and then you have really
lost any notion of "safety" regardless (keep in mind ES would totally remove
your personal judgment from this situation anyhow if it were being used,
which I believe validates my rationale here).

"catch(...) {}" may mean anything from "catch harmless user-dialog status
exception and ignore it" to "catch platform-specific unexpected hardware
error exception and ignore it" -- even without ES, it is simply far too
powerful to ever be used safely without a re-"throw;" in any situation. Even
under the most extreme cases where the only possible knowledge about
expected exceptions is that any random C++ exception may be thrown (maybe in
some templated code), it is at least probably just a little more correct to
say "catch(std::exception&) {}" and at least know you are in the realm of
C++ typed exceptions (and probably better yet to at least stick with
std::runtime_error, since logic_error might be used to raise an objection to
a fatal error in program internal consistency, although I guess Dave
Abrahams said recently you should probably just use abort() in that case
anyway..). (The possible issue of requiring "catch(...)" to catch valid
expected non std::exception-based typed exceptions can easily be addressed
by simply always using only exception types inherited from an appropriate
subclass of std::exception and just don't throw int/const char*/etc. at
all...)

(PS, I'm not at all suggesting that any of the std:: stuff violates this
principle, only the original code in question above "foo()")

Well that's my take on it.

thanks,
hys
--
Hillel Y. Sims
hsims AT factset.com

---

Garry Lancaster

unread,
Mar 27, 2002, 11:01:48 AM3/27/02
to
Hillel Y. Sims:
> > > Well, aside from philosophical reasons why you should
> > > probably never do what you have just written,

Garry Lancaster:


> > I don't have a problem with it in the situation I
> > showed. I'd be interested to hear what your
> > philosophical objections are.

Hillel Y. Sims:


> Well I tried to answer it already, but I'm willing to try to clarify...
some
> of it is hopefully even more pragmatic than just philosophical...
>
> [original code in question, does not want to propagate any exceptions out
of
> AlertUser():]

To clarify, the original code had AlertUser offering
the strong exception guarantee. It is foo, which
we see below, that prevents exceptions propagated
from AlertUser being further propagated to foo's caller.
I actually think we are in agreement here.

> > > void foo()
> > > {
> > > UserAlerter ua;
> > >
> > > try
> > > {
> > > ua.AlertUser( "This is an optional message." );
> > > }
> > > catch(...) {}
> > > }
>
> You are assuming "catch(...) {}" will prevent any possible exceptions,
/even
> anything not necessarily expected by "contract" from AlertUser()/, from
> causing foo() to exit abnormally.

I am assuming that exceptions propagated from AlertUser,
will not propagate to foo's caller. Exceptions from the
UserAlerter constructor will still propagate to foo's caller.
The language guarantees all this, as far as I can see.

> I believe this is a flawed hope, as well
> as this design being a misuse of the exception facility to achieve your
> goals. First, what exception(s) are defined that may be thrown from
> AlertUser() (exceptions are part of the "contract" when calling any
> function)..? Are they being thrown to indicate some non-fatal status, or
> only to indicate otherwise fatal error anyhow (out of memory?)

What makes you think std::bad_alloc is a fatal error? The
program may have no need of any additional dynamic
memory after the call to AlertUser(). Or it may only require
a smaller amount than AlertUser() requested and have
its request granted. Or another program may release
memory concurrently making subsequent requests
easily satisfiable.

> If only being
> used for status indication, consider maybe even just using return value
> instead of exceptions, but if not, why not _just_ catch the exceptions
that
> AlertUser() is intended to throw anyhow?

For one thing, because catch(...) is simpler since there is no
difference between

catch(...) {}

and

catch(first_exception_type&) {...}
catch(second_exception_type&) {...}
catch(third_exception_type&) {...}

when the try block can throw only first_exception_type,
second_exception_type or third_exception_type.

Why make over-complicated code?

> Shouldn't that be sufficient to
> prevent all unhandled exit from this case anyhow?

It should, but then so would the simpler catch(...) {}.

If what you are saying is that the contract of the function
might say the function progagates only first_exception_type,
second_exception_type and third_exception_type *but*
in reality might propagate a std::bad_alloc or something else,
then the contract is bad.

> (Also, consider what will
> likely happen to your program stability if you swallow std::bad_alloc
> exception from AlertUser() in this case.)

It will be absolutely fine, since the strong exception safety
guarantee applies to all exceptions including std::bad_alloc.
The program will be just as stable as it was before the
call to AlertUser().

Of course, any future dynamic allocation is likely (although
by no means certain) to also result in std::bad_alloc being
raised again. Again, if you use exception safety guarantees
correctly, this should not mean your application is unstable.

I think we would all agree that if the app has exhausted
dynamic memory and can't do any further useful work
without it, then eventually an exception of some type
(probably std::bad_alloc itself) should propagate up to
main() and the program should exit gracefully. But there
is no reason to assume that is the case with the code
I posted.

> Next, please consider the following scenario:
>
> class UserAlerter {
> public:
> ...
> class UserAlerterExc : public std::runtime_error { ... };
> void AlertUser(const char*) throw(UserAlerterExc, std::bad_alloc,
> std::thread_cancel);
> };
> (I'm sure 99% of people don't actually do that anywhere, I don't even use
it
> myself, but just imagine what would happen if it was using ES...)
>
> Now assume AlertUser is passed an invalid pointer by its caller. The
attempt
> to dereference it may raise an implementation-defined segfault/accvio
> exception on some platforms, which may be a private typename and cannot be
> caught directly but will fall into "catch(...)".

I would argue that any implementation that mapped
segfaults or similar to normal C++ exceptions was
broken. I don't think the standard permits this behaviour [15/1]:

"A handler will be invoked only by a throw-expression
invoked in code executed in the handler's try block or in
functions called from the handler's try block."

Since the segfault isn't a throw expression invoked in code
executed in the handler's try block, it cannot invoke a
handler, even the catch(...) handler.

Do you know of any implementations that do this?

> But, rather than even being
> "trapped" and swallowed by "catch(...) {}" (which would be horrible
enough,
> unless you prefer to continue running "normally" with a corrupted memory
> state...), std::unexpected() will be called instead because this violates
> the exception spec. list (assuming ES works "right" on that platform). So
> you will have busted out of your super catch-all exception eater and
crashed
> your program anyhow.

An invalid pointer was derefenced (undefined behaviour)
and this caused the program to crash. This in no way
implies any problem with catch(...) or any other part of the
exception handling mechanism.

> (Which is probably better in this case than allowing
> you to swallow the segfault exception and continuing to run in ignorance
of
> this fatal problem...) Perhaps this is a somewhat redeeming quality of the
> intent behind ES, than in theory it would help prevent users from trying
to
> catch more than they really ought to be allowed. (Even if the mechanism to
> formally express that contract currently seems to be less than fully
> satisfactory.. how would you even declare "accvio exception" to prevent
> std::unexpected() from being triggered, for example?)

Who knows? As I write above, I don't believe it is
permitted anyway.

> At the absolute minimum, even in completely unknown /valid/ no-throw
> situation, where you are calling some templated function and all you know
is
> that some random C++ exception of any type may be triggered and you must
> ignore it, consider using catch(std::exception&) instead, at least then
you
> will know you are dealing with an actual C++ exception caused by a throw()
> vs an OS issue caused by who-knows-what...

Again, OS problems shouldn't be translated into
exceptions automatically in this surprising way.

Further, not all C++ exceptions derive from
std::exception. Arguably this is good practice,
but there is nothing in the language to enforce
it. If you absolutely must catch all exceptions,
catch(...) is the one.

> (and then consider really only
> actually even trying to catch std::runtime_error at the least, and then
> consider in specific cases only actually trying to catch the minimum set
of
> actual /expected/ exceptions at any point -- keep in mind what would
happen
> in these cases philosophically if ES were being used to define a formal
> "exception contract" for the called functions, even if it isn't actually
> physically being used...)

I'm not sure what you are alluding to here.

> > > the safest way to avoid trouble solely w/r/t thread-cancels
> > > is to disable cancellation before entering the no-throw
> > > block:
> >
> > Note that I was addressing a point David Butenhof
> > made about normal exception handling in today's
> > C++. Therefore, thread cancellation was not directly
> > relevant.
>
> Thread cancellation is directly relevant to the above scenario as well.

I can see from your previous postings that you are
an advocate of adding asynchronous thread
cancellation in C++0x, but I think it is a bit of a
stretch to say it is directly relevant to C++98 code.

[snip]

Kind regards

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

---

Daniel Miller

unread,
Mar 27, 2002, 12:19:56 PM3/27/02
to
Garry Lancaster wrote:


Aborting execution of an entire executable/process is absolutely unacceptable
for numerous categories of embedded software. (Just because some minor layer of
code did not like a parameter to some arcane unimportant function-call, would
you like air traffic control to shut down because assert caused the tower's
software to abort? Just because the software in a telephone switch experienced
a minor bug while processing someone else's telephone call, would you like the
telephone switch serving tens of thousands of poeople through your local central
office to shut down because assert caused the software to abort, dropping your
telephone call and thousands of others?)

Furthermore, assert is a preprocessor-macro. The goal is rely on the
preprocessor less & less with the complete elimination of the preprocessor being
the end-goal. Exceptions (including logic_error) are a key part of eliminating
the use of the assert preprocessor-macro in field-deployed production software.

Throwing a logic_error exception can definitely cause the abortion of a
transaction in a sane & safe way in exception-safe software. Aborting
transactions sanely with clean-up is what David Abraham's entire
exception-safety guarantees are all about. True, software could hypothetically
be so full of bugs that nothing whatsoever works correctly, including the
abort-transaction mechanism. It all comes down to how the designer/architect
chooses the size/scope of transaction to abort when an error (including
logic_error) occurs. Deducing from your
always-use-assert-macro-instead-of-throwing-logic-error claim, when detecting a
bug (no matter how unimportant the behavior is to the mission of the overall
software), you always choose to bring down an entire macroscopic unit of
software (e.g., executable, process, system). Instead I typically choose to
bring down the most microscopic unit of work which permits the rest of the
system to survive intact. When experiencing a problem with one telephone call
caused by a bug in the software, should one abort that one telephone call
permitting thousands of others to continue (such as by throwing a logic_error
exception caught by an invoker) or should one cause an FCC-reportable outage to
100,000 people by invoking assert? (The FCC and I say the former minor glitch
avoidance, not the latter crashing of the software.) When experiencing a
problem with a thermometer 60 miles from the airport caused by a bug in the
software, should one abort a single read-attempt on that thermometer (such as by
throwing a logic_error exception caught by an invoker) or should one cause an
FAA-reportable outage to a major metropolitan airport by invoking assert? (The
FAA and I say the former minor glitch avoidance, not the latter crashing of the
software.)


> However, it's worth pointing out that the standard
> is descriptive, not prescriptive. It tells you what is
> in the language, not necessarily how to use it in a
> sensible way.


Here it is worth pointing out that one single person's perceived sensibility
might turn out to be another industry's unacceptability.


> For details of the exception safety guarantees from
> David Abrahams, the guy who first proposed them,
> check out:
>
> http://www.boost.org/more/generic_exception_safety.html


David Abraham's essay presents the concept of using exceptions to abort
transactions sanely/cleanly/completely, such as my example of throwing
logic_error to sanely abort each buggy microscopic read-transaction of a
non-mission-critical thermometer 60 miles from an airport or to sanely abort a
single microscopic transaction related to a buggy weird state of a single
telephone-call, preserving the integrity of the overall software system which is
expected to have at least 99.999% up-time. David's essay does *not* rail
hardline against throwing logic_error. Instead David's essay focuses on how to
assure that a design has sane transaction-abort semantics. David's essay does
*not* rail hardline against bug-detection being a root-cause of a transaction
abortion.

As I have demonstrated above, in layered-software upper layers can retain
sanity during a transaction-abort if a lower layer throws an exception
(including logic_error). Abortion of the entire software executable/process via
assert was itself not exception-safe because dtors were not invoked and thus
various resources which survive after a process-exit would be left in a
partially-completed indeterminate state---a major violation of
exception-safety---that dtors written using the exception-safe school-of-thought
would have cleaned up had an exception been thrown. There is a fundamental
axiom which must not be violated when architecting/designing/programming for
exception-safety: ctors are for resource acquisition and dtors are for resource
deacquisition. Crashing the process via the assert macro causes dtors to not be
invoked. Thus certain persistent resources would not be released by invocation
of assert, in turn causing further macroscopic cascading of what could have been
handled locally "within the family" by throwing an exception (including
logic_error) which would have been caught by the lowest higher-layer in the
microcosm which can make the decision of "oh nevermind, I will get along without
it just fine".


> To be blunt, anyone who doesn't have a thorough
> understanding of this topic is most unlikely to be
> able to write exception-safe code. And nowadays
> writing non-exception-safe code is becoming less
> and less acceptable.


Nowadays writing software (which when deployed in the field) aborts upon
experiencing the first hiccup is becoming less & less acceptable.

Nowadays writing software where resource acquisition is performed outside of
ctors and where resource release is performed outside of dtors is becoming less
& less acceptable.

Nowadays by-passing the invocation of dtors (such as via assert) is becoming
less & less acceptable in field-deployed software.

Alexander Terekhov

unread,
Mar 27, 2002, 1:23:08 PM3/27/02
to

Garry Lancaster wrote:
[...]

> Alexander Terekhov:
> > In the "ideal" world -- maybe. In the "real" world,
> > however, if its suddenly throws something like
> > "std::logic_error" (something you are most likely/
> > generally just NOT supposed to be able to RECOVER
> > from within a program istelf -- just an indication
> > of some coding error and/or already totally broken
> > program state by the time you call it) -- NO WAY,
> > IMHO.
>
> I disagree.
>
> The exception safety guarantees don't distinguish
> amongst different exception types.

I agree.

> std::logic_error,
> std::bad_alloc, anything, the type makes no difference
> as far as the guarantees are concerned. If an exception
> safety guarantee does not extend to *all* exception
> types, it isn't one I recognize.

Uhmm, you seem to NOT recognize what I am
talking about... Here is another example:

int Something::get( int index ) throw(out_of_range);

Garry Lancaster

unread,
Mar 27, 2002, 1:23:00 PM3/27/02
to
Looking back at my last post I can see it
was confusing or worse.

I really wanted to make two points:

1. Once you invoke undefined behaviour in your
program, anything can happen. One possibility is
that your run-time library could throw an
exception [see note 1].

2. In the absence of undefined behaviour, a
function's contractual exception safety guarantee
still holds and it holds for all exception types,
including std::logic_error and std::bad_alloc.

All the reasons that have been proposed to
explain why catch(...) {} was bad in my example
seem to rely on either

(a) examples involving undefined behaviour OR

(b) a misplaced belief that, even in the absence
of undefined behaviour, certain standard
exceptions are allowed to violate the strong
exception safety guarantee.

Needless to say, neither of these are reliable
foundations. If there are any other reasons I'd
love to hear them.

Kind regards

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

NOTES:

1. I was wrong when I quoted the standard to
say this wasn't allowed: if the behaviour is
undefined, anything is allowed.

Garry Lancaster

unread,
Mar 27, 2002, 3:03:37 PM3/27/02
to
Alexander Terekhov:

> Uhmm, you seem to NOT recognize what I am
> talking about... Here is another example:
>
> int Something::get( int index ) throw(out_of_range);

We were talking about a catch(...) block wrapping a
call to a function that offered a strong exception safety
guarantee. I guess I just don't see where your new
example fits into that discussion.

Kind regards

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

---

Hillel Y. Sims

unread,
Mar 28, 2002, 3:00:50 PM3/28/02
to
If Something::get() throws anything other than out_of_range, catch(...) {}
is meaningless anyhow because std::unexpected() will be called instead and
terminate() your application*.

"Garry Lancaster" <glanc...@ntlworld.com> wrote in message

news:Zvpo8.1487$3h5.7...@news11-gui.server.ntli.net...


> Alexander Terekhov:
> > Uhmm, you seem to NOT recognize what I am
> > talking about... Here is another example:
> >
> > int Something::get( int index ) throw(out_of_range);
>
> We were talking about a catch(...) block wrapping a
> call to a function that offered a strong exception safety
> guarantee. I guess I just don't see where your new
> example fits into that discussion.
>

Why do you need to wrap the call to that function? Why didn't you wrap the
call to the ctor too?

hys
--
Hillel Y. Sims
hsims AT factset.com

(* I know there are exceptions to this, but you will really have to go out
of your way and install an unexpected_handler to deal with that...)

Garry Lancaster

unread,
Mar 30, 2002, 5:45:21 AM3/30/02
to
Daniel Miller:
[snip]
> Deducing from your always-use-assert-macro-
> instead-of-throwing-logic-error claim,

Ahem. I didn't write that. I wrote:

"Once you accept the wisdom of programming with
the exception safety guarantees you can no longer
accept that it's generally acceptable to throw an
exception when class invariants are violated. (This
is the sort of thing assertions are for, IMHO.)"

In summary, using std::logic_error (or any other
exception type for that matter) to signal breakage
of class invariants is incorrect when using one
of the exception safety guarantees, because part
of the guarantee offered is that invariants will
remain intact.

There is nothing wrong with using std::logic_error
to signal other types of exceptional occurrence
provided these occurrences are compatible with
the integrity of the exception safety guarantees,
for example, to signal a bad parameter value [1].

Since you cannot use exceptions to respond to
a class invariant violation and still uphold any of
the exception safety guarantees, you must
choose an alternative mechanism such as
assert, or ignore them.

[snip]

Kind regards

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

NOTES:

1. Sometimes an assertion is still a better
choice for dealing with bad parameters. It
depends.

Niklas Matthies

unread,
Mar 31, 2002, 11:12:52 AM3/31/02
to
On Sat, 30 Mar 2002 10:45:21 GMT, Garry Lancaster <glanc...@ntlworld.com> wrote:
> Daniel Miller:
> [snip]
> > Deducing from your always-use-assert-macro-
> > instead-of-throwing-logic-error claim,
>
> Ahem. I didn't write that. I wrote:
>
> "Once you accept the wisdom of programming with the exception safety
> guarantees you can no longer accept that it's generally acceptable to
> throw an exception when class invariants are violated. (This is the
> sort of thing assertions are for, IMHO.)"
>
> In summary, using std::logic_error (or any other exception type for
> that matter) to signal breakage of class invariants is incorrect when
> using one of the exception safety guarantees, because part of the
> guarantee offered is that invariants will remain intact.

This guarantee presumes that the class invariants held true beforehand.
If they didn't, they can't possibly _remain_ intact, because they
weren't intact in the first place. At most, they could be restored, but
this is not what the exception safety guarantees are about.

Therefore, throwing a "found class invariant to be violated" exception
does not break the exception safety guarantees. It merely informes the
client that the class invariants have somehow been broken previously at
some point (which could be at an entirely different place and time).

-- Niklas Matthies
--
Light travels faster than sound. This is why some people appear bright
until you hear them speak.

John Nagle

unread,
Apr 1, 2002, 1:00:33 PM4/1/02
to
I'm suprised to see such interest in rigor here.
That's atypical of C++.

If you're serious about class invariants, then
any class with an invariant must have a catch block
which restores the invariant before exiting.

A practical way to do this is to mark objects
as valid or broken. Have a "valid" flag for the
object, with all invariants made true if "valid" is
false. That's sound.

It's hard to take class invariants too seriously
in C++. The language offers no support for them.
To do this right, you need considerable compiler
support, which would look something like this:

-- Language syntax for invariants (write once,
check everywhere necessary)
-- Making it unambiguous when control enters or
leaves an object.
-- Providing "synchronized" classes
in the Java sense, to avoid violating the invariant
by concurrency.
-- Providing some way to leave an object by a call.
(reestablish invariant, unlock object, call something,
relock object, expect invariant to be valid on return.)
-- Prohibit calling public methods from constructors
(the invariant isn't established yet.)
-- Prohibit public data members for classes with
invariants.
-- Optimize invariant checking
(look at variables method touches, decide what
needs to be checked. Big win for access functions.)

But C++ isn't going to do that.


John Nagle
Animats

Niklas Matthies wrote:

> On Sat, 30 Mar 2002 10:45:21 GMT, Garry Lancaster <glanc...@ntlworld.com> wrote:
>
>> Daniel Miller:
>> [snip]
>>
>>>Deducing from your always-use-assert-macro-
>>>instead-of-throwing-logic-error claim,
>>>
>>
>> Ahem. I didn't write that. I wrote:
>>
>> "Once you accept the wisdom of programming with the exception safety
>> guarantees you can no longer accept that it's generally acceptable to
>> throw an exception when class invariants are violated. (This is the
>> sort of thing assertions are for, IMHO.)"

---

David Abrahams

unread,
Apr 1, 2002, 4:39:24 PM4/1/02
to

"Garry Lancaster" <glanc...@ntlworld.com> wrote in message
news:cxpo8.1489$3h5.7...@news11-gui.server.ntli.net...

> Daniel Miller:
> [snip]
> > Deducing from your always-use-assert-macro-
> > instead-of-throwing-logic-error claim,
>
> Ahem. I didn't write that. I wrote:
>
> "Once you accept the wisdom of programming with
> the exception safety guarantees you can no longer
> accept that it's generally acceptable to throw an
> exception when class invariants are violated. (This
> is the sort of thing assertions are for, IMHO.)"
>
> In summary, using std::logic_error (or any other
> exception type for that matter) to signal breakage
> of class invariants is incorrect when using one
> of the exception safety guarantees, because part
> of the guarantee offered is that invariants will
> remain intact.

I agree with the spirit of this, but not quite the way it's formulated.
Usually, I prefer an assertion when an internal error is detected because it
gives me the best chance to diagnose the condition which ultimately caused
the problem.
However, that response might not be appropriate for all applications. It
isn't exactly incorrect to respond to a broken invariant with an exception -
in some sense, there's no such thing as a broken invariant: if you detect
something horribly wrong, it means what you thought was an invariant
actually isn't. At that point, you should usually consider yourself to be in
the realm of undefined behavior. Whatever response is most appropriate for
your application, be it throwing an exception or asserting, take it. Just be
sure you remember that anything you do now is somewhat ad-hoc, and that you
have carefully distinguished these ad-hoc attempts at recovery from
controlled exception-handling.

-Dave

David Abrahams

unread,
Apr 1, 2002, 6:41:38 PM4/1/02
to

"John Nagle" <na...@animats.com> wrote in message
news:3CA8A33...@animats.com...

> I'm suprised to see such interest in rigor here.
> That's atypical of C++.
>
> If you're serious about class invariants, then
> any class with an invariant must have a catch block
> which restores the invariant before exiting.

In every member function? You must be kidding. It is April 1st...

> A practical way to do this is to mark objects
> as valid or broken. Have a "valid" flag for the
> object, with all invariants made true if "valid" is
> false. That's sound.

That's hardly sound. If you ever have to mark something invalid, you
mis-specified
your invariants... unless "invalid" is one of the states allowed by the
invariant. But isn't an invariant like that next-to-useless?

In fact, a program which uses "invalid" conditions to try to ensure
robustness is more-than-likely unsound. It doesn't make much sense to try to
continue when invariants are broken.

> It's hard to take class invariants too seriously
> in C++. The language offers no support for them.

It seems to me that's an entirely separate issue. Thinking about and
documenting the invariants of your programs in any language can be an
invaluable tool for ensuring correctness and understandability. A language
with support for runtime invariant checking doesn't relieve you of any of
this thinking (it just helps you detect when you've mis-thunk), and a
language without such support doesn't make the thinking less-valuable.

Language support might be a nice tool to have, but its lack doesn't make
invariants useless w.r.t. C++.

-Dave

John Nagle

unread,
Apr 1, 2002, 11:00:08 PM4/1/02
to
David Abrahams wrote:

> "John Nagle" <na...@animats.com> wrote in message
> news:3CA8A33...@animats.com...
>
>> I'm suprised to see such interest in rigor here.
>>That's atypical of C++.
>>
>> If you're serious about class invariants, then
>>any class with an invariant must have a catch block
>>which restores the invariant before exiting.
>>
>
> In every member function? You must be kidding. It is April 1st...


Of course in every member function. How else
will recovery get done? Consider something like
this

void classname::memberfn()
{ try
{ ... } // do work
catch (exception err)
{ fix_invariant();
throw(err);
}
}

Otherwise, some lower level function can
throw an exception and leave the object
in an invalid state.

If you're serious about design by contract,
this has to be addressed.


>> A practical way to do this is to mark objects
>>as valid or broken.

> That's hardly sound. If you ever have to mark something invalid, you
> mis-specified
> your invariants... unless "invalid" is one of the states allowed by the
> invariant. But isn't an invariant like that next-to-useless?


If you can't fix the object, you have to note that it's
broken, and prevent further use of the object. You must
have recovery to a valid state


>> It's hard to take class invariants too seriously
>>in C++. The language offers no support for them.
> It seems to me that's an entirely separate issue.


It's a language design issue. Because the
language doesn't support it, it's hard to do,
likely to be coded wrong, and inefficient.

Realistically, design by contract in C++
is mostly an exercise in handwaving.

John Nagle
Animats

David Abrahams

unread,
Apr 2, 2002, 10:26:23 AM4/2/02
to

"John Nagle" <na...@animats.com> wrote in message
news:3CA92198...@animats.com...

[you didn't address the April 1st issue - is this really not a troll?]

> Of course in every member function. How else
> will recovery get done?

With destructors. With RAII. By ensuring that fully-constructed objects are
always in a valid state.

> Consider something like
> this
>
> void classname::memberfn()
> { try
> { ... } // do work
> catch (exception err)
> { fix_invariant();
> throw(err);
> }
> }

Surely you're not suggesting this is needed for non-mutating member
functions?

> Otherwise, some lower level function can
> throw an exception and leave the object
> in an invalid state.

There are lots of different ways to ensure that this stuff is taken care of.
Try/catch blocks are only one of them. Anyway, this is basic error recovery.
So what?

> If you're serious about design by contract,
> this has to be addressed.

Sure. You have to make sure you recover sanely from errors. Language support
for runtime invariant checking wouldn't change that fact one bit.

> If you can't fix the object, you have to note that it's
> broken, and prevent further use of the object. You must
> have recovery to a valid state

How can you "prevent further use"? Someone has a reference or a pointer to
it, or it would already be destroyed... unless it's been leaked.

Anyway, the cure for that is easy: don't write classes you can't "fix". In
other words, specify the invariants so that you can always reach them in the
event of an exception.

> >> It's hard to take class invariants too seriously
> >>in C++. The language offers no support for them.
> > It seems to me that's an entirely separate issue.
>
>
> It's a language design issue. Because the
> language doesn't support it, it's hard to do,
> likely to be coded wrong, and inefficient.

Invariants aren't "coded" (except as a byproduct of the class'
implementation), they're "specified", or if you're really lucky, they're
derived by an automated tool which looks at the code and tells you what the
invariants are.

> Realistically, design by contract in C++
> is mostly an exercise in handwaving.

When this started, we weren't talking about DBC, but about class invariants.

boldly-waving-noses-and-knees-ly y'rs,
Dave

David Butenhof

unread,
Apr 2, 2002, 10:27:46 AM4/2/02
to
John Nagle wrote:

> I'm suprised to see such interest in rigor here.
> That's atypical of C++.
>
> If you're serious about class invariants, then
> any class with an invariant must have a catch block
> which restores the invariant before exiting.
>
> A practical way to do this is to mark objects
> as valid or broken. Have a "valid" flag for the
> object, with all invariants made true if "valid" is
> false. That's sound.
>
> It's hard to take class invariants too seriously
> in C++. The language offers no support for them.
> To do this right, you need considerable compiler
> support, which would look something like this:
>
> -- Language syntax for invariants (write once,
> check everywhere necessary)

Absolutely. This is particularly important if language support for
concurrency is to be introduced.

> -- Making it unambiguous when control enters or
> leaves an object.

I don't think this is ever "ambiguous" now, but some of the exit paths
(particularly with exceptions) can be "subtle".

> -- Providing "synchronized" classes
> in the Java sense, to avoid violating the invariant
> by concurrency.

Java's "synchronized" is nothing more than an implicit mutex guard class,
with less flexibility. And technically, "classes" aren't synchronized: only
methods, or blocks within methods. This is an important distinction,
because you have to realize that it has absolutely nothing to do with
"invariants", a concept for which the Java language has no support.

For example, one of the big problems with synchronized and the notify/wait
mechanism in Java is that "synchronized" scopes can be nested (for the same
object), which requires (relatively expensive) "recursive" locking
strategies that can't easily be optimized away. Despite the nested locking,
though, you cannot nest without ensuring that all external invariants are
safe (even though you still hold the "synchronized" lock) unless you can be
sure that no nested method will wait. Because wait releases all nested
levels of the lock, exposing the invariants. This, because it's easy and
obvious to do, the effects are subtle, and most of all because the language
provides no support to detect or diagnose the broken invariants, is a major
weakness in the Java model.

In the old movie "Gumball Rally", a character proclaims that 55 miles an
hour is dangerous: "because it's fast enough to kill you, but slow enough
to make you feel safe". Java synchronize is dangerous: because the
semantics are loose enough to kill your data, but designed to imply
automatic protection the language cannot deliver.

One can of course argue that you should never make a call (even an internal
call on a private method) with a broken invariant. And that's true. But
that's just saying that nesting mutex scopes (from which a Java programmer
cannot escape) is a bad idea (which is also true). And, most especially, a
method should never be able to UNLOCK a scope it didn't begin, as the Java
"wait" method does, unless it's provided with a way to verify that the
action is reasonable.

Ada 95 provides real support for synchronized invariants. That is, it
actually understands the invariant being protected (and modified), so the
language can help instead of merely introducing traps and generally getting
in your way.

> -- Providing some way to leave an object by a call.
> (reestablish invariant, unlock object, call something,
> relock object, expect invariant to be valid on return.)
> -- Prohibit calling public methods from constructors
> (the invariant isn't established yet.)
> -- Prohibit public data members for classes with
> invariants.
> -- Optimize invariant checking
> (look at variables method touches, decide what
> needs to be checked. Big win for access functions.)
>
> But C++ isn't going to do that.

I guess that remains to be seen, which is, supposedly, the entire point of
all this discussion. (Though, yes, it rather sounds like adding invariant
support might be too big a bite.)

/------------------[ David.B...@compaq.com ]------------------\
| Compaq Computer Corporation POSIX Thread Architect |
| My book: http://www.awl.com/cseng/titles/0-201-63392-2/ |
\-----[ http://home.earthlink.net/~anneart/family/dave.html ]-----/

---

Alexander Terekhov

unread,
Apr 2, 2002, 1:01:17 PM4/2/02
to

David Butenhof wrote:
[...]
[...]

> > But C++ isn't going to do that.
>
> I guess that remains to be seen, which is, supposedly, the entire point of
> all this discussion. (Though, yes, it rather sounds like adding invariant
> support might be too big a bite.)

The following rather old (in Net years ;-)) "Re: [boost] Design
and documentation of Boost.Threads" article points to even much
older c.p.t discussion on the similar topic (AFAICT) and the
Butenhofs "bit", of course. ;-)

http://groups.yahoo.com/group/boost/message/19803
http://lists.boost.org/MailArchives/boost/msg19774.php
(see "that is why I dislike both" link and perhaps
the this whole boost thread, if you like)

It did NOT generate that much of the discussion on boost
at that time, however. To me, it would be quite enough to
settle on inclusion of C/pthreads (<cthread> PLUS some
decent C++ "wrappers" (<thread>) to support portable low
level threading in C/C++, in addition to some "language"
issues... for the beginning.

The next step could be that "full"/higher level concurrency
support via the language constructs incorporating the ideas
the Butenhof is talking about (AFAICT) from things like Ada
and perhaps some "research" works (not so widely popular,
"robust", etc) -- uC++:

ftp://plg.uwaterloo.ca/pub/uSystem

and these two papers[1]:

ftp://ftp.dsg.cs.tcd.ie/pub/doc/dsg-86.ps.gz
(Ciaran McHale, Synchronisation in Concurrent,
Object-oriented Languages: Expressive Power,
Genericity and Inheritance, 1994)

http://www.doc.ic.ac.uk/~sjg/thesis-mybg.ps.gz
(Michael Ya'akov Ben-Gershon, An Object-Oriented
Approach to Concurrency and Synchronisation, 2000)

regards,
alexander.

[1] Just in case you will have some problems with
the links above (reportedly do NOT always
work), I've got PDFs on my site too:

http://www.terekhov.de/OO-Concurrence/dsg-86.pdf
(McHale's paper)

http://www.terekhov.de/OO-Concurrence/thesis-mybg.pdf
(Ben-Gershon's paper)

Please let me know if that's somehow
"illegal" and I'll just pull these
PDFs from my site away, immediately.

John Nagle

unread,
Apr 3, 2002, 10:26:38 AM4/3/02
to
David Butenhof wrote:

> John Nagle wrote:

>> -- Making it unambiguous when control enters or
>> leaves an object.
>
> I don't think this is ever "ambiguous" now, but some of the exit paths
> (particularly with exceptions) can be "subtle".


There's exit "out the top", by return or
exception, and there's exit "out the bottom", where
you call something outside the object with the
possibility of recursive reentry into the object.
Few people think enough about this last.
(And it seems to come up all the time in
GUI systems.)


>> -- Providing "synchronized" classes
>> in the Java sense, to avoid violating the invariant
>> by concurrency.


>
> Java's "synchronized" is nothing more than an implicit mutex guard class,


Agreed that Java doesn't rigorously solve the problem.


> Ada 95 provides real support for synchronized invariants. That is, it
> actually understands the invariant being protected (and modified), so the
> language can help instead of merely introducing traps and generally getting
> in your way.


That's my point. Concurrency support and invariant support
have to be designed in and properly integrated. They're
not just add-on "features".


>>But C++ isn't going to do that.
>>
>
> I guess that remains to be seen, which is, supposedly, the entire point of
> all this discussion. (Though, yes, it rather sounds like adding invariant
> support might be too big a bite.)

If you take invariants seriously,
the language should guarantee that if control left an
object with the invariant true, at next entry, it will
be true. This implies suitable locking.

(And if the language provides that protection with static
checking, you only have to check invariants at object
exit, and only the portions of the invariants that involve
variables touched by the returning method.)

I'd argue that locking should work by requiring that
all objects in multi-thread programs must be owned by
some lock. Either an object has a lock of its own,
or it is associated with some other object
that does. (That object might be a thread,
for thread-local objects) That's the sound way to do it.
Could that be fitted into C++?

John Nagle
Animats

Peter Dimov

unread,
Apr 3, 2002, 3:16:46 PM4/3/02
to
John Nagle <na...@animats.com> wrote in message news:<3CA92198...@animats.com>...

> David Abrahams wrote:
>
> > "John Nagle" <na...@animats.com> wrote in message
> > news:3CA8A33...@animats.com...
> >
> >> I'm suprised to see such interest in rigor here.
> >>That's atypical of C++.
> >>
> >> If you're serious about class invariants, then
> >>any class with an invariant must have a catch block
> >>which restores the invariant before exiting.
> >>
> >
> > In every member function? You must be kidding. It is April 1st...
>
>
> Of course in every member function. How else
> will recovery get done? Consider something like
> this
>
> void classname::memberfn()
> { try
> { ... } // do work
> catch (exception err)
> { fix_invariant();
> throw(err);
> }
> }
>
> Otherwise, some lower level function can
> throw an exception and leave the object
> in an invalid state.

This means that the low-level function does not offer the basic
exception safety guarantee. Which most probably means that it has a
bug. And once we've admitted the possibility of a bug, then all bets
are off, because the function is likely to not only break its
"Exception safety guarantee" clause, but its "Effects" clause as well.

If you really want to test for bugs in low-level functions, the code
should look like:

void classname::memberfn()
{
invariant_tester tester;
// do work
}

where invariant_tester and ~invariant_tester abort when the invariant
is broken.

Hans Aberg

unread,
Apr 3, 2002, 3:16:54 PM4/3/02
to
In article <3CAA9616...@animats.com>, John Nagle <na...@animats.com> wrote:
>I'd argue that locking should work by requiring that
>all objects in multi-thread programs must be owned by
>some lock. Either an object has a lock of its own,
>or it is associated with some other object
>that does. (That object might be a thread,
>for thread-local objects) That's the sound way to do it.
>Could that be fitted into C++?

The picture I have in my mind is as follows:

One should have (high-level) runtime objects, that via a runtime interface
knows how to keep itself consistent. Such a runtime object has a
collection of methods, that when activated can respond, in due time, by
providing references to objects.

So C++ such have some such a way to indicate those runtime interfaces.

Now, it will probably be ineffective to let all objects or components be
such runtime objects, so one should allow the implementation of such an
object use inconsistent implementation techniques, just as is typical in
C++: Knowledge about optimization and what constitutes a good runtime
object has not move sufficiently far yet.

So, as for locks, if a runtime object is accessed by another runtime
object, then it should know how to secure the locks. But within itself,
this runtime object may not obey such rules: The implementer may know that
certain locks are not needed, and will avoid them in the name of
efficiency. (For example, one may decide use atomic code instead; for
example, a lock on a ref count will create an enormous slowdown.)

Hans Aberg * Anti-spam: remove "remove." from email address.
* Email: Hans Aberg <remove...@member.ams.org>
* Home Page: <http://www.matematik.su.se/~haberg/>
* AMS member listing: <http://www.ams.org/cml/>

Alexander Terekhov

unread,
Apr 4, 2002, 3:14:20 PM4/4/02
to
< another try >

-------- Original Message --------
Message-ID: <3CA21582...@web.de>
Date: Wed, 27 Mar 2002 19:54:58 +0100
From: Alexander Terekhov <tere...@web.de>
Newsgroups: comp.std.c++
Subject: Re: C++ and threads


Garry Lancaster wrote:
[...]


> > If only being
> > used for status indication, consider maybe even just using return value
> > instead of exceptions, but if not, why not _just_ catch the exceptions
> that
> > AlertUser() is intended to throw anyhow?
>
> For one thing, because catch(...) is simpler since there is no
> difference between
>
> catch(...) {}
>
> and
>
> catch(first_exception_type&) {...}
> catch(second_exception_type&) {...}
> catch(third_exception_type&) {...}
>
> when the try block can throw only first_exception_type,
> second_exception_type or third_exception_type.
>
> Why make over-complicated code?

http://groups.google.com/groups?as_umsgid=c29b5e33.0202161451.2ef75f1f%40posting.google.com

"....
And who is protecting your clients from really bad
things (exceptions/faults) along the lines of:


http://www.tru64unix.compaq.com/docs/base_doc/DOCUMENTATION/V51_HTML/ARH9RBTE/DOCU0011.HTM#excep_defs

To me, the current ES is just great! And I ALWAYS use "throw()"!!
(for *NOTHROW* exception safety guarantee)

Here is another ES example:

void oper(); // could throws anything

void checked_oper_call() throw( SOME_KNOWN_EXPECTED_EXCEPTIONS )
{ oper(); }

try {
checked_oper_call();
}
catch( ... ) // I know what I am doing/getting here,
// just do not want to write many
// catch clauses ;-)
{
//...
}

And the following might be really interesting too:

// Search/Check first!!
if ( std::expected_exception< BlaBla >() )
...."

Also, it appears that my previous posting
to this thread got somehow truncated in more
than a half... I'll just forward it inlined
here:

-------- Original Message --------
X-Mozilla-Status: 0001
X-Mozilla-Status2: 00000000
Message-ID: <3CA1FEC1...@web.de>
Date: Wed, 27 Mar 2002 18:17:53 +0100
From: Alexander Terekhov <tere...@web.de>
Reply-To: tere...@web.de
X-Mailer: Mozilla 4.77 [en] (Windows NT 5.0; U)
X-Accept-Language: en
MIME-Version: 1.0
Newsgroups: comp.std.c++
Subject: Re: C++ and threads
References: <3C7BD6B3...@web.de>
<vryl8.10317$bj1.4...@news02.optonline.net>
<3C98417...@animats.com>
<R%cm8.30100$bj1.10...@news02.optonline.net>
<hUFm8.1374$fL6....@news.cpqcorp.net>
<8sKm8.4584$bh1.3...@news11-gui.server.ntli.net>
<3C9F07B1...@web.de>
<uBXn8.539$0U4.2...@news2-win.server.ntlworld.com>
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit


Garry Lancaster wrote:
[...]
> Alexander Terekhov:
> > In the "ideal" world -- maybe. In the "real" world,
> > however, if its suddenly throws something like
> > "std::logic_error" (something you are most likely/
> > generally just NOT supposed to be able to RECOVER
> > from within a program istelf -- just an indication
> > of some coding error and/or already totally broken
> > program state by the time you call it) -- NO WAY,
> > IMHO.
>
> I disagree.
>

> The exception safety guarantees don't distinguish
> amongst different exception types.

I agree.

> std::logic_error,
> std::bad_alloc, anything, the type makes no difference
> as far as the guarantees are concerned. If an exception
> safety guarantee does not extend to *all* exception
> types, it isn't one I recognize.

Uhmm, you seem to NOT recognize what I am

talking about... Here is another example:

int Something::get( int index ) throw(out_of_range);

---


[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]

[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]

nmtop40

unread,
Apr 4, 2002, 11:42:58 PM4/4/02
to

> "Do you really believe that .NET is good for C++? Why?
>
> I do. ....
>
> Here's a for-instance: Within the standards committee and the
> standards process, I've seen several key recurring questions
> and wishes since C++98 was passed. Three of them are:
> a) "what about standardizing thread support?";
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<snip> ....
> But C++ developers on all platforms wants these kinds of
> things - threads, running on a VM, a managed GUI library.
> ^^^^^^^

Many companies are choosing Java over C++ for exactly these reasons.

The C++ standard needs to add things that are already in Java (standard
threading, standard GUI, standard graphics) to compete with Java. Maybe
borrow some of Java's interfaces.

Were it not for these issues, C++ would be chosen for performance reasons,
among others.

Garry Lancaster

unread,
Apr 5, 2002, 10:50:40 AM4/5/02
to

David Abrahams <david.a...@rcn.com> wrote in message
news:a87tpo$ipq$1...@bob.news.rcn.net...

>
> "Garry Lancaster" <glanc...@ntlworld.com> wrote in message
> news:cxpo8.1489$3h5.7...@news11-gui.server.ntli.net...
> > Daniel Miller:
> > [snip]
> > > Deducing from your always-use-assert-macro-
> > > instead-of-throwing-logic-error claim,

Garry Lancaster:


> > Ahem. I didn't write that. I wrote:
> >
> > "Once you accept the wisdom of programming with
> > the exception safety guarantees you can no longer
> > accept that it's generally acceptable to throw an
> > exception when class invariants are violated. (This
> > is the sort of thing assertions are for, IMHO.)"
> >
> > In summary, using std::logic_error (or any other
> > exception type for that matter) to signal breakage
> > of class invariants is incorrect when using one
> > of the exception safety guarantees, because part
> > of the guarantee offered is that invariants will
> > remain intact.

David Abrahams:


> I agree with the spirit of this, but not quite the way
> it's formulated.

Fair enough. What I gave was a simplification. For
some people it will be an over-simplification.

> Usually, I prefer an assertion when an internal error
> is detected because it gives me the best chance to
> diagnose the condition which ultimately caused
> the problem.

Up to here we are in violent agreement ;-) Assertions
are a much directer form of error reporting than
exceptions.

> However, that response might not be appropriate for all
> applications. It isn't exactly incorrect to respond to a broken
> invariant with an exception - in some sense, there's no
> such thing as a broken invariant: if you detect something
> horribly wrong, it means what you thought was an invariant
> actually isn't. At that point, you should usually consider
> yourself to be in the realm of undefined behavior.

I would still say the invariant exists. The fact that in a correct
program invariants must not be broken, but one has, doesn't
mean the invariant wasn't an invariant, it means the program
is incorrect (because a coding or design error was made
during development). To some extent this is just games with
words, but looked at from a contractual point of view, a
broken contract doesn't mean the contract is wrong,
it means the program doesn't implement it correctly.

I kind of agree when you say this puts us into
undefined behaviour. However this term is used
by the C++ standard with a very specific meaning
and we are going beyond that here. For example,

// Returns:
// Sum of parameters.
int add(int a, int b)
{ return a * b; }

(assume boundary conditions such as overflow are
avoided)

is not undefined behaviour in the standard but clearly
breaks the contract offered by the function. Further, some
things that the standard leaves as undefined may
be specified by particular C++ implementations.

Therefore when talking about a program's failure
to uphold its contract I think it may be less confusing
if the term "undefined behaviour" was avoided in favour
of something like "corrupt state".

> Whatever response is most appropriate for your
> application, be it throwing an exception or asserting,
> take it. Just be sure you remember that anything you
> do now is somewhat ad-hoc, and that you have
> carefully distinguished these ad-hoc attempts at
> recovery from controlled exception-handling.

The problem with using exceptions to respond to
corrupt state is that you do not know the extent of
the corruption and so cannot recover from it. Therefore
the chief advantages of exceptions over assertions,
the ability to correctly release resources during stack
unwinding and to restart the normal execution path with
well-defined state at some point, do not apply.

Kind regards

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

---


[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]

[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]

David Schwartz

unread,
Apr 5, 2002, 12:13:10 PM4/5/02
to
John Nagle wrote:
>
> It's quite possible to make asynchronous
> exceptions work, at least on most current processors.
> Any CPU that can back out a page fault can do clean
> exception handling. This was done in Ada years ago.
>
> The key idea is that for each address in the
> executable program, there must be a unique recovery
> action. Given that, all that's needed is a table
> of address ranges vs. recovery points. On an
> interrupt, the recovery point table is searched for
> the appropriate address, and control is then
> transferred to the recovery handler for that point.
>
> This is similar to the mechanism used now
> to allow code with intermixed declaration and "throw"
> statements. It's just more fine-grained.
>
> There are other obvious implementations, involving
> periodic checks in the code, but they're slower.
>
> The ability to defer asynchronous exceptions
> during critical sections is useful if this is
> to work.

It seems to me that this capability could be added with the thinnest of
wrappers on top of existing C++ exceptions. Just catch the exception,
test if the exception is supposed to be asynchronous 'virtual bool
Asynchronous() could be implemented by exceptions' and if so, create a
job object to service the exception and put it on the job queue.

DS

Niklas Matthies

unread,
Apr 5, 2002, 12:41:33 PM4/5/02
to
On Fri, 5 Apr 2002 15:50:40 GMT, Garry Lancaster <glanc...@ntlworld.com> wrote:
[···]

> > Whatever response is most appropriate for your application, be it
> > throwing an exception or asserting, take it. Just be sure you
> > remember that anything you do now is somewhat ad-hoc, and that you
> > have carefully distinguished these ad-hoc attempts at recovery from
> > controlled exception-handling.
>
> The problem with using exceptions to respond to corrupt state is that
> you do not know the extent of the corruption and so cannot recover
> from it. Therefore the chief advantages of exceptions over
> assertions, the ability to correctly release resources during stack
> unwinding and to restart the normal execution path with well-defined
> state at some point, do not apply.

No, but at least you get a chance to correctly release those resources
and rollback those transactions whose state happens to not have been
corrupted, for example. This might be something as trivial--but utterly
sensible--as setting output volume to zero before exiting a sound
playing application that encounters corrupted state.

More generally put, the decision of what to do in a situation of state
corruption is delegated to the client code, instead of just assuming
that it will be fine with immediately aborting the program. I would
think that separating, by way of exceptions, the detection of corrupt
state from the decision of what to do about it is a rather reasonable
thing to do.

Earlier in this thread (or possibly in a different, but thematically
related one), the argument was made that throwing exceptions upon
detection of program bugs (notably segfaults) was a Bad Thing because
the stack unwinding destroys the faulty program state resulting in it
not being available any more for inspection by a debugging tool.
However, I don't quite buy this reasoning, because having an exception
thrown doesn't prevent a coredump being made before stack unwinding.

If anything, the decision on whether processing the exception, dumping
core and/or aborting the program is something that should best be made a
configuration setting of the runtime/operating system. For this it would
help to consistently differentiate program-bug exceptions from "regular"
exceptions. (Since you certainly don't want core to be dumped upon
encountering the latter. :)

-- Niklas Matthies
--
A mathematician is a machine for converting coffee into theorems.

David Abrahams

unread,
Apr 5, 2002, 1:29:35 PM4/5/02
to

"Garry Lancaster" <glanc...@ntlworld.com> wrote in message
news:1Mdr8.241$6o2....@news11-gui.server.ntli.net...

>
> David Abrahams <david.a...@rcn.com> wrote in message
> news:a87tpo$ipq$1...@bob.news.rcn.net...
> >
> > "Garry Lancaster" <glanc...@ntlworld.com> wrote in message
> > news:cxpo8.1489$3h5.7...@news11-gui.server.ntli.net...
> > However, that response might not be appropriate for all
> > applications. It isn't exactly incorrect to respond to a broken
> > invariant with an exception - in some sense, there's no
> > such thing as a broken invariant: if you detect something
> > horribly wrong, it means what you thought was an invariant
> > actually isn't. At that point, you should usually consider
> > yourself to be in the realm of undefined behavior.
>
> I would still say the invariant exists. The fact that in a correct
> program invariants must not be broken, but one has, doesn't
> mean the invariant wasn't an invariant, it means the program
> is incorrect (because a coding or design error was made
> during development). To some extent this is just games with
> words, but looked at from a contractual point of view, a
> broken contract doesn't mean the contract is wrong,
> it means the program doesn't implement it correctly.

Yes, I agree it's a game with words. However, if we play the game your way,
it admits the possibility of having a program with broken invariants. Then
people get into counterproductive games of marking their objects invalid,
trying to ensure they aren't used, etc. Their code fills up with ad-hoc
devices for managing corrupt states, increasing maintenance cost. What
happens eventually, since there's no good way to delineate the code which is
less-legitimate, is that they start thinking about these states as though
they're somehow predictable.

> I kind of agree when you say this puts us into
> undefined behaviour. However this term is used
> by the C++ standard with a very specific meaning
> and we are going beyond that here.

A standard C++ implementation is just one system. If I write another system,
I am free to tell its users "if you do this bad thing, the result is
undefined". And I do. I see no need to invent a new term for that.

> Therefore when talking about a program's failure
> to uphold its contract I think it may be less confusing
> if the term "undefined behaviour" was avoided in favour
> of something like "corrupt state".

I don't like "corrupt state" because of a) the reasons I mentioned above,
and b) it might not be what's normally thought of as a state (e.g. returning
0 from operator new).

> > Whatever response is most appropriate for your
> > application, be it throwing an exception or asserting,
> > take it. Just be sure you remember that anything you
> > do now is somewhat ad-hoc, and that you have
> > carefully distinguished these ad-hoc attempts at
> > recovery from controlled exception-handling.
>
> The problem with using exceptions to respond to
> corrupt state is that you do not know the extent of
> the corruption and so cannot recover from it.

Probably you can't make a full recovery. At this point, you're gambling.

> Therefore
> the chief advantages of exceptions over assertions,
> the ability to correctly release resources during stack
> unwinding and to restart the normal execution path with
> well-defined state at some point, do not apply.

You can't count on them, but that doesn't mean you shouldn't try. For
example, you may need to reclaim some stack resources before writing out
some error log, or a copy of the current state of the user's documents, etc.
You might have detected corruption in an isolated subsystem which can be
restarted with a high degree of confidence.

-Dave

It is loading more messages.
0 new messages