Re: [c++-pthreads] Restating the Jason model
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [c++-pthreads] Restating the Jason model



Jason Merrill wrote:

I think this is an appropriate time to restate my proposal.  I think
there's a fair amount of consensus around these three points:

* Cancellation is a normal exception.
* If a cancellation exception is destroyed, the cancellation request
  is re-entered, and acted on again at the next cancellation point.
* Cancellation is disabled during unwinding.

But there are still some open questions:

* Which of the POSIX cancellation points are cancellation points in C++?

None of the mandatory cancellation points are mentioned in the C++
standard, so I don't see any reason to prevent them from throwing
cancellation in C++ code.

POSIX also says that the C standard I/O functions may be cancellation
points, while the C++ standard says that they don't throw.  This
contradiction can be resolved either by allowing them to throw cancellation
or declaring that they are not cancellation points when called from C++
code; if we choose the latter, an implementation could just change them to
never be cancellation points, since that is allowed by POSIX.

Dave Butenhof mentioned that on Tru64 printf is not a cancellation point,
to avoid having to deal with cleaning up internal state.  But what about
scanf, which can block?  One of the convenient things about pthread
cancellation is that it wakes up a blocked thread.  Does this not happen on
Tru64 if the thread is using stdio functions?

My preference is still to amend the C++ standard to allow stdio functions
to throw cancellation.
Since most platforms likely to support C++ cancellation already support POSIX cancellation, making the rules compatible would be "nice". (Perhaps even essential for wide acceptance.)

As such, the obvious path for C++ would be to start with, and extend as necessary, the Single UNIX Specification (SUS) rules (which are in turn extensions of the base POSIX rules to cover interfaces not defined by POSIX). If you did this, you're starting with two cancellation point lists: mandatory and optional. Implementations MUST raise cancel for all required cancellation points, and MAY (but need not) for the optional points. Applications, meanwhile, can depend portably on cancellation from the mandatory points; while they always must be prepared for (but cannot portably depend upon) cancellation from the optional points.

The general logic for the division between mandatory and obsolete is based on both "traditional UNIX implementation" (mandatory points are almost always direct kernel syscalls while the optional points are generally user library routines) and also on the efficiency/logic that mandatory points ALWAYS (or at least "nearly always") perform operations subject to blocking, while the optional points SOMETIMES perform such operations. (E.g., "read" always reads from a file unless there's an error... while scanf reads from a file only when any existing buffer is empty; you might make several scanf or printf calls purely from a local stdio buffer.) The twin logic forks are therefore that the syscalls (e.g., file access) are cancellation points; while "higher level" operations that use these syscalls are allowed to act as cancellation points only when they actually make a syscall. But that was a plausible excuse: the real reason for mandatory cancellation points was that we couldn't force everyone to analyze and substantially redesign their stdio packages (in particular) to deal with cancellation and cleanup, so we wanted to allow them to simply disable cancellation.

The idea was that this would be "transitional", but of course nothing ever works out that way. Tru64's libc, like many, provides a set of "nocancel" syscall stubs for internal use, and printf() (for example) calls write_nc() instead of write() so that it never needs to deal with cancellation. The issue for Tru64 is one of packaging, primarily; our exception library (libexc) has a long and twisted history going back to our original DCE OSF/1 implementation on MIPS R2000, using (along with Mach and BSD and OSF/1) a bunch of MIPSCO IP... including Mark Himmelstein's exceptions. It's big and a little unwieldy, and nobody was willing to either merge it into libc (which I always thought was the right solution) or to make libc depend on it. Thus there's simply no way for libc code to handle exceptions, and "we" simply had no way to implement the optional cancellation points.

The mandatory cancellation points, as I said, were deliberately those blocking entries that are "pure syscalls"; where user-mode cleanup isn't an issue.

* Which bits of the C++ library are cancellation points?

I would think pretty much all I/O code, and nothing else.
Not just "I/O", but in general any explicit control point that might "indefinitely block". Except for some low-level blocking operations, like mutex lock, where we felt that requiring all calls to prepare for cancellation would be impractical.

Closely related to this is the question of what happens if a cancellation
exception is thrown under a formatted I/O function; currently it would be
caught and discarded, so it would only escape on a flush or the like.  I
think it should escape from formatted I/O as well.  This could be
implemented by explicitly rethrowing cancel, by limiting the set of
exceptions trapped, or by calling pthread_testcancel after the try/catch
block.

If formatted I/O functions continue to trap cancellation exceptions, they
would not be cancellation points; a cancellation point in the C++ binding
would be a function which can throw a cancellation exception.
Any C++ runtime function is in much the same category as the user-mode C runtime functions. With the "exception" that the C++ runtime definitely CAN deal with exceptions. Still, though, presumably any C++ formatted I/O is buffered, and therefore may not on each operation make a true "I/O call". Life is easier for programmers if they can depend on behavior with no exceptions, arguing all cancellation points ought to be mandatory and unconditional -- which would mean testing for cancel explicitly when not otherwise making a cancellable call. On the other hand, life is easier for C++ runtime developers if they're not required to ensure that this happens in all possible code paths. And, like most C runtime libraries, C++ runtimes may be written without preparation for cancellation (or indeed exceptions in any form) in most of these code paths, since the developers knew where THEY might raise exceptions, and nobody else could do it to them.

Idealism suggests supporting the application developers' desire for consistency and predictability. Pragmatism argues against forcing all C++ runtimes to be substantially analyzed and modified. Pragmatism won in POSIX as it usually did, and many of the arguments I've seen in this group suggest (unscientifically) that C++ committee members might share some similar biases.

* Should cancellation also be disabled in destructors run during normal
  execution?  In catch blocks?

IMO, no and no.
Cancellation should NOT be disabled in destructors? Did you mean to say that?

* How can C++ code interact with a cancellation exception?

I think everyone agrees that it should be possible to catch a cancel by
name.  We still need to specify that name and any additional operations the
cancel object might support.
Additional operations on the cancel object. Interesting. Like, for example, if the cancel object destructor were to automatically re-pend the cancel unless the handler had already declared "cancel.finalize()"? (Could or should this be done automatically be the runtime for 'catch(cancel)' as opposed to 'catch(...)'?)

* What about pthread_exit?

I'm happy with the g++ status quo whereby destroying a pthread_exit
exception calls terminate.  Unlike cancellation, the position of a call to
pthread_exit is deterministic, so the user is responsible for making sure
that it can propagate.
Well, yes; although it also seems better to make one new rule for the new "thread terminating exceptions" rather than two separate new rules.

Anything else?
Yes; but I don't yet know what it is. ;-)

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