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

pthread : nifty source code package

1 view
Skip to first unread message

Sean P. Burke

unread,
Aug 22, 2002, 2:55:04 AM8/22/02
to

I've created a package of nifty pthread utilities that may
be useful to the readers of this group. This package is
distributed under a BSD-ish copyright: you are free to
do anything with the code, except remove the copyright.

You can download this package from:

ftp://ftp.xenadyne.com/pub/Nifty.tgz

The package includes the following utilities,
implemented in ANSI C:

* Task Scheduler

This utility allows you to submit tasks for execution
at a later time, and cancel tasks before they execute.
This is especially useful for doing timeouts, e.g.:
1. schedule a pthread_kill(my_self, sig),
2. read a blocking socket,
3. cancel the timeout task.

You can also schedule tasks to repeat at regular intervals.

* Work Queue

Based on the work queue from Butenhof, section 7.2,
this allows you submit tasks to a "thread pool".

* Message Queue

This is a basic message queue for implementing
message-passing and producer/consumer systems.

* Readers/Writer Lock

If your system lacks posix readers/writer locks.

* Thread-safe popen/pclose

Thread-safe substitutes for popen() and pclose()
that use file descriptors rather than FILE *'s,
to play nice with poll() and select().

I welcome any suggestions for improvements.
-SEan


Alexander Terekhov

unread,
Aug 22, 2002, 8:27:00 AM8/22/02
to

"Sean P. Burke" wrote:
[...]

> I welcome any suggestions for improvements.

To begin with, add thread cleanup handlers and
{more} error checking. ;-)

regards,
alexander.

Sean P. Burke

unread,
Aug 22, 2002, 1:20:46 PM8/22/02
to

Alexander Terekhov <tere...@web.de> writes:

> "Sean P. Burke" wrote:
> [...]
> > I welcome any suggestions for improvements.
>
> To begin with, add thread cleanup handlers and
> {more} error checking. ;-)

Cleanup handlers? Where, and why?

-SEan

Alexander Terekhov

unread,
Aug 22, 2002, 2:34:45 PM8/22/02
to

Sean P. Burke

unread,
Aug 22, 2002, 5:06:35 PM8/22/02
to

Alexander Terekhov <tere...@web.de> writes:

> "Sean P. Burke" wrote:
> >
> > Alexander Terekhov <tere...@web.de> writes:
> >
> > > "Sean P. Burke" wrote:
> > > [...]
> > > > I welcome any suggestions for improvements.
> > >
> > > To begin with, add thread cleanup handlers and
> > > {more} error checking. ;-)
> >
> > Cleanup handlers? Where, and why?
>
> http://www.opengroup.org/onlinepubs/007904975/functions/xsh_chap02_09.html#tag_02_09_05
> (Thread Cancelation)

Thread cancelation? Bah! Anyone who tries to use
that, richly deserves whatever happens to them.
Still, a note to that effect in the README would
not be amiss.

-SEan

Randy Howard

unread,
Aug 24, 2002, 8:12:56 PM8/24/02
to
In article <82ptwak...@dev0.welkyn.com>, sbu...@dev0.welkyn.com says...

> Thread cancelation? Bah! Anyone who tries to use
> that, richly deserves whatever happens to them.

HERETIC!!! Outcast, unclean!

--
randy AT thegateway DOT net

Danny Diaz

unread,
Aug 27, 2002, 11:38:42 AM8/27/02
to
Randy Howard <nos...@thegateway.net> wrote:
> In article <82ptwak...@dev0.welkyn.com>, sbu...@dev0.welkyn.com says...
>> Thread cancelation? Bah! Anyone who tries to use
>> that, richly deserves whatever happens to them.
>
> HERETIC!!! Outcast, unclean!
>

LOL. I have to agree with him, however. On a newsgroup that
defames those who want to implement recursive mutexes, waiting
on multiple objects, and anything else deemed "overly exotic",
you'd think it would be herecy(sp?) to even mention thread
cancellation. I mean, damn, too much can go wrong in such
a little amount of code. Like all previously mentioned
defamatory actions, you *can* do them right, but to quote
the average comp.programming.threads reader, "WHY?? There
MUST be a better way!"

-Danny

--
Danny Diaz | "There are only 10 kinds of people in the
ddiaz | world -- Those who know binary and those
@gehennon.net | who don't." -- Unknown
Confidence is what you had before you understood the situation.

Alexander Terekhov

unread,
Aug 27, 2002, 12:09:45 PM8/27/02
to

Danny Diaz wrote:
>
> Randy Howard <nos...@thegateway.net> wrote:
> > In article <82ptwak...@dev0.welkyn.com>, sbu...@dev0.welkyn.com says...
> >> Thread cancelation? Bah! Anyone who tries to use
> >> that, richly deserves whatever happens to them.
> >
> > HERETIC!!! Outcast, unclean!
> >
>
> LOL. I have to agree with him, however. On a newsgroup that
> defames those who want to implement recursive mutexes, waiting
> on multiple objects, and anything else deemed "overly exotic",

Excuse me, but the things you've mentioned aren't "overly exotic";
they are simply "brain dead". "overly exotic" would be the right
term for something along the lines of MS-IOCP-like thread-pooling,
but with "better control" -- taking into account NOT only min.
amount of context switching overhead, but also average response
times, "optimal" bandwidth-/IO-utilization, etc., I guess.

regards,
alexander.

David Butenhof

unread,
Aug 27, 2002, 1:49:15 PM8/27/02
to
Sean P. Burke wrote:

> Alexander Terekhov <tere...@web.de> writes:
>
>> "Sean P. Burke" wrote:
>> >
>> > Alexander Terekhov <tere...@web.de> writes:
>> >
>> > > "Sean P. Burke" wrote:
>> > > [...]
>> > > > I welcome any suggestions for improvements.
>> > >
>> > > To begin with, add thread cleanup handlers and
>> > > {more} error checking. ;-)
>> >
>> > Cleanup handlers? Where, and why?
>

> Thread cancelation? Bah! Anyone who tries to use
> that, richly deserves whatever happens to them.

Hey, if you can't handle cancellation, by all means don't use it in your
applications. However, if you're writing a library package that supports
POSIX threads, you should presume that a number of potential users WILL be
able to handle it, and will want to be able to properly exploit it.

POSIX cancellation and cleanup handlers are just a language-neutral
expression of exceptions; just like C++ exceptions and object destructors.
There's no real difference in how or why you use them. (As long as you stay
away from asynchronous cancelability, which is a completely different beast
that can be used only in extremely specialized and restricted ways.)

Well, either you write exception-safe code, or you don't.

If you don't, then you definitely deserve what happens to you. The question
is whether those who choose to use your package really deserve it.

> Still, a note to that effect in the README would
> not be amiss.

By all means, please craft a prominent warning in your README that your
package hasn't been taught to clean up after itself!

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

Sean P. Burke

unread,
Aug 27, 2002, 3:48:58 PM8/27/02
to

David Butenhof <David.B...@compaq.com> writes:

> Sean P. Burke wrote:
>
> > Alexander Terekhov <tere...@web.de> writes:
> >
> >> "Sean P. Burke" wrote:
> >> >
> >> > Alexander Terekhov <tere...@web.de> writes:
> >> >
> >> > > "Sean P. Burke" wrote:
> >> > > [...]
> >> > > > I welcome any suggestions for improvements.
> >> > >
> >> > > To begin with, add thread cleanup handlers and
> >> > > {more} error checking. ;-)
> >> >
> >> > Cleanup handlers? Where, and why?
> >
> > Thread cancelation? Bah! Anyone who tries to use
> > that, richly deserves whatever happens to them.
>
> Hey, if you can't handle cancellation, by all means don't use it in your
> applications. However, if you're writing a library package that supports
> POSIX threads, you should presume that a number of potential users WILL be
> able to handle it, and will want to be able to properly exploit it.

Yes, and a similar number who only think that their code is able
to handle it. :-/ I wonder how many of those using cancellation
are aware that printf, et. al. are cancellation points, and how
many have test suites that succeed in excercising these code paths.

> POSIX cancellation and cleanup handlers are just a language-neutral
> expression of exceptions; just like C++ exceptions and object destructors.
> There's no real difference in how or why you use them. (As long as you stay
> away from asynchronous cancelability, which is a completely different beast
> that can be used only in extremely specialized and restricted ways.)
>
> Well, either you write exception-safe code, or you don't.
>
> If you don't, then you definitely deserve what happens to you. The question
> is whether those who choose to use your package really deserve it.
>
> > Still, a note to that effect in the README would
> > not be amiss.
>
> By all means, please craft a prominent warning in your README that your
> package hasn't been taught to clean up after itself!

Let's say I decide to make the code deferred-cancel safe, at least in
the RWlock and queue codes, where user threads may block on conditions.
May I assume that the caller has chosen their thread's cancellation type
knowingly, and respect their choice, or does good style recommend that
the cancel type be set on entry to what my code can handle, and restored
prior to return:

int old_cancel_type;
int rc;
rc = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &old_cancel_type);
[ ... ]
rc = pthread_setcanceltype(&old_cancel_type, NULL);
return;

I also begrudge the cycles that are wasted in pthread_cleanup_push/pop
for codes that (wisely, IMHO) eschew thread cancellation. I suspect that
many pthread_cleanup_push() implementations call malloc, which would be
a huge overhead in something like a readers/writer lock. ( And yet,
it doesn't return any error code. If there is a fixed area for cleanup
handlers, then overflow is possilbe, or if the area isn't fixed,
it seems like ENOMEM might need to be returned. ???)

One could avoid pushing/popping cleanup handlers by testing the cancel
state of the calling thread, for example:

bool make_cancel_safe = false;
int old_cancel_state;
int rc;

rc = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancel_state);
make_cancel_safe = (old_cancel_state == PTHREAD_CANCEL_ENABLE);
rc = pthread_setcancelstate(old_cancel_state, NULL);

if (make_cancel_safe)
pthread_cleanup_push(handler, arg);
while (predicate)
pthread_cond_wait(&cond, &mutex);
if (make_cancel_safe)
pthread_cleanup_pop(0);

I'm not sure this would be a win, especially since many codes
(like mine) that don't use cancellation, also don't bother to
disable it.

-SEan


Alexander Terekhov

unread,
Aug 28, 2002, 5:54:03 AM8/28/02
to

"Sean P. Burke" wrote:
[...]
> Let's say I decide to make the code deferred-cancel safe, at least in
> the RWlock and queue codes, where user threads may block on conditions.

You should make it cancel-safe w.r.t. each and every cancelation {throw}
point used in your code, async-cancel regions aside for a moment.

> May I assume that the caller has chosen their thread's cancellation type
> knowingly, and respect their choice,

In addition to destructible/basic/strong/nothrow guarantees w.r.t. the
``state of resource(s)'' (exception/error-safety guarantees), you should
just clearly document (for each and every function) whether it is:

- no cancel point (doesn't ``throw cancel exception'');

- guaranteed deferred cancel point (does ``throw cancel exception''
if cancelability is enabled and there is pending cancel request);

- same as above but only if execution is going to be blocked
(race w.r.t. wakeup and cancel aside);

- "may occur" deferred cancel point (things like printf() or if you
use internal async-cancel regions w/o "manual" tests on region's
entry/exit);

- async-cancel-safe (... http://groups.google.com/groups?selm=3C926C82.AD56845D%40web.de).

> or does good style recommend that
> the cancel type be set on entry to what my code can handle, and restored
> prior to return:

I guess, "good style" is this: ;-)

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

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

[...]


> One could avoid pushing/popping cleanup handlers by testing the cancel

> state of the calling thread, for example: ....

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

< from pthread.h, "* AUTHORS: Dave Butenhof * CREATION DATE: 20 February 1990" >

"....
/*
* Implement push and pop for cancellation handlers, using TRY and ENDTRY
*/

#define pthread_cleanup_push(routine,arg) \
{ \
pthread_cleanup_t _XXX_proc = (pthread_cleanup_t)(routine); \
pthread_addr_t _XXX_arg = (arg); \
int _XXX_completed = 0; \
TRY {

#define pthread_cleanup_pop(execute) \
_XXX_completed = 1;} \
FINALLY { \
int _XXX_execute = execute; \
if ((! _XXX_completed) || (_XXX_execute)) _XXX_proc (_XXX_arg);} \
ENDTRY}
...."

regards,
alexander.

David Butenhof

unread,
Aug 28, 2002, 7:05:47 AM8/28/02
to
Sean P. Burke wrote:

> David Butenhof <David.B...@compaq.com> writes:
>
>> Sean P. Burke wrote:
>>
>> > Alexander Terekhov <tere...@web.de> writes:
>> >
>> >> "Sean P. Burke" wrote:
>> >> >
>> >> > Alexander Terekhov <tere...@web.de> writes:
>> >> >
>> >> > > "Sean P. Burke" wrote:
>> >> > > [...]
>> >> > > > I welcome any suggestions for improvements.
>> >> > >
>> >> > > To begin with, add thread cleanup handlers and
>> >> > > {more} error checking. ;-)
>> >> >
>> >> > Cleanup handlers? Where, and why?
>> >
>> > Thread cancelation? Bah! Anyone who tries to use
>> > that, richly deserves whatever happens to them.
>>
>> Hey, if you can't handle cancellation, by all means don't use it in your
>> applications. However, if you're writing a library package that supports
>> POSIX threads, you should presume that a number of potential users WILL
>> be able to handle it, and will want to be able to properly exploit it.
>
> Yes, and a similar number who only think that their code is able
> to handle it. :-/ I wonder how many of those using cancellation
> are aware that printf, et. al. are cancellation points, and how
> many have test suites that succeed in excercising these code paths.

One can make the same argument (much more easily) about programming with
threads AT ALL. Nor is there any need for those seeking opportunities for
programming errors to resort to asynchrony -- there are plenty of
opportunities in straight line C code.

So if you'd like to avoid all opportunity for programming errors, you'd best
find a new profession (or hobby if such is the case).

Otherwise, you should endeavor to remove programming errors from YOUR code,
so that those who wish to use it can concentrate on their own bugs instead
of yours.

As I said, if you really don't feel you can (or absolutely refuse to) deal
with cancellation in your code, that's your choice. But please at least
document the restriction so that anyone who might think to exploit your
library will have fair warning of the limitations.

>> POSIX cancellation and cleanup handlers are just a language-neutral
>> expression of exceptions; just like C++ exceptions and object
>> destructors. There's no real difference in how or why you use them. (As
>> long as you stay away from asynchronous cancelability, which is a
>> completely different beast that can be used only in extremely specialized
>> and restricted ways.)
>>
>> Well, either you write exception-safe code, or you don't.
>>
>> If you don't, then you definitely deserve what happens to you. The
>> question is whether those who choose to use your package really deserve
>> it.
>>
>> > Still, a note to that effect in the README would
>> > not be amiss.
>>
>> By all means, please craft a prominent warning in your README that your
>> package hasn't been taught to clean up after itself!
>
> Let's say I decide to make the code deferred-cancel safe, at least in
> the RWlock and queue codes, where user threads may block on conditions.
> May I assume that the caller has chosen their thread's cancellation type
> knowingly, and respect their choice, or does good style recommend that
> the cancel type be set on entry to what my code can handle, and restored
> prior to return:

The default for every thread is "deferred". Certain (and very few) functions
are documented as "async cancel safe", and ONLY those specific routines may
be called in a region of code where asynchronous cancelability has been
enabled. Asynchronous cancelability is not intended to be (and cannot be)
general purpose, and should be used only in very restricted local code.
Specifically, in a tight compute-bound loop where the overhead of checking
for a pending cancel might be considered unreasonable.

So if you don't document your library functions as async cancel safe (and
you SHOULDN'T), then nobody has any business calling them with async
cancelability. You don't need to worry about it.

> I also begrudge the cycles that are wasted in pthread_cleanup_push/pop
> for codes that (wisely, IMHO) eschew thread cancellation. I suspect that
> many pthread_cleanup_push() implementations call malloc, which would be
> a huge overhead in something like a readers/writer lock. ( And yet,
> it doesn't return any error code. If there is a fixed area for cleanup
> handlers, then overflow is possilbe, or if the area isn't fixed,
> it seems like ENOMEM might need to be returned. ???)

As I said before, cancellation is supposed to be an exception. Cleanup
"push" is intended to be something very much like a C++ "try {" while "pop"
is essentially a "} finally {<call cleanup handler>)}" if the argument is
TRUE, or something more like "} catch (...) {<call handler>; throw;}" if
it's FALSE.

If the platform implements exceptions competently, there is NO runtime
overhead in handling exceptions unless they occur. It should all be in
compile-time PC maps and function descriptors.

If a platform lacks true exception support, that's not a problem with the
standard -- it's a "quality of implementation" issue that ought to be
fixed. (Any modern system without true common system exception support is
simply broken. There's just no excuse.)

> One could avoid pushing/popping cleanup handlers by testing the cancel
> state of the calling thread, for example:
>
> bool make_cancel_safe = false;
> int old_cancel_state;
> int rc;
>
> rc = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancel_state);
> make_cancel_safe = (old_cancel_state == PTHREAD_CANCEL_ENABLE);
> rc = pthread_setcancelstate(old_cancel_state, NULL);
>
> if (make_cancel_safe)
> pthread_cleanup_push(handler, arg);
> while (predicate)
> pthread_cond_wait(&cond, &mutex);
> if (make_cancel_safe)
> pthread_cleanup_pop(0);

No, you can't, because you're breaking the lexical scope rules for push/pop.
They're intended and specified to behave as if push ends with an "{" and
pop begins with the matching "}". BECAUSE they're supposed to be exception
scope language syntax. Any conflicting use is erronous, illegal, and
non-portable.

> I'm not sure this would be a win, especially since many codes
> (like mine) that don't use cancellation, also don't bother to
> disable it.

When writing an APPLICATION that doesn't use cancellation, you can often get
away with that. (Except in special cases where you may provide callback
routines into threads created by a library.) It's always a good idea to
write all code cancel-safe -- it's easier to do when you're first coding
than to add later. Still, it's in fairly bad taste to cancel a thread you
didn't create, so you'll rarely run into a problem there. Maybe, once in a
while, some library will try to pthread_exit() on an abnormal condition
(also something in supremely poor taste when running in a thread you didn't
create, but possible). Again... "usually" you can get away with it.

When you're writing code that's called in someone else's thread, you're
simply obligated to do certain things: like making your code thread-safe...
and making it cancel-safe. If you choose to disable cancelability on entry
and restore the previous setting on exit, that's legal, but if the code can
block any substantial time it's unfair to the caller. (And might make an
otherwise appealing library unusable.)

If you're writing in C++, you write object destructors. If you don't, maybe
nobody will notice (for a while anyway), but you're violating the implicit
contract of the language capabilities, and sooner or later you'll get into
trouble. It's the same with threads and cancellation.

Again, this is your code, and your decision. But if you really choose to
write incomplete code, at least do your potential users the minimal favor
of making that clear up front in your documentation. That's really all I
asked.

Alexander Terekhov

unread,
Aug 28, 2002, 8:08:03 AM8/28/02
to

David Butenhof wrote:
[...]

> So if you don't document your library functions as async cancel safe
> (and you SHOULDN'T),
^^^^^^^^^^^^^^^^^

Heck, on the contrary, why not make quite a few STANDARD functions,
for example: strlen/strcpy/str* (stuff WITHOUT any internally done
resource "alloc/free") *async-cancel-safe*... with "basic exception
safety guarantee", of course?

regards,
alexander.

Alexander Terekhov

unread,
Aug 28, 2002, 10:37:49 AM8/28/02
to

David Butenhof wrote:
[...]

> As I said before, cancellation is supposed to be an exception. Cleanup
> "push" is intended to be something very much like a C++ "try {" while "pop"
^^^^^^^^^^

> is essentially a "} finally {<call cleanup handler>)}" if the argument is

^^^^^^^

> TRUE, or something more like "} catch (...) {<call handler>; throw;}" if

> it's FALSE. ^^^^^^^^^^^

Nah, "Solution 4: Petru's Approach" ;-)

http://www.cuj.com/experts/1812/alexandr.htm?topic=experts
(Generic<Programming>: Simplify Your Exception-Safe Code,
Andrei Alexandrescu and Petru Marginean)

#define pthread_cleanup_push(routine,arg) \
{ \
ScopeGuard _guard = MakeObjGuard(arg,routine);
//^^^^^^^^^^--> typedef const ScopeGuardImplBase& ScopeGuard;

#define pthread_cleanup_pop(execute) \
if (!execute) _guard.Dismiss(); \
}

Or am I missing something?

regards,
alexander.

David Butenhof

unread,
Aug 28, 2002, 12:45:18 PM8/28/02
to
Alexander Terekhov wrote:

> David Butenhof wrote:
> [...]
>> As I said before, cancellation is supposed to be an exception. Cleanup
>> "push" is intended to be something very much like a C++ "try {" while
>> "pop"
>

> Nah, "Solution 4: Petru's Approach" ;-)
>
> http://www.cuj.com/experts/1812/alexandr.htm?topic=experts
> (Generic<Programming>: Simplify Your Exception-Safe Code,
> Andrei Alexandrescu and Petru Marginean)
>
> #define pthread_cleanup_push(routine,arg) \
> { \
> ScopeGuard _guard = MakeObjGuard(arg,routine);
> //^^^^^^^^^^--> typedef const ScopeGuardImplBase& ScopeGuard;
>
> #define pthread_cleanup_pop(execute) \
> if (!execute) _guard.Dismiss(); \
> }
>
> Or am I missing something?

That's certainly one practical way to implement a "conditional finally
clause" using stock C++.

And yes, in "real C++" it would make sense to implement cleanup push and pop
with a guard object of some sort. (I was trying to stick to a sort of
"pseudo C++" that keeps to a basic exception model without dragging in
objects.) On the other hand, my idea of a "C++ POSIX thread binding"
wouldn't have pthread_cleanup_push or pthread_cleanup_pop anyway, because
they're redundant in a language that already has exceptions and object
destructors.

Sean P. Burke

unread,
Aug 28, 2002, 2:16:28 PM8/28/02
to

My thanks to David and Alexander for the constructive advice.
I will do a new spin of the package with cancellation-safety
in mind. Look for an announcement here in a few days.

-SEan

Alexander Terekhov

unread,
Aug 28, 2002, 2:46:45 PM8/28/02
to

David Butenhof wrote:
[...]

> And yes, in "real C++" it would make sense to implement cleanup push and pop
> with a guard object of some sort. (I was trying to stick to a sort of
> "pseudo C++" that keeps to a basic exception model without dragging in
> objects.)

Yes, I understand. I just wanted to point out that in C++, it is rather
easy to write exception-safe code without any "superficial" try/catch/
rethrow constructs; simply using RAII "idiom" (scope guards). For some
strange reason people really like to try/catch(...) and rethrow, however.

Plauger's standard C++ library (STLport too), "for example", is FULL of
such silly constructs. I don't get it. Well, Abrahams claims that scope
guards (cleanup objects) aren't "that efficient" comparing with try/catch/
rethrow. I disagree. (I'm attaching below a couple of recent messages of
mine on this topic)

> On the other hand, my idea of a "C++ POSIX thread binding"
> wouldn't have pthread_cleanup_push or pthread_cleanup_pop anyway, because
> they're redundant in a language that already has exceptions and object
> destructors.

Yep. Thanks.

regards,
alexander.

-------- Original Message --------
Message-ID: <3D6A4D0B...@web.de>
Date: Mon, 26 Aug 2002 17:45:15 +0200
From: Alexander Terekhov <tere...@web.de>
Newsgroups: comp.lang.c++.moderated
Subject: Re: feedback on wrapped boost smart pointers

"Sergey P. Derevyago" wrote:
>
> Alexander Terekhov wrote:
> > > explicit sh_ptr(T* ptr_)
> > > {
> > > try { rep=new Rep(ptr_); }
> > > catch (...) { delete ptr_; }
> > > }
> > Uhmm. Care to elaborate on this
> It should be possible to write
> sh_ptr<T> sh(new T(whatever));
> without a resource leak.

Ahhh, ``okay.'' ;-) ;-)

explicit sh_ptr(T* ptr_)
{
std::auto_ptr<T> auto_ptr_(ptr_); // ``simple and terse'' res.manager
rep = new Rep(auto_ptr_); // pass ownership to rep's auto_ptr
}

> > > sh_ptr& operator=(const sh_ptr& shp)
> > > {
> > > if (this!=&shp) {
> > > deleteRep();
> > > copyRep(shp);
> > > }
> > >
> > > return *this;
> > > }
> >
> > [snip swap]
> >
> > Sergey, why not: (really simple >>AND TERSE<< ;-) )
> >
> > sh_ptr<T>& operator=(sh_ptr<T> temp) throw() // ;-) ;-)
> > { return this->swap( temp ); }
> >
> > <?>
> Performance.

Uhmm. Care to elaborate on this? ;-)

regards,
alexander.

-------- Original Message --------
Message-ID: <3D6CB886...@web.de>
Date: Wed, 28 Aug 2002 13:48:22 +0200
From: Alexander Terekhov <tere...@web.de>
Newsgroups: comp.lang.c++.moderated
Subject: Re: feedback on wrapped boost smart pointers

Peter Dimov wrote:
>
> "Sergey P. Derevyago" <non-ex...@iobox.com> wrote in message news:<3D6A29D9...@iobox.com>...
> > Alexander Terekhov wrote:
> > > > explicit sh_ptr(T* ptr_)
> > > > {
> > > > try { rep=new Rep(ptr_); }
> > > > catch (...) { delete ptr_; }
> > > > }
> > > Uhmm. Care to elaborate on this
> > It should be possible to write
> > sh_ptr<T> sh(new T(whatever));
> > without a resource leak.
>
> You forgot to rethrow the exception.

std::auto_ptr-like approach without silly try/catch(...) is MUCH
better, in my view. Frankly, I'd rather make catch(...) generate
compiler diagnostic... unless the entire stuff in the try scope
is nothing (automatic storage; optional stack overflow exception
aside for a moment) but invocations of function(s) with EXCEPTION
SPECIFICATIONS... and those will hopefully become "revised&fixed"
(get rid of their catch(...) "semantics", but PRESERVING run-time
checking) in the next {C/}C++ standardization round.

> > > > sh_ptr& operator=(const sh_ptr& shp)
> > > > {
> > > > if (this!=&shp) {
> > > > deleteRep();
> > > > copyRep(shp);
> > > > }
> > > >
> > > > return *this;
> > > > }
> > >
> > > [snip swap]
> > >
> > > Sergey, why not: (really simple >>AND TERSE<< ;-) )
> > >
> > > sh_ptr<T>& operator=(sh_ptr<T> temp) throw() // ;-) ;-)
> > > { return this->swap( temp ); }
> > >
> > > <?>
> > Performance.
>
> If destroying this->rep also destroys shp, your version doesn't work.
> It's not easy to find this bug, and you are in good company as
> boost::shared_ptr also had it at one time IIRC. :-)
>
> struct X
> {
> X(): next(0) {}
> sh_ptr<X> next;
> };
>
> int main()
> {
> sh_ptr<X> p(new X);
> p->next = sh_ptr<X>(new X);
> p = p->next;
> }

Nice example.

regards,
alexander.


-------- Original Message --------
Message-ID: <3D3EB392...@web.de>
Date: Wed, 24 Jul 2002 16:02:58 +0200
From: Alexander Terekhov <tere...@web.de>
Newsgroups: comp.lang.c++.moderated,no.it.programmering.c++,comp.programming.threads
Subject: Re: Is internal catch-clause rethrow standard?

David Abrahams wrote:
[...]
> Uh, yeah, now that you mention it: on the reality side, my standard library
> implementation is likely to use catch(...) anyway.

< a couple of days earlier >

Alexander Terekhov wrote:
>
> David Abrahams wrote:
> [...]
> > How would you write uninitialized_fill without catch(...)?
>
> Show me the code, please.
>
> [...]
> > Yeah, but if you're interfacing with other peoples' libraries, or writing
> > templates, or writing frameworks that supply base classes with virtual
> > functions, you don't always have control over the types of exceptions that
> > get thrown. Writing certain kinds of exception-neutral code efficiently can
> > be extremely difficult without catch(...).
>
> Examples?

< Still no reply; Okay, "Bringing the mountain to Mmm.." er. Abrahams ;-) >

C:\IBMCPP40\include\memory

------
<snip>
/********************************************************************/
/* <memory> header file */
/* */
/* VisualAge C++, Version 4.0 */
/* Licensed Material - Property of IBM */
/* */
/* 5801-AAR and Other Materials */
/* */
/* (c) Copyright IBM Corp 1991, 1998. All rights reserved. */
/* */
/* */
/* Licensed Materials. */
/* */
/* Dinkum C++ Library */
/* Copyright (c) 1998. Licensed to IBM Corp. and its suppliers. */
/* */
/********************************************************************/
<snip>
// TEMPLATE FUNCTION uninitialized_fill
template<class _FI, class _Ty> inline
void uninitialized_fill(_FI _F, _FI _L, const _Ty& _X)
{for (; _F != _L; ++_F)
_Construct(&*_F, _X); }

<snip>
/*
* Copyright (c) 1995 by P.J. Plauger. ALL RIGHTS RESERVED.
* Consult your license regarding permissions and restrictions.
*/

/*
* This file is derived from software bearing the following
* restrictions:
*
* Copyright (c) 1994
* Hewlett-Packard Company
<snip>
------


C:\Program Files\Microsoft Visual Studio\VC98\Include\memory

------
<snip>
// TEMPLATE FUNCTION uninitialized_fill
template<class _FI, class _Ty> inline
void uninitialized_fill(_FI _F, _FI _L, const _Ty& _X)
{for (; _F != _L; ++_F)
_Construct(&*_F, _X); }

<snip>
/*
* Copyright (c) 1995 by P.J. Plauger. ALL RIGHTS RESERVED.
* Consult your license regarding permissions and restrictions.
*/

/*
* This file is derived from software bearing the following
* restrictions:
*
* Copyright (c) 1994
* Hewlett-Packard Company
<snip>
------


C:\Program Files\Microsoft Visual Studio .NET\Vc7\include\memory:

------
<snip>
// TEMPLATE FUNCTION uninitialized_fill
template<class _FwdIt,
class _Tval> inline
void uninitialized_fill(_FwdIt _First, _FwdIt _Last, const _Tval& _Val)
{ // copy _Val throughout raw [_First, _Last)
_Uninit_fill(_First, _Last, _Val, _Ptr_cat(_First, _First));
}

template<class _FwdIt,
class _Tval> inline
void _Uninit_fill(_FwdIt _First, _FwdIt _Last, const _Tval& _Val,
_Nonscalar_ptr_iterator_tag)
{ // copy _Val throughout raw [_First, _Last), arbitrary type
_FwdIt _Next = _First;

_TRY_BEGIN
for (; _First != _Last; ++_First)
_Construct(&*_First, _Val);
_CATCH_ALL
for (; _Next != _First; ++_Next)
_Destroy(&*_Next);
_RERAISE;
_CATCH_END
}

<snip>
/*
* Copyright (c) 1992-2001 by P.J. Plauger. ALL RIGHTS RESERVED.
* Consult your license regarding permissions and restrictions.
*/

/*
* This file is derived from software bearing the following
* restrictions:
*
* Copyright (c) 1994
* Hewlett-Packard Company
<snip>
----


C:\Documents and Settings\Terekhov\ABRAuhmmCADABRA.cpp:

----
<snip>

template< class _FwdIt >
class _Uninit_fill_cleanup {
_FwdIt const& m_First;
_FwdIt const& m_Last;
_FwdIt m_Next;
public:

_Uninit_fill_cleanup( _FwdIt const& _First, _FwdIt const& _Last ) :
m_First( _First ), m_Last( _Last ), m_Next( _First ) {
}

~_Uninit_fill_cleanup() {
if ( m_First != m_Last )
for (; m_First != m_Next; ++m_Next)
_Destroy(&*m_Next);
}

};

template< class _FwdIt, class _Tval >
inline void _Uninit_fill( _FwdIt _First, _FwdIt _Last, const _Tval& _Val ) {
_Uninit_fill_cleanup< _FwdIt > _Cleanup( _First, _Last );
for (; _First != _Last; ++_First)
_Construct(&*_First, _Val);
}

<snip>
---

< dreams on >

template< context > // see "10-o'clock-wish-list"
class _Uninit_fill_cleanup {
typeof( context._First ) m_Next;
public:

_Uninit_fill_cleanup() :
m_Next( context._First ) {
}

~_Uninit_fill_cleanup() {
if ( context.m_First != context.m_Last )
for (; context.m_First != m_Next; ++m_Next)
_Destroy(&*m_Next);
}

};

template< class _FwdIt, class _Tval>
inline void _Uninit_fill( _FwdIt _First, _FwdIt _Last, const _Tval& _Val ) {
_Uninit_fill_cleanup< context > _Cleanup();
for (; _First != _Last; ++_First)
_Construct(&*_First, _Val);
}

< dreams off >

regards,
alexander.

Hillel Y. Sims

unread,
Aug 28, 2002, 11:10:50 PM8/28/02
to

"David Butenhof" <David.B...@compaq.com> wrote in message
news:f82b9.10$oh7.2...@news.cpqcorp.net...

> Asynchronous cancelability is not intended to be (and cannot be)
> general purpose, and should be used only in very restricted local code.
> Specifically, in a tight compute-bound loop where the overhead of checking
> for a pending cancel might be considered unreasonable.

...except that it's not necessarily guaranteed by posix standard that
cancellation will actually be delivered at any point during the async cancel
block anyway, right? ;-)

>
> If you're writing in C++, you write object destructors. If you don't,
maybe
> nobody will notice (for a while anyway), but you're violating the implicit
> contract of the language capabilities, and sooner or later you'll get into
> trouble. It's the same with threads and cancellation.
>

unfortunately, many C++ / pthread implementations (eg, linux, solaris) don't
implement thread cancellation as an exception, making it heinous to write
cancel-safe (aka thread-safe) C++ code..

general C++ suggestions to achieve cancel-safety (for superior systems where
it is implemented via an exception: VMS, Tru64, pthreads-win32, IBM):
- follow standard techniques to achieve at least basic exception safety in
all code
- disable cancellation state during object destructors and nothrow code, if
they may encounter any cancel points (aka calls to printf, fclose, etc...) -
to avoid issues of either throwing in nothrow code or swallowing cancel
exception. I use the following RAII guard object as needed:

class CancelGuard : boost::noncopyable
{
public:
CancelGuard()
{ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &m_origState); }
~CancelGuard()
{ pthread_setcancelstate(m_origState, 0); }

private:
int m_origState;
};

#define CANCEL_GUARD CancelGuard TEXT_CONCAT_MACRO(CANCELGUARD, __LINE__)

Seems like it might be useful if "throw()" ES spec could do that for you
automatically in future C++0x..?

hys
--
Hillel Y. Sims
FactSet Research Systems
hsims AT factset.com


Mike Mowbray

unread,
Aug 29, 2002, 12:10:18 AM8/29/02
to
"Hillel Y. Sims" wrote:

> unfortunately, many C++ / pthread implementations (eg, linux,
> solaris) don't implement thread cancellation as an exception,
> making it heinous to write cancel-safe (aka thread-safe) C++ code.

Huh? On Solaris? Using Sun C++ and Solaris, it's easy to
write cancel-safe C++ code since destructors get called
automatically when you cancel a thread.

- MikeM.


Alexander Terekhov

unread,
Aug 29, 2002, 5:01:02 AM8/29/02
to

I'm just curious: what about catch(...) blocks -- do they get
called automatically when you cancel/exit a thread? What if
catch(...) doesn't rethrow? Do they use "forced unwinding"
(for thread exit and cancel) ala Intel/HP/etc. IA64 C++ ABI?

regards,
alexander.

David Butenhof

unread,
Aug 29, 2002, 8:18:40 AM8/29/02
to
Alexander Terekhov wrote:

> David Butenhof wrote:
> [...]
>> And yes, in "real C++" it would make sense to implement cleanup push and
>> pop with a guard object of some sort. (I was trying to stick to a sort of
>> "pseudo C++" that keeps to a basic exception model without dragging in
>> objects.)
>
> Yes, I understand. I just wanted to point out that in C++, it is rather
> easy to write exception-safe code without any "superficial" try/catch/
> rethrow constructs; simply using RAII "idiom" (scope guards). For some
> strange reason people really like to try/catch(...) and rethrow, however.

I agree that there are a lot of people who write C++ that doesn't take full
advantage of the power of destructors. However, although guard objects can
be a "neat" and "clever" way to clean up, it's not the only way supported
by C++, and it's not the only "right" way.

Where it's more convenient, there's nothing wrong with catch() {... throw;}
idioms. There are also places where you really want to finalize an
exception, using catch() without re-throwing, and those needs cannot be met
with guard objects (which will always propagate the exception).

You're clearly wrong about efficiency, though. With any sort of REAL
exception support try/catch is free (at runtime) unless an exception
occurs; whereas the guard object (if is has no other function) adds
allocation, initialization, destruction, and deallocation overhead. If you
do that frequently, the effect on the application could be substantial.

David Butenhof

unread,
Aug 29, 2002, 8:38:54 AM8/29/02
to
Hillel Y. Sims wrote:

>
> "David Butenhof" <David.B...@compaq.com> wrote in message
> news:f82b9.10$oh7.2...@news.cpqcorp.net...
>> Asynchronous cancelability is not intended to be (and cannot be)
>> general purpose, and should be used only in very restricted local code.
>> Specifically, in a tight compute-bound loop where the overhead of
>> checking for a pending cancel might be considered unreasonable.
>
> ...except that it's not necessarily guaranteed by posix standard that
> cancellation will actually be delivered at any point during the async
> cancel block anyway, right? ;-)

Like many such issues, we decided to leave this sort of guarantee up to the
implementation. This doesn't affect how and why an application might want
to use a feature... only whether an application developer might choose to
deploy and rely on a particular implementation.

It is, in short, a "quality of implementation" issue, not a standardization
issue. That's why (despite some pressure from the hard-core realtime people
in the working group) we did not document or require implementation
documentation of the realtime scheduling latencies for various operations.
That's just not stuff an application interface standard should be doing,
because it unreasonably limits the scope of applicability. (Most
applications and implementations won't care, and there's no reason they
should.)

>> If you're writing in C++, you write object destructors. If you don't,
> maybe
>> nobody will notice (for a while anyway), but you're violating the
>> implicit contract of the language capabilities, and sooner or later
>> you'll get into trouble. It's the same with threads and cancellation.
>
> unfortunately, many C++ / pthread implementations (eg, linux, solaris)
> don't implement thread cancellation as an exception, making it heinous to
> write cancel-safe (aka thread-safe) C++ code..

If you're sticking to "pure standards" and portable code, it's impossible to
write THREADED C++ code. Period. Because C++ doesn't recognize threads, and
POSIX doesn't deal with C++. So once you've made that leap you're again
into "quality of implementation". Any reasonable implementation will make
sure they work together. Why use anything that doesn't? If you choose to,
that's your decision and you live with the limitations that decision
imposes.

Although Solaris has had an uneven and generally bad history in this area,
they have at least made some attempts. It appears, from reports, that at
this point it mostly generally sorta kinda works for most people.

I won't say anything about Linux in this context, because there'd be no
point.

> general C++ suggestions to achieve cancel-safety (for superior systems
> where it is implemented via an exception: VMS, Tru64, pthreads-win32,
> IBM): - follow standard techniques to achieve at least basic exception
> safety in all code
> - disable cancellation state during object destructors and nothrow code,
> if they may encounter any cancel points (aka calls to printf, fclose,
> etc...) - to avoid issues of either throwing in nothrow code or swallowing
> cancel exception. I use the following RAII guard object as needed:
>
> class CancelGuard : boost::noncopyable
> {
> public:
> CancelGuard()
> { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &m_origState); }
> ~CancelGuard()
> { pthread_setcancelstate(m_origState, 0); }
>
> private:
> int m_origState;
> };
>
> #define CANCEL_GUARD CancelGuard TEXT_CONCAT_MACRO(CANCELGUARD, __LINE__)
>
> Seems like it might be useful if "throw()" ES spec could do that for you
> automatically in future C++0x..?

Hmm. That's certainly an interesting idea to consider, anyway.

Alexander Terekhov

unread,
Aug 29, 2002, 10:38:33 AM8/29/02
to

David Butenhof wrote:
[...]

> You're clearly wrong about efficiency, though. With any sort of REAL
> exception support try/catch is free (at runtime) unless an exception
> occurs;

Yes, but "const&" members (inside guard object) can also be "free",
please see below.

> whereas the guard object (if is has no other function) adds
> allocation, initialization, destruction, and deallocation overhead.

Maybe. Well, consider the following code:

template<class _FwdIt,
class _Tval> inline
void _Uninit_fill(_FwdIt _First, _FwdIt _Last, const _Tval& _Val,
_Nonscalar_ptr_iterator_tag)
{ // copy _Val throughout raw [_First, _Last), arbitrary type
_FwdIt _Next = _First;

_TRY_BEGIN
for (; _First != _Last; ++_First)
_Construct(&*_First, _Val);
_CATCH_ALL
for (; _Next != _First; ++_Next)
_Destroy(&*_Next);
_RERAISE;
_CATCH_END
}

Now consider the following somewhat verbose "alternative":

template< class _FwdIt >
class _Uninit_fill_cleanup {
_FwdIt const& m_First;
_FwdIt const& m_Last;
_FwdIt m_Next;
public:

_Uninit_fill_cleanup( _FwdIt const& _First, _FwdIt const& _Last ) :
m_First( _First ), m_Last( _Last ), m_Next( _First ) {
}

~_Uninit_fill_cleanup() {
if ( m_First != m_Last ) // if ( std::unwinding(this) ) // exceptional path


for (; m_First != m_Next; ++m_Next)
_Destroy(&*m_Next);
}

};

template< class _FwdIt, class _Tval >
inline void _Uninit_fill( _FwdIt _First, _FwdIt _Last, const _Tval& _Val ) {
_Uninit_fill_cleanup< _FwdIt > _Cleanup( _First, _Last );
for (; _First != _Last; ++_First)
_Construct(&*_First, _Val);
}

Finally, consider this:

template< context > // see "10-o'clock-wish-list"
class _Uninit_fill_cleanup {
typeof( context._First ) m_Next;
public:

_Uninit_fill_cleanup() :
m_Next( context._First ) {
}

~_Uninit_fill_cleanup() {
if ( std::unwinding(this) ) // exceptional path
for (; context._First != m_Next; ++m_Next)
_Destroy(&*m_Next);
}

};

template< class _FwdIt, class _Tval>
inline void _Uninit_fill( _FwdIt _First, _FwdIt _Last, const _Tval& _Val ) {
_Uninit_fill_cleanup< context > _Cleanup();
for (; _First != _Last; ++_First)
_Construct(&*_First, _Val);
}

Am I really clearly wrong about efficiency here? To me, there
should be no difference whatsoever (using some smart compiler,
of course). The advantage is that using guard objects, nothing
"unknown"/"unexpected" is caught.

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

template<class _FwdIt,
class _Tval> inline
void _Uninit_fill(_FwdIt _First, _FwdIt _Last, const _Tval& _Val,
_Nonscalar_ptr_iterator_tag)
{ // copy _Val throughout raw [_First, _Last), arbitrary type
_FwdIt _Next = _First;

try {


for (; _First != _Last; ++_First)
_Construct(&*_First, _Val);
}

action_on_propagation_of(...) { /* THIS DOESN'T CATCH *UNEXPECTED* EXCEPTIONS */
/* THIS DOES RETHROW EXCEPTIONS AUTOMATICALLY */
/* NOTHING ELSE CAN BE THROWN FROM THIS SCOPE */


for (; _Next != _First; ++_Next)
_Destroy(&*_Next);
}
}

too; probably. ;-)

regards,
alexander.

Mike Mowbray

unread,
Aug 30, 2002, 1:05:22 AM8/30/02
to

I wrote:

>> [...] Using Sun C++ and Solaris, it's easy to write


>> cancel-safe C++ code since destructors get called
>> automatically when you cancel a thread.

and Alexander Terekhov asked:

> I'm just curious: what about catch(...) blocks -- do they get
> called automatically when you cancel/exit a thread? What if
> catch(...) doesn't rethrow? Do they use "forced unwinding"
> (for thread exit and cancel) ala Intel/HP/etc. IA64 C++ ABI?

I'm not sure what you mean. If a function f1() enters a try{} block
within which it calls another function f2(), and the thread is
cancelled while in f2(), why would you expect the catch(FooBar) block
in f1() to be called? No FooBar exception has been thrown anywhere.

- MikeM.


Alexander Terekhov

unread,
Aug 30, 2002, 5:44:32 AM8/30/02
to

Mike Mowbray wrote:
[...]

> > I'm just curious: what about catch(...) blocks -- do they get
> > called automatically when you cancel/exit a thread? What if
> > catch(...) doesn't rethrow? Do they use "forced unwinding"
> > (for thread exit and cancel) ala Intel/HP/etc. IA64 C++ ABI?
>
> I'm not sure what you mean. ....

A) "what about catch(...) blocks -- do they get


called automatically when you cancel/exit a thread"

void f2()
{
pthread_cancel( pthread_self() );
pthread_testcancel();
// pthread_exit( 0 );
}

void f1()
{
try {
f2();
}
catch(...) {
std::cout << "catch(...)/rethrow" << std::endl;
throw;
}
}

B) "What if catch(...) doesn't rethrow? Do they use "forced unwinding"


(for thread exit and cancel) ala Intel/HP/etc. IA64 C++ ABI?"

void f2()
{
pthread_cancel( pthread_self() );
pthread_testcancel();
// pthread_exit( 0 );
}

void f1()
{
try {
f2();
}
catch(...) {
std::cout << "catch(...)/ignore" << std::endl;
//throw;
}
std::cout << "I'm here" << std::endl;
}

regards,
alexander.

Alexander Terekhov

unread,
Aug 30, 2002, 5:50:57 AM8/30/02
to

Alexander Terekhov wrote:
>
> Mike Mowbray wrote:
> [...]
> > > I'm just curious: what about catch(...) blocks -- do they get
> > > called automatically when you cancel/exit a thread? What if
> > > catch(...) doesn't rethrow? Do they use "forced unwinding"
> > > (for thread exit and cancel) ala Intel/HP/etc. IA64 C++ ABI?
> >
> > I'm not sure what you mean. ....
>
> A) "what about catch(...) blocks -- do they get
> called automatically when you cancel/exit a thread"
>
> void f2()
> {
> pthread_cancel( pthread_self() );
> pthread_testcancel();
> // pthread_exit( 0 );

^^^
Nah, pthread_exit( PTHREAD_CANCELED ); // ;-) ;-)

regards,
alexander.

Sean P. Burke

unread,
Aug 30, 2002, 10:20:18 PM8/30/02
to

sbu...@dev0.welkyn.com (Sean P. Burke) writes:

> I've created a package of nifty pthread utilities that may
> be useful to the readers of this group. This package is
> distributed under a BSD-ish copyright: you are free to
> do anything with the code, except remove the copyright.
>
> You can download this package from:
>
> ftp://ftp.xenadyne.com/pub/Nifty.tgz
>
> The package includes the following utilities,
> implemented in ANSI C:
>
> * Task Scheduler
>
> This utility allows you to submit tasks for execution
> at a later time, and cancel tasks before they execute.
> This is especially useful for doing timeouts, e.g.:
> 1. schedule a pthread_kill(my_self, sig),
> 2. read a blocking socket,
> 3. cancel the timeout task.
>
> You can also schedule tasks to repeat at regular intervals.
>
> * Work Queue
>
> Based on the work queue from Butenhof, section 7.2,
> this allows you submit tasks to a "thread pool".
>
> * Message Queue
>
> This is a basic message queue for implementing
> message-passing and producer/consumer systems.
>
> * Readers/Writer Lock
>
> If your system lacks posix readers/writer locks.
>
> * Thread-safe popen/pclose
>
> Thread-safe substitutes for popen() and pclose()
> that use file descriptors rather than FILE *'s,
> to play nice with poll() and select().


>
> I welcome any suggestions for improvements.

> -SEan

Due to popular demand, these codes are now cancellation-safe.
The latest version is in the same place:

ftp://ftp.xenadyne.com/pub/Nifty.tgz

-SEan

Mike Mowbray

unread,
Sep 3, 2002, 11:29:35 PM9/3/02
to
Alexander Terekhov wrote:

> >> I'm just curious: what about catch(...) blocks -- do they get
> >> called automatically when you cancel/exit a thread? What if
> >> catch(...) doesn't rethrow? Do they use "forced unwinding"
> >> (for thread exit and cancel) ala Intel/HP/etc. IA64 C++ ABI?
>

Adding a main() to your code example below, like this:

main()
{
f1();
return 0;
}

and compiling with Sun C++ 5.3 with "-mt" and "-lpthread"
results in no cout output in both cases (A) and (B).
I.e: you can't catch() a cancellation. Is that what
you were asking?

- MikeM.


----- Alexander's previous code examples follow -------

Alexander Terekhov

unread,
Sep 4, 2002, 5:35:38 AM9/4/02
to

Mike Mowbray wrote:
>
> Alexander Terekhov wrote:
>
> > >> I'm just curious: what about catch(...) blocks -- do they get
> > >> called automatically when you cancel/exit a thread? What if
> > >> catch(...) doesn't rethrow? Do they use "forced unwinding"
> > >> (for thread exit and cancel) ala Intel/HP/etc. IA64 C++ ABI?
> >
>
> Adding a main() to your code example below, like this:
>
> main()
> {
> f1();
> return 0;
> }
>
> and compiling with Sun C++ 5.3 with "-mt" and "-lpthread"
> results in no cout output in both cases (A) and (B).
> I.e: you can't catch() a cancellation. Is that what
> you were asking?

Yep. Thank you, Mike. Now the key question: could you please do
a search for "catch(...)"/"CATCH_ALL" in the include directory
that contains Standard C++ Library headers... I'm just curious
how many hits you'll get. If you won't find anything resembling
"catch-whatever", well, then please post here the implementation
of uninitialized_fill from the <memory> header.

regards,
alexander.

Mike Mowbray

unread,
Sep 4, 2002, 8:14:44 PM9/4/02
to
> I wrote:

>
> >> [...] compiling with Sun C++ 5.3 with "-mt" and "-lpthread"
> >> results in no cout output in both cases [...].


> >> I.e: you can't catch() a cancellation. Is that what
> >> you were asking?

Alexander Terekhov wrote:

>
> > Yep. Thank you, Mike. Now the key question: could you please do
> > a search for "catch(...)"/"CATCH_ALL" in the include directory
> > that contains Standard C++ Library headers... I'm just curious
> > how many hits you'll get. If you won't find anything resembling
> > "catch-whatever", well, then please post here the implementation
> > of uninitialized_fill from the <memory> header.

Sun standard c++ library stuff seems to based on Rogue Wave software.
There's lots of catch(...) in deque.cc, iomanip, [io]stream,
[io]stream.cc, vector.

Regarding uninitialized_fill from the <memory> header, the copyright
notice in the file makes it pretty clear that I'd be in violation
if I posted it without permission on a public newsgroup. But after
looking at the implementation, I think I can guess what you suspect.
Are you thinking that some parts of the memory region (not) constructed
during the uninitialized_fill will get destructors called in rubbish
memory if a cancellation occurs during uninitialized_fill? If so,
could you post a code example that exposes the problem and I'll
happily try it.

- MikeM.

Alexander Terekhov

unread,
Sep 5, 2002, 5:44:08 AM9/5/02
to

Mike Mowbray wrote:
[...]

> Sun standard c++ library stuff seems to based on Rogue Wave software.
> There's lots of catch(...) in deque.cc, iomanip, [io]stream,
> [io]stream.cc, vector.

Yeah, ``surprise surprise.'' ;-)

> Regarding uninitialized_fill from the <memory> header, the copyright
> notice in the file makes it pretty clear that I'd be in violation
> if I posted it without permission on a public newsgroup.

Does it have "catch(...)" to cleanup/destruct already constructed stuff
if some constructor (invoked via "placement new") would throw?

> But after
> looking at the implementation, I think I can guess what you suspect.
> Are you thinking that some parts of the memory region (not) constructed
> during the uninitialized_fill will get destructors called in rubbish
> memory if a cancellation occurs during uninitialized_fill?

No. I suspect that destructors/cleanup-stuff won't be called at all...
due to catch(...) NOT being "called" (on thread cancel/exit).

regards,
alexander.

Mike Mowbray

unread,
Sep 6, 2002, 12:45:12 AM9/6/02
to
> I wrote:
>
> >> Regarding uninitialized_fill from the <memory> header, the copyright
> >> notice in the file makes it pretty clear that I'd be in violation
> >> if I posted it without permission on a public newsgroup.

Alexander Terekhov wrote:

> > Does it have "catch(...)" to cleanup/destruct already constructed stuff
>
> > if some constructor (invoked via "placement new") would throw?

> [...] I suspect that destructors/cleanup-stuff won't be called at all...


> due to catch(...) NOT being "called" (on thread cancel/exit).

Yes, that *does* look like what would happen.

(...and now I sit back and wonder why I've never had any trouble with
cancellation in Sun C++ & Solaris pthreads. Must be because I
instinctively avoid dodgy design styles that would expose such bugs.)

- MikeM.

Hillel Y. Sims

unread,
Sep 7, 2002, 12:32:15 AM9/7/02
to
BTW, the same issue exists on VMS 7.3 / cxx 6.5, which uses Rogue Wave's STL
headers too, with the same catch(...) and "you can't catch() a cancellation
exception" issue (I would guess Tru64 unix too?).. I guess depending on your
use of thread cancel / exit you might not particularly come across it very
often (would indeed be manifest as resource leaks). A similar issue also
exists with old non-exception C++ code using stack-based objects (dtors are
not run on thread cancel/exit), but we've only really encountered minor
memory leaks due to this a few times in actual practice (in our older code
base). I am definitely in strong favor of exception-based cancel/exit idiom
combined with real C++ exception-handling. Well we generally use cancel only
during system shutdown and threads usually complete themselves, so the
catch(...) in system headers is not really a showstopper kind of problem,
but still it does kind of open holes in your overall system reliability.

This is another reason why I tell people to NOT use catch(...) but they just
say it is only a platform-specific problem, "pure C++" does not have any
such problem. Also, Dave Butenhof has previously recommended on this issue
that cancellation exception should really be catch()able from C++ code
anyhow (well, you can do it via CATCH_ALL, so why not). He's probably right.
(On the other hand, based on some tech-support discussion I've had with
(now)HP C++ team over the issue, they seem to have made a distinct decision
to special-case and _not_ catch the pthread exceptions in the C++ runtime
condition-handler routine. It seems like it'd be fairly easy to change, but
I wonder if they had a good reason for it or if it just seemed like a good
idea at the time?) I guess that would eliminate this loophole, provide
flexibility for specialized cases which legitimately need to
finalize/swallow cancel exception, and open a new loophole for newbies to
accidentally swallow cancel exceptions in bad code. :-) But still I think
catch(...) will generally remain a bad idea...

hys

"Mike Mowbray" <NOmik...@ot.removethis.com.andthiscrap.au> wrote in
message news:3D7832C3...@ot.removethis.com.andthiscrap.au...

--

0 new messages