This is the mail archive of the libc-alpha@sourceware.cygnus.com mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

FWD: Dave Butenhof (author of Programming with POSIX Threads) on C++ and pthread_cancel()


I've got this book called "Programming with POSIX Threads" by Dave
Butenhof.  It's really good, so I thought I'd run the whole thread
cancellation issue past him.  I asked him if it would be OK to forward
his reply to the lists, and he said it was OK, so here it is...

--
George T. Talbot
<george@moberg.com>

P.S.  From what I can glean from replies on the list, the current
thread-safe exception model in GCC does not use setjmp()/longjmp() and
doesn't have runtime cost for declaring an exception scope.


-------- Original Message --------
Subject: Re: Question about your book...
Date: Wed, 18 Aug 1999 13:59:48 -0400
From: Dave Butenhof <David.Butenhof@compaq.com>
Organization: Compaq Computer Corporation
To: "George T. Talbot" <george@moberg.com>
References: <XFMail.990603144303.george@moberg.com>
<3758014E.51BFC7C3@compaq.com> <37BADDE2.205C9974@moberg.com>

"George T. Talbot" wrote:

> Dear Mr. Butenhof,
>
> I've contacted you before on the issue of thread cancellation and C++,
> and I have another question, if you don't mind.
>
> I've been revisiting thread cancellation and C++ on Linux.  What is the
> correct behaviour for C++ when a thread is cancelled?  I.E.  In an ideal
> world what effect on C++ code should pthread_cancel() in deferred
> cancellation mode have?
>
> 1)  Run all the destructors for objects on the stack and all code in the
> catch (...) handlers?
> 2)  Run all the destructors for objects on the stack?
> 3)  Nothing?
> 4)  Segfault?  ;^)

Yes, in an ideal world, all actions would segfault. That would make
coding and
debugging easy. (Write anything. It segfaults. Good; it was supposed to.
Done with
that. On to the next project. ;-) )

MY ideal behavior, and the one towards which I'm working on Tru64 UNIX,
is, more or
less, your "1". Actually, "1" is what happens with the current OS and
C++ release.
We're working on moving beyond that to make cancellation (and other
system/thread
exceptions) known C++ exception classes so that you could write
something like
"catch(POSIX_cancel) {}" instead of anonymously catching all exceptions.

I know others, though, who believe that cancellation shouldn't be
catch-able
(anonymously or otherwise), and should only run destructors. That's your
"2". I don't
know of anyone who believes that cancellation shouldn't be required to
run
destructors. (And if there are any, they're wrong.) Although I don't
agree with the
idea of "hiding" cancellation exceptions, at least "2" would allow
writing cancel-safe
code.

So I'd have to say that 1 is "near-ideal" (but not as good as making
cancel a real C++
exception), 2 is "acceptable". The others are not.

> Right now, pthreads under Linux does 3.  I've been fooling around with
> the implementation, and if I recompile the C library with exception
> support (-fexceptions), and have pthread_cancel() do a throw as its last
> action after running the cancellation handlers, I can get it to do 1).

Interleaved C++ destructors and POSIX cleanup handlers should be run in
proper nested
order, from inner to outer, in one pass. (They're not "cancellation
handlers", by the
way, because they're also run by thread exit, and should be run any time
an exception
propagates through the code scope.) I don't believe it's acceptable to
run all cleanup
handlers first, then all destructors... or vice versa. Ideally, both are
implemented
as exception handlers, and the exception propagates normally as one
would expect an
exception to propagate.

One option, when you're using just C++, is to have
pthread_cleanup_push() to expand to
a block containing a local object with a destructor. The
pthread_cleanup_pop() macro
would expand to terminate the block. Exiting the scope would then run
the object
destructor. On normal exit, it would check the pop argument to determine
whether to
call the cleanup handler; otherwise it'd always call the handler. But
that won't help
if you have interleaved C and C++ code, unless both languages support a
common
exception model. That's what we do on Tru64 UNIX, because the underlying
common
calling standard supports exceptions, and the C compiler provides
extensions to
declare essentially try/catch scopes.

> However the maintainer of the GNU C library won't enable exception
> support in the main-line version, as he says it slows down the library
> by about 5%.  Also, it's been really obvious that the maintainer and
> others working on the C library and gcc don't like C++, so I'm kind-of
> on my own with this.

I don't know what this -fexceptions option does on Linux, but it sounds
like it's NOT
real exceptions. Maybe something based on setjmp/longjmp. Real
exceptions don't have
performance overhead (except indirectly via a small increase in object
size) until an
exception is raised. Exception scopes should be PC mapped, so there's no
runtime cost
to declare a scope; when an exception is raised, the common exception
library can walk
the stack and check for exception code ranges to do the proper unwind
actions.

Too bad. Any system without well-defined "native" exception support is
going to have a
hard time doing C++ (or Ada, or Modula-2+) at all well. Exceptions need
to be free (or
very nearly free) until you actually raise one.

> I'm hoping that I can figure out a way to unwind the stack and call
> destructors, which I might be able to get into a patch, but I'm not
> clear on the correct behaviour.

You need to be able to unwind the thread's stack one frame at a time,
and detect a C++
catch, destructor, or cleanup handler (or any combination) in each
frame. Then
activate that handler, then unwind to the next frame, and so forth. If
you can do a
stack walk, you're halfway there; you just need to be able to identify
both the
C++ and cleanup information. Without real exceptions, most likely the C
runtime has
some list of setjmp blocks, and the thread library has its own list of
setjmp blocks.
You'd need to be able to find both lists, and traverse them both to find
all the
blocks for each frame as you walk the stack.

> Thanks for your time.  Sorry if I'm adding to an overly full mailbox.

That's OK. Good luck!

/---------------------------[ Dave Butenhof ]--------------------------\
| Compaq Computer Corporation                David.Butenhof@compaq.com |
| 110 Spit Brook Rd ZKO2-3/Q18       http://members.aol.com/drbutenhof |
| Nashua NH 03062-2698  http://www.awl.com/cseng/titles/0-201-63392-2/ |
\-----------------[ Better Living Through Concurrency ]----------------/

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]