[c++-pthreads] concrete library-code example

Dave Butenhof David.Butenhof at hp.com
Mon Jan 5 16:57:32 UTC 2004


Nathan Myers wrote:

>On Wed, Dec 24, 2003 at 08:09:57AM -0500, Jason Merrill wrote:
>  
>
>>On Tue, 23 Dec 2003 11:03:14 -0500, Ted Baker <baker at cs.fsu.edu> wrote:
>>    
>>
>>>How do you propose to modify read() to throw an exception and
>>>still have backwards compatability with applications that expect
>>>read() to always return (more specifically, to return -1 if it
>>>fails)?
>>>      
>>>
>>read() already doesn't return if it's acting on a cancellation request.
>>Throwing an exception is just a different way of not returning.
>>    
>>
First, let me say, "ouch, my brain hurts". I've been on vacation for 2 
weeks, and this mailing list had just started when I left, and the 
traffic during that time is a bit overwhelming! I haven't finished yet, 
and I don't expect to today. Nevertheless, I'm going to offer some 
comments while I'm "inspired". I'll also comment (with some degree of 
surprise and perhaps even consternation) that so far I've seen nothing 
to suggest that an eventual more detailed response will differ in any 
great detail from the comments made by Alexander Terekhov. But then, as 
I said, I haven't finished catching up yet. ;-)

>Enlarging on this question...
>
>Here is a more-or-less concrete example, for discussion purposes.
>It's meant as a generic example of code written according to the 
>existing contract offered by C libraries.
>  
>
Correction: "... offered by C libraries that support POSIX 1003.1b-1993 
or earlier."

>  int affect_world(struct state* s)
>  {
>    int result;
>    violate_invariants_or_claim_resources(s);
>    result = c_function_or_system_call(s->member);
>    if (result < 0) {
>      clean_up(s, result);
>      return result;
>    }
>    act_on_result(s, result);
>    restore_invariants_and_release_resources(s);
>    return 0;
>  }
>
>This pattern is extremely common in both C and C++ libraries.  If 
>read() were to throw (or to "just ... not return"), the program state 
>would be corrupted.  A redefinition of c_function_or_system_call 
>semantics that breaks this code breaks many thousands of existing 
>thread-safe C and C++ libraries.
>  
>
If this code exists in a pure ANSI C/POSIX application using threads, 
and if the thread running this code can be cancelled, then the 
implementation of this function is broken because IT (not the 
implementation, nor the cancellation) corrupts program state.

While I'm not at all trying to argue that the issue is at all as simple 
as this, that's the facts all the same.

Depending on propagation of error statuses is a really bad way to 
implement cancellation. At least, given the primitive and limited 
concept of ANSI/POSIX error codes. Too much code ignores statuses in the 
first place, which is bad enough. But, worse, there are many legitimate 
reasons for library code to CONVERT return status values; e.g., I called 
read() and it returned some error but MY function only implicitly 
involves a read() and it simply wouldn't be useful or meaningful to 
return that error to my caller. Instead, I want to indicate that my 
function (say, synchronizing a database) failed, and so any (or at least 
most) failures of my "support calls" will result in my returning 'unable 
to synchronize database' (which often isn't an ANSI/POSIX error number 
in the first place, but even if it is, it's unlikely to be the value 
returned by read). The ECANCELLED some have proposed would be lost, and 
that's unacceptable. This is why we settled on exceptions to represent 
cancellation. And because POSIX and ANSI C don't have exceptions, we 
devised the simple "cleanup handler" mechanism that allowed a clean  and 
transparent implementation on top of exceptions, or a "hack" 
implementation private to the thread library where exceptions weren't 
available.

>(The cancellation model described in
>  http://www.codesourcery.com/archives/c++-pthreads/msg00021.html
>is designed to preserve libraries that contain code that follows 
>this pattern.)
>
>Jason, do you not consider those libraries worth preserving?
>  
>
If you're talking about a currently non-threaded library to which you'd 
like to transparently add thread support; well, I doubt that's possible, 
and this particular proposal isn't going to help. When they're 
redesigned and recoded to be thread-safe, they can also be made 
cancel-safe. If you're talking about adding cancel support transparently 
to an existing C++ library, I doubt this is sufficient unless there's 
some standard requirement that all C++ libraries must pass through the 
system failure code to the caller. (There isn't, can't be, and shouldn't 
be.) And it also presupposes that the C++ library isn't exception-safe; 
because if it is, then delivering cancellation as an exception would 
seem "obviously" to be the most compatible and complete solution.

And I'm deliberately discounting the mention I've seen several times in 
this list of "thread-safe" libraries that aren't "cancel-safe". Such 
libraries are simply broken, from basic design. Cancellation is a basic 
and important part of the POSIX thread model, and if you're not safe 
you're not safe. The only viable exclusion (there, I avoided using the 
word "exception", though it took me a few moments of thought) is if you 
can be guaranteed to be running only in threads that can never be 
cancelled... and in that case the whole issue is irrelevant!

-- 
/--------------------[ David.Butenhof at 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/Threads/Threads.html ]---/




More information about the c++-pthreads mailing list