From gdr at integrable-solutions.net Thu Jan 1 00:48:19 2004 From: gdr at integrable-solutions.net (Gabriel Dos Reis) Date: 01 Jan 2004 01:48:19 +0100 Subject: [c++-pthreads] Re: C++ and posix threads In-Reply-To: <20031231220611.GI30780@tofu.dreamhost.com> References: <1072131497.7562.22.camel@doubledemon.codesourcery.com> <20031223160314.GA9780@diablo.name> <20031224044622.GG27836@tofu.dreamhost.com> <20031225001117.GA13447@redhat.com> <20031227232444.GA14692@redhat.com> <20031228045406.GI2774@tofu.dreamhost.com> <20031229232817.GA21305@redhat.com> <20031230015808.GM2774@tofu.dreamhost.com> <20031230050755.GB21387@redhat.com> <20031231220611.GI30780@tofu.dreamhost.com> Message-ID: Nathan Myers writes: | Richard, please suspend judgment and read carefully. | | On Mon, Dec 29, 2003 at 09:07:55PM -0800, Richard Henderson wrote: | > On Mon, Dec 29, 2003 at 05:58:08PM -0800, Nathan Myers wrote: | > > | > > Richard, all these "ZERO uptake" remarks make it very hard to discuss | > > design decisions calmly. [...] | > | > If you think I mean just Uli, then you're wrong. I mean any vendor. | | Have you actually asked all all these vendors? I suspect you'd find | that most would have preferred the option to offer _only_ error-return | semantics, and not be obliged to implement unwinding at all. On one hand, what it is important here is what is of interest to users. The idea of checking return value for error codes might work in small, but it does not work properly in larger systems/libraries. I do not believe we should be conducting this discussion by focusing on specific individual and I can't see anything from RTH's previous message leading to believe that he was meaning Uli. On the other hand, the idea of having printf (or assimilated) work differently depending on whether it is part of C or C++ program is a good recipe for confusion and disaster. We have something to learn from past experience, messing with C functions. -- Gaby From TEREKHOV at de.ibm.com Fri Jan 2 12:30:12 2004 From: TEREKHOV at de.ibm.com (Alexander Terekhov) Date: Fri, 2 Jan 2004 13:30:12 +0100 Subject: [c++-pthreads] Re: C++ and posix threads In-Reply-To: <20031231220611.GI30780@tofu.dreamhost.com> Message-ID: Nathan Myers wrote: [...] > Nothing in the goal of supporting thread cancellation seems to > require violating ISO-standard language semantics. Exception based thread cancellation comes from the DCE (CMA aside for a moment) which *by far* predates ISO C++. http://archive.adaic.com/tools/bindings/DCE/THREADS/pthread_exc.h regards, alexander. From alexvn at connect.to Fri Jan 2 12:14:08 2004 From: alexvn at connect.to (Alex Vinokur) Date: Fri, 2 Jan 2004 14:14:08 +0200 Subject: pthreads, printf and cout Message-ID: Hi, Behavior of printf and cout in a program below is different : cout prints nothing. What is wrong? =================================== Windows 2000 Professional CYGWIN_NT-5.0 1.5.4(0.94/3/2) gcc version 3.3.1 (cygming special) =================================== ====== C++ code : foo.cpp : BEGIN ====== #include #include #include #include using namespace std; extern "C" void* Run(void *) { printf ("printf : Run Start\n"); cout << "cout : Run Start" << endl; sleep (3); printf ("printf : Run Finish\n"); cout << "cout : Run Finish" << endl; } int main () { pthread_t t1; pthread_create(&t1, NULL, Run, NULL); int errNum = pthread_join(t1, NULL); printf ("printf : errNum = %d\n", errNum); cout << "cout : errNum = " << errNum << endl; } ====== C++ code : foo.cpp : END ======== ====== Compilation & Run : BEGIN ====== $ g++ foo.cpp $ a printf : Run Start printf : Run Finish printf : errNum = 0 // cout prints nothing ====== Compilation & Run : END ======== -- ===================================== Alex Vinokur mailto:alexvn at connect.to http://mathforum.org/library/view/10978.html news://news.gmane.org/gmane.comp.lang.c++.perfometer ===================================== From David.Butenhof at hp.com Mon Jan 5 16:57:32 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Mon, 05 Jan 2004 11:57:32 -0500 Subject: [c++-pthreads] concrete library-code example In-Reply-To: <20031224170517.GG30780@tofu.dreamhost.com> References: <3FE70542.5060104@bourguet.org> <1072111706.3474.17.camel@minax.codesourcery.com> <20031222215712.GB1528@diablo.name> <1072131497.7562.22.camel@doubledemon.codesourcery.com> <20031223160314.GA9780@diablo.name> <20031224170517.GG30780@tofu.dreamhost.com> Message-ID: <3FF9977C.7040003@hp.com> 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 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 ]---/ From David.Butenhof at hp.com Mon Jan 5 17:39:09 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Mon, 05 Jan 2004 12:39:09 -0500 Subject: [c++-pthreads] Re: cancellation points report failure In-Reply-To: <3FF18485.93F2D588@terekhov.de> References: <3FF18485.93F2D588@terekhov.de> Message-ID: <3FF9A13D.9030105@hp.com> Alexander Terekhov wrote: >David Abrahams wrote: >[...] > > >>Of course it's not safe. That's my point, sort of: if you use >>synchronous cancellation, you have to give up on any *guarantee* that >>the thread will be cancelled, so we shouldn't be considering measures >>that take heroic steps to try to ensure it. If you want to guarantee >>that cancellation happens, you have to do something unsafe. >> >> >Note that the use of POSIX asynchronous cancellation (I mean the >presence of async-cancel{-safe} regions on the execution path) does >NOT guarantee thread termination (cancel request delivery) at all. >Conforming implementations are free to ignore it completely, so to >speak. I wish the standard would define pthread_testcancel() "in >terms" of an empty async-cancel region (and it would also provide >async-cancel-safety for pthread_testcancel() itself): > >void pthread_testcancel() { /* mandatory shall occur semantics */ > int oldtype; > pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); > pthread_setcanceltype(oldtype, &oldtype); >} > > This is all way off topic, but what's your point here, Alexander? Are you intending to suggest that pthread_setcanceltype() be made a guaranteed cancellation point, so your substitution would have the same effect as the current pthread_testcancel()... or that the definition of pthread_testcancel() (using the current semantics of pthread_setcanceltype()) should be allowed to ignore a pending cancel? ;-) -- /--------------------[ 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 ]---/ From boo at terekhov.de Mon Jan 5 18:53:33 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Mon, 05 Jan 2004 19:53:33 +0100 Subject: [c++-pthreads] Re: cancellation points report failure References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> Message-ID: <3FF9B2AD.D7E6C4DD@terekhov.de> Dave Butenhof wrote: [...] > >Note that the use of POSIX asynchronous cancellation (I mean the > >presence of async-cancel{-safe} regions on the execution path) does > >NOT guarantee thread termination (cancel request delivery) at all. > >Conforming implementations are free to ignore it completely, so to > >speak. I wish the standard would define pthread_testcancel() "in > >terms" of an empty async-cancel region (and it would also provide > >async-cancel-safety for pthread_testcancel() itself): > > > >void pthread_testcancel() { /* mandatory shall occur semantics */ > > int oldtype; > > pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); > > pthread_setcanceltype(oldtype, &oldtype); > >} > > > > > This is all way off topic, but what's your point here, Alexander? > > Are you intending to suggest that pthread_setcanceltype() be made a > guaranteed cancellation point, ... I'm (once again) intending to suggest that "an act" of transition from synchronous to asynchronous cancellation be made a guaranteed cancellation point so that "passing through" any async-cancel-safe region with cancel request pending on its entry (or by the time of pthread_testcancel() invocation inside async-cancel-safe region [I also want pthread_testcancel() be added to the list of async.cancel safe functions]) shall result in raising std::thread_cancel_request exception (subject to PTHREAD_CANCEL_ENABLE cancellation state, of course). Well, to you, this is/was just a matter of control (apart from your async-cancel "phobia"***, so to say): google.com/groups?threadm=ki1M8.3%24VD1.187532%40news.cpqcorp.net (Subject: Re: cancelling one thread from inside another one) I disagree. regards, alexander. ***) "Personally, if I were to spend any serious time considering the future of async cancelability in the standard, I'd rather argue for removing it entirely." (quote taken from the other message of yours in the same thread referenced above) From boo at terekhov.de Mon Jan 5 19:11:23 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Mon, 05 Jan 2004 20:11:23 +0100 Subject: [c++-pthreads] concrete library-code example References: <3FE70542.5060104@bourguet.org> <1072111706.3474.17.camel@minax.codesourcery.com> <20031222215712.GB1528@diablo.name> <1072131497.7562.22.camel@doubledemon.codesourcery.com> <20031223160314.GA9780@diablo.name> <20031224170517.GG30780@tofu.dreamhost.com> <3FF9977C.7040003@hp.com> Message-ID: <3FF9B6DB.1C0EA581@terekhov.de> Dave Butenhof wrote: [...] > 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 asynchronous cancelability was another reason, I guess. Oder? ;-) regards, alexander. From David.Butenhof at hp.com Tue Jan 6 11:58:57 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Tue, 06 Jan 2004 06:58:57 -0500 Subject: [c++-pthreads] Re: cancellation points report failure In-Reply-To: <3FF9B2AD.D7E6C4DD@terekhov.de> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> Message-ID: <3FFAA301.6080301@hp.com> Alexander Terekhov wrote: >Dave Butenhof wrote: >[...] > > >>Are you intending to suggest that pthread_setcanceltype() be made a >>guaranteed cancellation point, ... >> >> >I'm (once again) intending to suggest that "an act" of transition >from synchronous to asynchronous cancellation be made a guaranteed >cancellation point so that "passing through" any async-cancel-safe >region with cancel request pending on its entry (or by the time of >pthread_testcancel() invocation inside async-cancel-safe region [I >also want pthread_testcancel() be added to the list of async.cancel >safe functions]) shall result in raising std::thread_cancel_request >exception (subject to PTHREAD_CANCEL_ENABLE cancellation state, of >course). > Very simply, Alexander, your proposal COMBINES two separate primitives: disabling async cancel, and testing for a pending cancel. Keeping those primitives separate allows applications to choose the behavior appropriate to their needs. Combining them removes that flexibility. We could have created two variants of "disable async cancel", one that tests for pending cancel and one that doesn't, but there'd be no real advantage over disabling and then testing separately. >Well, to you, this is/was just a matter of control (apart >from your async-cancel "phobia"***, so to say): > >google.com/groups?threadm=ki1M8.3%24VD1.187532%40news.cpqcorp.net >(Subject: Re: cancelling one thread from inside another one) > > Since a "phobia" is an "abnormal or illogical fear", I'd have to say you're wrong on two counts. First, I don't fear it; I merely argue that it's rarely usable and far too easy to misuse, and that the cost of the feature outweighs its value. And none of that is "illogical". Furthermore, since some of the C++ people here seem to have enough trouble with the idea of cancel as an exception in the first place, (one of those rare goals in which you and I are united), nevermind introducing the concept of an ASYNCHRONOUS exception, let's just leave this alone, eh? Personally, I would be happy to accept a C++ binding with no way to enable asynchronous cancelability, and to avoid defining any C++ code as "async cancel-safe". -- /--------------------[ 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 ]---/ From dave at boost-consulting.com Tue Jan 6 13:51:19 2004 From: dave at boost-consulting.com (David Abrahams) Date: Tue, 06 Jan 2004 08:51:19 -0500 Subject: cancellation points report failure References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> Message-ID: Dave Butenhof writes: > Since a "phobia" is an "abnormal or illogical fear", I'd have to say > you're wrong on two counts. First, I don't fear it; I merely argue > that it's rarely usable and far too easy to misuse, and that the cost > of the feature outweighs its value. And none of that is > "illogical". Furthermore, since some of the C++ people here seem to > have enough trouble with the idea of cancel as an exception in the > first place, (one of those rare goals in which you and I are united), > nevermind introducing the concept of an ASYNCHRONOUS exception, let's > just leave this alone, eh? I, at least, have no trouble with the idea of using C++ exceptions to report cancellation. In fact, I think it's a great idea in general. It's just a really bad idea for functions which can normally be expected never to throw exceptions to start throwing cancellations when used in a POSIX threads environment. IIUC, other "C++ people" who don't like the idea of printf throwing an exception feel the same way. -- Dave Abrahams Boost Consulting www.boost-consulting.com From dave at boost-consulting.com Tue Jan 6 14:03:09 2004 From: dave at boost-consulting.com (David Abrahams) Date: Tue, 06 Jan 2004 09:03:09 -0500 Subject: concrete library-code example References: <3FE70542.5060104@bourguet.org> <1072111706.3474.17.camel@minax.codesourcery.com> <20031222215712.GB1528@diablo.name> <1072131497.7562.22.camel@doubledemon.codesourcery.com> <20031223160314.GA9780@diablo.name> <20031224170517.GG30780@tofu.dreamhost.com> <3FF9977C.7040003@hp.com> Message-ID: Dave Butenhof writes: >>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." I think Nathan meant what he said. You seem to be viewing this in terms of some POSIX standard, but I'm pretty sure even most good C programmers write code without giving POSIX a second thought... even thread-safe code. >> 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. Again, you're missing our point. We'd like to keep working (non-POSIX) thread-safe code working when used in a POSIX environment, without getting into a detailed review of every line of code. Adding exception-safety to an existing codebase is a nontrivial exercise, and that's essentially what the current binding requires of people who want to use code not written with POSIX in mind. -- Dave Abrahams Boost Consulting www.boost-consulting.com From dave at boost-consulting.com Tue Jan 6 14:04:54 2004 From: dave at boost-consulting.com (David Abrahams) Date: Tue, 06 Jan 2004 09:04:54 -0500 Subject: concrete library-code example References: <3FE70542.5060104@bourguet.org> <1072111706.3474.17.camel@minax.codesourcery.com> <20031222215712.GB1528@diablo.name> <1072131497.7562.22.camel@doubledemon.codesourcery.com> <20031223160314.GA9780@diablo.name> <20031224170517.GG30780@tofu.dreamhost.com> <3FF9977C.7040003@hp.com> Message-ID: Dave Butenhof writes: > 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. Do any other threading models count? -- Dave Abrahams Boost Consulting www.boost-consulting.com From David.Butenhof at hp.com Tue Jan 6 14:24:22 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Tue, 06 Jan 2004 09:24:22 -0500 Subject: [c++-pthreads] Re: cancellation points report failure In-Reply-To: References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> Message-ID: <3FFAC516.2010803@hp.com> David Abrahams wrote: >Dave Butenhof writes: > > >>Since a "phobia" is an "abnormal or illogical fear", I'd have to say >>you're wrong on two counts. First, I don't fear it; I merely argue >>that it's rarely usable and far too easy to misuse, and that the cost >>of the feature outweighs its value. And none of that is >>"illogical". Furthermore, since some of the C++ people here seem to >>have enough trouble with the idea of cancel as an exception in the >>first place, (one of those rare goals in which you and I are united), >>nevermind introducing the concept of an ASYNCHRONOUS exception, let's >>just leave this alone, eh? >> >> >I, at least, have no trouble with the idea of using C++ exceptions to >report cancellation. In fact, I think it's a great idea in general. >It's just a really bad idea for functions which can normally be >expected never to throw exceptions to start throwing cancellations >when used in a POSIX threads environment. > >IIUC, other "C++ people" who don't like the idea of printf throwing >an exception feel the same way. > > Although I wasn't counting you in particular as an objector, I may have overgeneralized the objections of others. The whole matter of interaction with throw specs in particular is complicated, and I certainly don't claim to have any answers -- easy or otherwise. I've considered making a couple of rambling conceptual statements, but I'm way out of my league here on C++ philosophy and history, and I wouldn't want anyone to think I'm doing more than idle speculation. One of those idle thoughts was that the concept of "cancel state" (enable or disable) might even be meaningless (or at least irrelevant) for a pure C++ binding. This might simply be a dynamic property of the current call tree; depending on the nested throw specs. That is, if the cancel exception can be thrown, cancel is "enabled"; and if it cannot then cancel is "disabled". This might well make it too hard to ever allow cancellation, since "most" throw specs would probably need to include cancellation. (That it might also make everything without a throw spec cancellable I discount as irrelevant: that's only an issue if the code runs in a thread that can be cancelled, in which case the code is so far out of any supported boundaries now that I don't care. This of course doesn't disallow or invalidate the possibility that YOU care. ;-) ) That also doesn't help in dealing with "printf throwing an exception" in the most generic sense, if that's taken as a placeholder for "library routines that don't currently raise exceptions but ought to be cancellation points, including read and write. In particular, though, POSIX designates printf as an OPTIONAL cancellation point. Most current implementations do NOT support cancellation of printf because it'd mean putting handlers into the C runtime to clean up stdio state. (On Tru64 UNIX, for example, handling exceptions requires linking libexc, on which libc has never depended, so adding cancellation support to libc would be "a big deal".) If printf isn't a cancellation point, then it shouldn't normally be expected to raise the cancel exception. ;-) -- /--------------------[ 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 ]---/ From David.Butenhof at hp.com Tue Jan 6 14:31:45 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Tue, 06 Jan 2004 09:31:45 -0500 Subject: [c++-pthreads] Re: concrete library-code example In-Reply-To: References: <3FE70542.5060104@bourguet.org> <1072111706.3474.17.camel@minax.codesourcery.com> <20031222215712.GB1528@diablo.name> <1072131497.7562.22.camel@doubledemon.codesourcery.com> <20031223160314.GA9780@diablo.name> <20031224170517.GG30780@tofu.dreamhost.com> <3FF9977C.7040003@hp.com> Message-ID: <3FFAC6D1.2090005@hp.com> David Abrahams wrote: >Dave Butenhof writes: > > >>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. >> >> >Do any other threading models count? > > On a discussion list titled explicitly "c++-pthreads", I automatically presumed not, without really giving it much thought. But, as I said, I was completely overwhelmed with the flood of messages when I returned from vacation yesterday, and I SHOULD have recalled previous discussions on this subject that pointed out you really didn't intend to target "pthreads" specifically. (I.e., this mailing list is inappropriately named.) You're basically talking about Win32 or UI threads (which essentially is legacy Solaris code), being converted trivially to "native C++ threading" environment. Is that a realistic limitation on your model? If you say so, hey, I'm not in a position to argue about your goals. -- /--------------------[ 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 ]---/ From dave at boost-consulting.com Tue Jan 6 18:37:24 2004 From: dave at boost-consulting.com (David Abrahams) Date: Tue, 06 Jan 2004 13:37:24 -0500 Subject: concrete library-code example References: <3FE70542.5060104@bourguet.org> <1072111706.3474.17.camel@minax.codesourcery.com> <20031222215712.GB1528@diablo.name> <1072131497.7562.22.camel@doubledemon.codesourcery.com> <20031223160314.GA9780@diablo.name> <20031224170517.GG30780@tofu.dreamhost.com> <3FF9977C.7040003@hp.com> <3FFAC6D1.2090005@hp.com> Message-ID: Dave Butenhof writes: >>Do any other threading models count? >> >> > On a discussion list titled explicitly "c++-pthreads", I automatically > presumed not, without really giving it much thought. > > But, as I said, I was completely overwhelmed with the flood of > messages when I returned from vacation yesterday, and I SHOULD have > recalled previous discussions on this subject that pointed out you > really didn't intend to target "pthreads" specifically. (I.e., this > mailing list is inappropriately named.) > > You're basically talking about Win32 or UI threads (which essentially > is legacy Solaris code) Aren't there also various "lightweight thread" models used in embedded systems? I think non-threaded library code which doesn't use globals alos counts. > being converted trivially to "native C++ threading" environment. Well, not neccessarily trivially, but the idea is that it should be tractable to do it. > Is that a realistic limitation on your model? If you say so, hey, > I'm not in a position to argue about your goals. I'm not really in a position to say how important or realistic the scenario is. It does seem reasonable to want to pick up a C/C++ library that's written with the general priniciples of thread-safety in mind but without specific knowledge of POSIX, and use it in some other code that's launched in a cancellable POSIX thread. -- Dave Abrahams Boost Consulting www.boost-consulting.com From dave at boost-consulting.com Tue Jan 6 18:41:25 2004 From: dave at boost-consulting.com (David Abrahams) Date: Tue, 06 Jan 2004 13:41:25 -0500 Subject: cancellation points report failure References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> Message-ID: Dave Butenhof writes: > David Abrahams wrote: > >>Dave Butenhof writes: >> >> >>>Since a "phobia" is an "abnormal or illogical fear", I'd have to say >>>you're wrong on two counts. First, I don't fear it; I merely argue >>>that it's rarely usable and far too easy to misuse, and that the cost >>>of the feature outweighs its value. And none of that is >>>"illogical". Furthermore, since some of the C++ people here seem to >>>have enough trouble with the idea of cancel as an exception in the >>>first place, (one of those rare goals in which you and I are united), >>>nevermind introducing the concept of an ASYNCHRONOUS exception, let's >>>just leave this alone, eh? >>> >>> >>I, at least, have no trouble with the idea of using C++ exceptions to >>report cancellation. In fact, I think it's a great idea in general. >>It's just a really bad idea for functions which can normally be >>expected never to throw exceptions to start throwing cancellations >>when used in a POSIX threads environment. >> >>IIUC, other "C++ people" who don't like the idea of printf throwing >>an exception feel the same way. >> >> > Although I wasn't counting you in particular as an objector, I may > have overgeneralized the objections of others. The whole matter of > interaction with throw specs in particular is complicated, and I > certainly don't claim to have any answers -- easy or otherwise. I've > considered making a couple of rambling conceptual statements, but I'm > way out of my league here on C++ philosophy and history, and I > wouldn't want anyone to think I'm doing more than idle speculation. > > One of those idle thoughts was that the concept of "cancel state" > (enable or disable) might even be meaningless (or at least > irrelevant) for a pure C++ binding. This might simply be a dynamic > property of the current call tree; depending on the nested throw > specs. That is, if the cancel exception can be thrown cancel is > "enabled"; and if it cannot then cancel is "disabled". What do you mean by "can be", and what does it have to do with exception-specifications? Are you suggesting that cancellation is disabled by writing an exception-specification that doesn't include it? Interesting thought, if so, but how do you disable just cancellation without masking other exceptions? -- Dave Abrahams Boost Consulting www.boost-consulting.com From jason at redhat.com Tue Jan 6 18:59:20 2004 From: jason at redhat.com (Jason Merrill) Date: Tue, 06 Jan 2004 13:59:20 -0500 Subject: [c++-pthreads] Re: cancellation points report failure In-Reply-To: (David Abrahams's message of "Wed, 24 Dec 2003 12:38:46 -0500") References: <2A8DB02E3018D411901B009027FD3A3F01CAAF6A@mchp905a.mch.sbs.de> <2E98D8A4-3251-11D8-B7E5-00039390D9E0@apple.com> <20031219194503.GA31795@tofu.dreamhost.com> <20031219205746.GA12322@diablo.name> <20031219225650.GA13735@diablo.name> <20031220045618.GA27836@tofu.dreamhost.com> <1071946483.25325.69.camel@doubledemon.codesourcery.com> Message-ID: On Wed, 24 Dec 2003 12:38:46 -0500, David Abrahams wrote: > Jason Merrill writes: > >> * C++ I/O functions can throw a cancellation exception. >> * C++ catch blocks work normally. >> * If a cancellation exception is destroyed, the cancellation request >> is re-entered, and acted on again at the next cancellation point. > > I appreciate that your motivation is to ensure that synchronous > cancellation requests aren't ignored, but that doesn't really seem to > accomplish the goal. After all, a thread never has to reach a > cancellation point, and even if it does, it can keep catching and > discarding the exceptions indefinitely. That's fine with me. I'm not trying to assure that cancellation always succeeds; as you say, a thread never has to reach a cancellation point. My goal is to make sure that the cancellation request is never lost, that it remains either active or pending until the thread exits. Jason From austern at apple.com Tue Jan 6 19:33:47 2004 From: austern at apple.com (Matt Austern) Date: Tue, 6 Jan 2004 11:33:47 -0800 Subject: What are the real issues? In-Reply-To: <3FFAC516.2010803@hp.com> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> Message-ID: <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> I'm just trying to trace back why people see C++ thread cancellation as a problem. First: the usual assumption has been that thread cancellation is most naturally represented in C++ as an exception. That's because the POSIX model for thread cancellation is that if you cancel a particular thread, then, the next time the thread reaches one of a set of well-defined cancellation points, it'll perform some cleanup and then stop. In C the cleanup is via handlers that you register. We could have that model in C++ as well, but most C++ programmers would think it's very strange that you would stop execution at some point without cleaning up the stack. And once you're talking about stack cleanup, i.e. invoking destructors, you're talking about something that's close enough to an exception that it makes no difference. The Itanium C++ ABI, which gcc adopted, made cancellation a special kind of exception, "forced unwinding", so that a thread can't just catch the cancellation exception and swallow it. The idea was that a thread may disable cancellation tempoarily, but that once a cancellation has begun it can't be thrown away. This was a slightly controversial decision at the time, and maybe it's at the heart of the real issue. Second: the immediate issue was that some people saw a contradiction between this model and the C++ library specification. For example, POSIX says that read() is a cancellation point. But it's reasonable to assume that std::istream:;read() invokes POSIX's read() system call, and the C++ standard says that (under ordinary circumstances) std::istream doesn't throw. If the cancellation exception were an ordinary exception instead of special forced unwinding, what would happen would be that istream would invoke its streambuf, the streambuf would call read(), read would throw a cancellation exception, then istream would catch the exception, swallow it, and set and error flag. Forced unwinding means that the cancellation exception will propagate even though istream tries to swallow it. This means that an exception will propagate from istream when the C++ standard says it's not supposed to. Third, some people simply object to having cancellation be an exception (or anything that looks like an exception): it means there are some functions that can throw exceptions in the presence of threads that wouldn't throw in a single-threaded program. It's actually pretty hard for me to imagine any model for thread cancellation that's very different from the POSIX. Anything that doesn't involve thread execution stopping sounds more like a communication mechanism than a cancellation mechanism. Seems to me that the only real issues for debate are which functions are cancellation points, how a thread can enable and disable cancellation, whether a thread should be allowed to disregard a cancellation request once it has been received, and what kind of cleanup a thread performs before it stops. My feeling: it's just plain inevitable that a multithreaded program has more functions that might throw than a single-thread program. Dealing with this is part of what it means to make a program thread-safe. We might argue that POSIX defines too many cancellation points, and that cutting it down to a smaller number (and, especially, getting rid of the functions that POSIX says may or may not be cancellation points) would make it easier to write correct code. But as I said, I think the really fundamental issue is whether a thread should be allowed to receive a cancellation request, start to do some work as a result of the request, and then decide that it doesn't want to be cancelled. If we think that's reasonable then I think what we should probably do is abandon the notion of forced unwinding and make cancellation into an ordinary exception. If we do that then the std::istream contradiction goes away. The cost, of course, is that it might become surprisingly hard to cancel some threads. --Matt From ncm at cantrip.org Tue Jan 6 21:12:36 2004 From: ncm at cantrip.org (Nathan Myers) Date: Tue, 6 Jan 2004 13:12:36 -0800 Subject: [c++-pthreads] What are the real issues? In-Reply-To: <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> Message-ID: <20040106211236.GX30780@tofu.dreamhost.com> I object to Matt's characterization of the issues on several levels. It misstates expressed concerns, omits others, and assumes its own conclusions. I hope to post in more detail, but I would like to address the most glaring problems immediately. On Tue, Jan 06, 2004 at 11:33:47AM -0800, Matt Austern wrote: > It's actually pretty hard for me to imagine any model for thread > cancellation that's very different from the POSIX. ... I presume you mean "different from the POSIX C binding". One such was already detailed here, so you don't need to imagine it, you can just go back and read it. (Since it's a proper subset of the POSIX C binding, it remains compatible, and could even be back-ported, as an optional mode, to the C binding.) > Seems to me that the only real issues for debate are: > which functions are cancellation points, Any function may be a cancellation point. If cancellation does not cause it to violate its long-documented interface, nothing is broken. If it does, then it doesn't matter much which set you choose, you've broken most libraries anyway. If you're starting with zero existing code, all that new code can be written to any interface you make up. > how a thread can enable and disable cancellation, POSIX already defines functions to block and unblock cancellation. (They might be called automatically on entry and certain exits from catch blocks, though. That's an interesting discussion.) > whether a thread should be allowed to disregard > a cancellation request once it has been received, and Nobody has proposed allowing a thread to "disregard" cancellation. That's a strawman. > what kind of cleanup a thread performs before it stops. This is just where we started. Really at issue are (1) what cancellation is to mean in a C++ context, (2) how to arrange that cancellation does not corrupt the process state to the point where it would be pointless not to terminate anyway, and (3) how to preserve the frankly enormous body of thread-safe code already written, deployed, and running for years. Most presentations here that argue that the POSIX C++ binding cannot deviate from the POSIX C binding ignore, or carefully avoid, point (2), and sidestop point (3) with disingenuous historical revisionism. They also, as in Matt's, start out assuming much that is as issue. > My feeling: it's just plain inevitable that a multithreaded program has > more functions that might throw than a single-thread program. Dealing > with this is part of what it means to make a program thread-safe. This is far from clear, and makes a very poor starting assumption. We have had thread-safe libraries for a long time. Under one proposed model most can simply be declared already "cancellation-safe", without changes. The POSIX C binding people have already abandoned their constituents' libraries, but we need not, and indeed we could rescue many of those that they have abandoned. > ... I think the really fundamental issue is whether a thread > should be allowed to receive a cancellation request, start to do some > work as a result of the request, and then decide that it doesn't want > to be cancelled. Nobody has proposed any such thing. Matt, have you confused the cancellation-exception object, which would be destroyed by running off the end of a catch block, with the thread's cancellation state? Nathan Myers ncm at cantrip.org From austern at apple.com Tue Jan 6 21:29:22 2004 From: austern at apple.com (Matt Austern) Date: Tue, 6 Jan 2004 13:29:22 -0800 Subject: [c++-pthreads] What are the real issues? In-Reply-To: <20040106211236.GX30780@tofu.dreamhost.com> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <20040106211236.GX30780@tofu.dreamhost.com> Message-ID: <64F121A7-408F-11D8-B918-00039390D9E0@apple.com> On Jan 6, 2004, at 1:12 PM, Nathan Myers wrote: >> ... I think the really fundamental issue is whether a thread >> should be allowed to receive a cancellation request, start to do some >> work as a result of the request, and then decide that it doesn't want >> to be cancelled. > > Nobody has proposed any such thing. Really? I think that you and I both did. (1) My proposal (which may or may not be a good idea, but which I think should be considered) is that cancellation could be represented by an ordinary exception instead of by a special kind of exception with forced unwinding semantics. This would allow a thread to catch the exception, set a flag, swallow the exception, and continue executing. that is, it would allow a thread to receive and act on a cancellation request but for it not to be canceled. (2) Your proposal is that the POSIX functions that are currently described as cancellation points shouldn't raise any kind of exception and shouldn't cause the thread to stop executing, but should instead return an error code. Again, this would allow a thread to note the error return, check errno to see if it's ETHREADCANCELED, set a flag as a result of having received the cancelation request, and continue executing. Again, your proposal would allow a thread to receive and act on a cancellation request without being canceled. I'm not sure whether making this possible is a good thing or a bad thing. If we think it's good, then that would simplify the design. --Matt From jason at redhat.com Tue Jan 6 21:50:29 2004 From: jason at redhat.com (Jason Merrill) Date: Tue, 06 Jan 2004 16:50:29 -0500 Subject: [c++-pthreads] What are the real issues? In-Reply-To: <64F121A7-408F-11D8-B918-00039390D9E0@apple.com> (Matt Austern's message of "Tue, 6 Jan 2004 13:29:22 -0800") References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <20040106211236.GX30780@tofu.dreamhost.com> <64F121A7-408F-11D8-B918-00039390D9E0@apple.com> Message-ID: On Tue, 6 Jan 2004 13:29:22 -0800, Matt Austern wrote: > On Jan 6, 2004, at 1:12 PM, Nathan Myers wrote: > >>> ... I think the really fundamental issue is whether a thread >>> should be allowed to receive a cancellation request, start to do some >>> work as a result of the request, and then decide that it doesn't want >>> to be cancelled. >> >> Nobody has proposed any such thing. > > Really? I think that you and I both did. > (1) My proposal (which may or may not be a good idea, but which > I think should be considered) is that cancellation could be > represented by an ordinary exception instead of by a special > kind of exception with forced unwinding semantics. This would > allow a thread to catch the exception, set a flag, swallow the > exception, and continue executing. that is, it would allow a > thread to receive and act on a cancellation request but for it not > to be canceled. My proposal is that it acts as a normal exception, but that * If a cancellation exception is destroyed, the cancellation request is re-entered, and acted on again at the next cancellation point. So the cancellation request never goes away until the thread does. > (2) Your proposal is that the POSIX functions that are currently > described as cancellation points shouldn't raise any kind of > exception and shouldn't cause the thread to stop executing, > but should instead return an error code. Again, this would allow > a thread to note the error return, check errno to see if it's > ETHREADCANCELED, set a flag as a result of having received > the cancelation request, and continue executing. Again, your > proposal would allow a thread to receive and act on a cancellation > request without being canceled. You are assuming that this would only happen for the first call to a cancellation point, which doesn't seem to be what Nathan had in mind. Here's his proposal again: C library and system calls never throw. In the event of cancellation, cancellation point functions report failure via their normal means (e.g. return -1, or NULL), and set errno if appropriate. Blocked calls fail immediately. C++ library functions that are normally allowed to throw may throw a cancellation exception. (E.g. std::filebuf::overflow().) A thread, once cancelled, stays cancelled, regardless of any C++ catch blocks entered or left. C++ catch blocks work normally; a cancellation exception may be rethrown, or swallowed like any other. In a catch block handling a cancellation exception, functions identified as cancellation points work normally. When a cancellation exception is swallowed by a catch block, subsequently-called cancellation point functions report failure, as before, until another cancellation exception is thrown. Eventually the inability of the thread to achieve anything leads it to top-level code equipped to recognize the cancelled condition as such, and to clean up and die in a controlled way. Jason From rth at redhat.com Tue Jan 6 22:15:51 2004 From: rth at redhat.com (Richard Henderson) Date: Tue, 6 Jan 2004 14:15:51 -0800 Subject: [c++-pthreads] What are the real issues? In-Reply-To: <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> Message-ID: <20040106221551.GA18646@redhat.com> On Tue, Jan 06, 2004 at 11:33:47AM -0800, Matt Austern wrote: > The Itanium C++ ABI, which gcc adopted, made cancellation a special > kind of exception, "forced unwinding", so that a thread can't just > catch the cancellation exception and swallow it. This is not correct. The IA-64 ABI describes "forced unwinding" but does not describe its semantics at all. Which is of course completely unhelpful. My initial implementation of forced unwinding skipped catch-all, and ran destructors. This was vetoed by G++ folks. The current implementation of forced unwinding is to treat it just like any other kind of exception. Almost useless, IMO, since there's now a high likelyhood that longjmp_unwind will not arrive at its intended destination. The current protection against swallowing thread cancellation (and longjmp_unwind) is done by having the destructor for the exception object call abort. > ... we should probably do is abandon the notion of forced unwinding > and make cancellation into an ordinary exception. We already have, modulo the fact that the current implementation has no typename. r~ From austern at apple.com Tue Jan 6 22:22:13 2004 From: austern at apple.com (Matt Austern) Date: Tue, 6 Jan 2004 14:22:13 -0800 Subject: [c++-pthreads] What are the real issues? In-Reply-To: <20040106221551.GA18646@redhat.com> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <20040106221551.GA18646@redhat.com> Message-ID: On Jan 6, 2004, at 2:15 PM, Richard Henderson wrote: > On Tue, Jan 06, 2004 at 11:33:47AM -0800, Matt Austern wrote: >> The Itanium C++ ABI, which gcc adopted, made cancellation a special >> kind of exception, "forced unwinding", so that a thread can't just >> catch the cancellation exception and swallow it. > > This is not correct. The IA-64 ABI describes "forced unwinding" > but does not describe its semantics at all. Which is of course > completely unhelpful. That's interesting. Sorry for the goof! That sounds like a pretty serious problem that we ought to fix no matter what else we do. --Matt From ncm at cantrip.org Tue Jan 6 22:33:16 2004 From: ncm at cantrip.org (Nathan Myers) Date: Tue, 6 Jan 2004 14:33:16 -0800 Subject: [c++-pthreads] What are the real issues? In-Reply-To: <64F121A7-408F-11D8-B918-00039390D9E0@apple.com> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <20040106211236.GX30780@tofu.dreamhost.com> <64F121A7-408F-11D8-B918-00039390D9E0@apple.com> Message-ID: <20040106223316.GY30780@tofu.dreamhost.com> On Tue, Jan 06, 2004 at 01:29:22PM -0800, Matt Austern wrote: > On Jan 6, 2004, at 1:12 PM, Nathan Myers wrote: > > >>... I think the really fundamental issue is whether a thread > >>should be allowed to receive a cancellation request, start to do some > >>work as a result of the request, and then decide that it doesn't want > >>to be cancelled. > > > >Nobody has proposed any such thing. > > Really? I think that you and I both did. I didn't. Jason didn't. Those were the only models posted. If you want to post a model or a family of models, we can discuss them too. Did you have some reason for suggesting that cancellations might be readily discarded, or did you think somebody else had asked for it? > (2) Your proposal is that the POSIX functions that are currently > described as cancellation points shouldn't raise any kind of > exception and shouldn't cause the thread to stop executing, > but should instead return an error code. Again, this would allow > a thread to note the error return, check errno to see if it's > ETHREADCANCELED, set a flag as a result of having received > the cancelation request, and continue executing. Again, your > proposal would allow a thread to receive and act on a > cancellation request without being canceled. No, the thread remains cancelled. The difference between that and the thread actually discarding the cancellation request is that the next time a function identified as a cancellation point is called, it fails, too. The cancellation is not lost, or discarded, or ignored; it surfaces again and again, indefinitely, as long as the thread fails to terminate. Furthermore, a subsequent (e.g.) filebuf operation would actually throw. If that exception were caught and discarded, the next would throw again. Somebody described this as "sticky cancellation". Under POSIX C, a thread cancellation handler can continue to run indefinitely, so you're not describing something fundamentally different from the C model. Likewise, if unwinding were to run catch clauses, code in a catch clause could continue to run indefinitely. Nathan Myers ncm at cantrip.org From ncm at cantrip.org Tue Jan 6 22:48:13 2004 From: ncm at cantrip.org (Nathan Myers) Date: Tue, 6 Jan 2004 14:48:13 -0800 Subject: [c++-pthreads] What are the real issues? In-Reply-To: <20040106221551.GA18646@redhat.com> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <20040106221551.GA18646@redhat.com> Message-ID: <20040106224813.GZ30780@tofu.dreamhost.com> On Tue, Jan 06, 2004 at 02:15:51PM -0800, Richard Henderson wrote: > On Tue, Jan 06, 2004 at 11:33:47AM -0800, Matt Austern wrote: > My initial implementation of forced unwinding skipped catch-all, > and ran destructors. This was vetoed by G++ folks. This is an example of proposed semantics (fortunately vetoed) that would have corrupted the process state, making the unwind pointless. > The current implementation of forced unwinding is to treat it > just like any other kind of exception. Almost useless, IMO, > since there's now a high likelyhood that longjmp_unwind will > not arrive at its intended destination. If that's part of its definition, then new code can be written with that in mind. Vanishingly little existing code makes longjmp_unwind calls, so there need be no concern (in particular) about breaking third-party libraries. Nathan Myers ncm at cantrip.org From dave at boost-consulting.com Tue Jan 6 23:31:23 2004 From: dave at boost-consulting.com (David Abrahams) Date: Tue, 06 Jan 2004 18:31:23 -0500 Subject: cancellation points report failure References: <2A8DB02E3018D411901B009027FD3A3F01CAAF6A@mchp905a.mch.sbs.de> <2E98D8A4-3251-11D8-B7E5-00039390D9E0@apple.com> <20031219194503.GA31795@tofu.dreamhost.com> <20031219205746.GA12322@diablo.name> <20031219225650.GA13735@diablo.name> <20031220045618.GA27836@tofu.dreamhost.com> <1071946483.25325.69.camel@doubledemon.codesourcery.com> Message-ID: Jason Merrill writes: > On Wed, 24 Dec 2003 12:38:46 -0500, David Abrahams wrote: > >> Jason Merrill writes: >> >>> * C++ I/O functions can throw a cancellation exception. >>> * C++ catch blocks work normally. >>> * If a cancellation exception is destroyed, the cancellation request >>> is re-entered, and acted on again at the next cancellation point. >> >> I appreciate that your motivation is to ensure that synchronous >> cancellation requests aren't ignored, but that doesn't really seem to >> accomplish the goal. After all, a thread never has to reach a >> cancellation point, and even if it does, it can keep catching and >> discarding the exceptions indefinitely. > > That's fine with me. I'm not trying to assure that cancellation always > succeeds; as you say, a thread never has to reach a cancellation point. > My goal is to make sure that the cancellation request is never lost, > that it remains either active or pending until the thread exits. OK. I don't have enough of an understanding of the reasons for cancellation in the first place to know whether it's a good goal or not, but I don't see any major problems with it. AFAICT, the difference between your proposal and Nathan's, once cancellation is thrown, is that your proposal can prevent cancellation from being thrown unexpectedly in a catch block (**) that's part of an unwind sequence? (**) From a destructor during unwinding also, but that could be prevented using std::unhandled_exception(). -- Dave Abrahams Boost Consulting www.boost-consulting.com From dave at boost-consulting.com Tue Jan 6 23:33:51 2004 From: dave at boost-consulting.com (David Abrahams) Date: Tue, 06 Jan 2004 18:33:51 -0500 Subject: What are the real issues? References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> Message-ID: Matt Austern writes: > Third, some people simply object to having cancellation be an > exception (or anything that looks like an exception): Who? > it means there are some functions that can throw exceptions in the > presence of threads that wouldn't throw in a single-threaded > program. I don't think it means that. We might choose to throw cancellation exceptions only from functions that could throw in a single-threaded app or (threading-specific) functions that can't appear in a single-threaded app. -- Dave Abrahams Boost Consulting www.boost-consulting.com From hinnant at twcny.rr.com Wed Jan 7 02:50:09 2004 From: hinnant at twcny.rr.com (Howard Hinnant) Date: Tue, 6 Jan 2004 21:50:09 -0500 Subject: [c++-pthreads] Re: What are the real issues? In-Reply-To: References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> Message-ID: <3503666E-40BC-11D8-A006-003065D18932@twcny.rr.com> I've been debating whether to stay silent and merely suspect I'm an idiot, or to open my mouth and remove all doubt... Basic question: Are we trying to solve a problem that really exists? I'm putting aside for the moment asynchronous cancelation as once you get into that you give up all hope of releasing resources. For now I'm simply discussing deferred cancelation. In C you need an organized system in which to register cleanup handlers to release resources in case a thread is canceled. You also need a way to temporarily disable cancelations. And finally you need a mechanism by which a thread can be notified that someone has requested cancelation. The POSIX lib neatly supplies all of these requirements. In C++ we have such a different environment than in C that I am questioning whether there is a need for a library supported cancelation mechanism in the first place. One thread can easily request another thread to cancel itself via a reference to a shared variable (which need not be global). As long as the "cancelation mechanism" is via a thrown exception, there is no need for cleanup handlers. And since a thread can manually check whether its cancel variable has been set or not, there is no need to disable cancelations (the thread simply doesn't check whether it has been canceled if it isn't willing to cancel itself). In other words, the C++ language makes it so easy to write "cancelable threads" and "cancel-safe code", are we making an easy problem hard by trying to code a solution into a library? For example below is a simple multi-thread helloworld using Metrowerks::threads (think boost::threads) where the main thread creates and then cancels a couple of worker threads. Resources are acquired and released properly, not due to the Metrowerks::threads lib, but due to the C++ language itself (RAII and basic exception safety). One might point out that the demo below relies on polling to detect thread cancelation, and this is true. But pthread cancellation also seems to rely on polling, so . The demo also avoids global variables to communicate cancelation to the threads by using function objects instead of functions (a technique unavailable to the C programmer). And of course, the use of exceptions and (basic) exception-safe code eliminates the need for cleanup handlers (also unavailable to the C programmer). So is the solution we're designing here somehow superior to what the C++ programmer can easily do for himself already? I'm merely asking, I'm not claiming to know the answer to that question. #include #include Metrowerks::mutex cout_mutex; // A is a generic resource holder class class A { public: explicit A(int id) : id_(id) { Metrowerks::mutex::scoped_lock lock(cout_mutex); std::cout << "A(" << id_ << ") constructed\n"; } ~A() { Metrowerks::mutex::scoped_lock lock(cout_mutex); std::cout << "A(" << id_ << ") destructed\n"; } private: A(const A&); A& operator=(const A&); int id_; }; struct cancel {}; // func is a generic (cancelable) thread-worker function object class func { public: func(int id, volatile bool& b) : id_(id), cancel_(b) {} void operator()() const; private: void job1() const; void sub_job1() const; void job2() const; int id_; volatile bool& cancel_; }; void func::operator()() const { unsigned i = 0; A a(id_); while (true) { try { job1(); job2(); Metrowerks::mutex::scoped_lock lock(cout_mutex); std::cout << i++ << std::endl; } catch (std::exception& e) { // fix and swallow std::exceptions, but not a cancel Metrowerks::mutex::scoped_lock lock(cout_mutex); std::cout << "Fixing " << e.what() << '\n'; } catch (cancel& e) { // Note (but don't swallow) cancel Metrowerks::mutex::scoped_lock lock(cout_mutex); std::cout << "Canceling " << id_ << '\n'; throw; } catch (...) { // Note (but don't swallow) non-std exceptions Metrowerks::mutex::scoped_lock lock(cout_mutex); std::cout << "Unknown exception - rethrowing\n"; throw; } } } void func::job1() const { A a(id_); sub_job1(); } void func::sub_job1() const { A a(id_); if (cancel_) throw cancel(); } void func::job2() const { A a(id_); if (cancel_) throw cancel(); } int main() { try { // main can create and cancel worker threads volatile bool cancel1 = false; volatile bool cancel2 = false; Metrowerks::thread t1(func(1, cancel1)); Metrowerks::thread t2(func(2, cancel2)); Metrowerks::thread::sleep(Metrowerks::elapsed_time(1)); cancel2 = true; Metrowerks::thread::sleep(Metrowerks::elapsed_time(1)); cancel1 = true; t2.join(); t1.join(); std::cout << "Done\n"; } catch (std::exception& e) { // Note std::exceptions Metrowerks::mutex::scoped_lock lock(cout_mutex); std::cout << e.what() << " - Abnormal termination\n"; } catch (...) { // Note non-std exceptions Metrowerks::mutex::scoped_lock lock(cout_mutex); std::cout << "Unknown exception - Abnormal termination\n"; } } A(1) constructed A(1) constructed A(1) constructed A(1) destructed A(1) destructed A(1) constructed A(1) destructed 0 ... 3093 A(1) constructed A(1) constructed A(1) destructed A(1) destructed A(1) constructed A(1) destructed Canceling 1 A(1) destructed Done -Howard From fjh at cs.mu.OZ.AU Wed Jan 7 03:06:40 2004 From: fjh at cs.mu.OZ.AU (Fergus Henderson) Date: Wed, 7 Jan 2004 14:06:40 +1100 Subject: [c++-pthreads] Re: What are the real issues? In-Reply-To: <3503666E-40BC-11D8-A006-003065D18932@twcny.rr.com> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <3503666E-40BC-11D8-A006-003065D18932@twcny.rr.com> Message-ID: <20040107030640.GA2175@ceres.cs.mu.oz.au> On 06-Jan-2004, Howard Hinnant wrote: > In other words, the C++ language makes it so easy to write "cancelable > threads" and "cancel-safe code", are we making an easy problem hard by > trying to code a solution into a library? Sure you can write your own cancellation framework. That can be done quite easily in C too. The point of standardizing a particular cancellation framework is to enable interoperability between code written by different parties. For example, this is needed to allow application code to cancel threads which happen to be running third-party library code, or even standard library code such as getchar() or system calls such as read(). -- Fergus Henderson | "I have always known that the pursuit The University of Melbourne | of excellence is a lethal habit" WWW: | -- the last words of T. S. Garp. From fjh at cs.mu.OZ.AU Wed Jan 7 03:24:38 2004 From: fjh at cs.mu.OZ.AU (Fergus Henderson) Date: Wed, 7 Jan 2004 14:24:38 +1100 Subject: [c++-pthreads] What are the real issues? In-Reply-To: <20040106211236.GX30780@tofu.dreamhost.com> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <20040106211236.GX30780@tofu.dreamhost.com> Message-ID: <20040107032438.GB2175@ceres.cs.mu.oz.au> On 06-Jan-2004, Nathan Myers wrote: > > On Tue, Jan 06, 2004 at 11:33:47AM -0800, Matt Austern wrote: > > My feeling: it's just plain inevitable that a multithreaded program has > > more functions that might throw than a single-thread program. Dealing > > with this is part of what it means to make a program thread-safe. > > This is far from clear, and makes a very poor starting assumption. We > have had thread-safe libraries for a long time. Under one proposed > model most can simply be declared already "cancellation-safe", without > changes. Thread-safe code which may perform unbounded computation without encountering a cancellation-point is not what I would call cancellation-safe. So even if code has been written to be thread-safe, you need to analyze it in detail in order to be sure that it will be properly cancellation-safe. Perhaps we need to talk about two different kinds of cancellation safety; I'll call them "weakly cancellation safe" and "strongly cancellation safe". For the former, you'd be guaranteed that it was OK to send a cancellation request to threads running such code, but the cancellation request might be delayed indefinitely or ignored. For the latter, you'd be guaranteed that it was OK to send a cancellation request, and in addition there would be a guarantee that the cancellation request would be acted on in a finite amount of time, i.e. that the cancelled thread would actually terminate. (Suggestions on terminology welcome. Are there existing terms for these notions?) Using this terminology, my point above is that thread safety does not imply strong cancellation safety. -- Fergus Henderson | "I have always known that the pursuit The University of Melbourne | of excellence is a lethal habit" WWW: | -- the last words of T. S. Garp. From hinnant at twcny.rr.com Wed Jan 7 03:24:57 2004 From: hinnant at twcny.rr.com (Howard Hinnant) Date: Tue, 6 Jan 2004 22:24:57 -0500 Subject: [c++-pthreads] Re: What are the real issues? In-Reply-To: <20040107030640.GA2175@ceres.cs.mu.oz.au> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <3503666E-40BC-11D8-A006-003065D18932@twcny.rr.com> <20040107030640.GA2175@ceres.cs.mu.oz.au> Message-ID: <116612D3-40C1-11D8-A006-003065D18932@twcny.rr.com> On Jan 6, 2004, at 10:06 PM, Fergus Henderson wrote: > The point of standardizing a particular cancellation framework > is to enable interoperability between code written by different > parties. > For example, this is needed to allow application code to cancel threads > which happen to be running third-party library code, or even standard > library code such as getchar() or system calls such as read(). Thanks, I appreciate the need for code written by different parties to work together in a thread/cancel environment. In the scenario I've outlined, code written by different parties would work together in a thread/cancel environment as long as: 1. The code met basic exception safety requirements. 2. The code was not compute-bound (and thus ignored cancellation requests). For example, I could've sprinkled my helloworld with std::lib calls, and it still would've been ok because the std::lib maintains basic exception safety. Admittedly if a std::lib call took an inordinate amount of time, cancellation requests would be ignored during that call. But it seems we already have the risk of ignoring cancellation requests anyway. You correctly point out that YourLib would not recognize cancellation requests from MyLib. However, YourLib could correctly clean up its resources in the event that it called MyLib which subsequently initiated cancellation. Is that sufficient interoperability? -Howard From fjh at cs.mu.OZ.AU Wed Jan 7 03:57:04 2004 From: fjh at cs.mu.OZ.AU (Fergus Henderson) Date: Wed, 7 Jan 2004 14:57:04 +1100 Subject: [c++-pthreads] Re: What are the real issues? In-Reply-To: <116612D3-40C1-11D8-A006-003065D18932@twcny.rr.com> References: <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <3503666E-40BC-11D8-A006-003065D18932@twcny.rr.com> <20040107030640.GA2175@ceres.cs.mu.oz.au> <116612D3-40C1-11D8-A006-003065D18932@twcny.rr.com> Message-ID: <20040107035704.GD2175@ceres.cs.mu.oz.au> On 06-Jan-2004, Howard Hinnant wrote: > In the scenario I've outlined, code written by different parties would > work together in a thread/cancel environment as long as: > > 1. The code met basic exception safety requirements. > 2. The code was not compute-bound (and thus ignored cancellation > requests). It would also require 3. The code doesn't block for I/O. wouldn't it? > For example, I could've sprinkled my helloworld with std::lib calls, > and it still would've been ok because the std::lib maintains basic > exception safety. Won't it fail to cancel the threads if the output blocks, e.g. because it is redirected to Unix pipe that fills up? > Admittedly if a std::lib call took an inordinate > amount of time, cancellation requests would be ignored during that > call. But it seems we already have the risk of ignoring cancellation > requests anyway. That risk of cancellations being ignored can be avoided by ensuring that all the code that you call is strongly cancellation-safe. The problem with your suggested approach is that unless the cancellation framework is part of the standard library, in general it's not possible for third parties to write strongly cancellation-safe code! Writing strongly cancellation-safe code requires (a) being able to poll for cancellation and (b) being able to do blocking I/O in a way that will wake up when a cancellation request is received. -- Fergus Henderson | "I have always known that the pursuit The University of Melbourne | of excellence is a lethal habit" WWW: | -- the last words of T. S. Garp. From ncm at cantrip.org Wed Jan 7 05:15:11 2004 From: ncm at cantrip.org (Nathan Myers) Date: Tue, 6 Jan 2004 21:15:11 -0800 Subject: [c++-pthreads] What are the real issues? In-Reply-To: <20040107032438.GB2175@ceres.cs.mu.oz.au> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <20040106211236.GX30780@tofu.dreamhost.com> <20040107032438.GB2175@ceres.cs.mu.oz.au> Message-ID: <20040107051511.GM32272@tofu.dreamhost.com> On Wed, Jan 07, 2004 at 02:24:38PM +1100, Fergus Henderson wrote: > On 06-Jan-2004, Nathan Myers wrote: > > > > On Tue, Jan 06, 2004 at 11:33:47AM -0800, Matt Austern wrote: > > > My feeling: it's just plain inevitable that a multithreaded program has > > > more functions that might throw than a single-thread program. Dealing > > > with this is part of what it means to make a program thread-safe. > > > > This is far from clear, and makes a very poor starting assumption. We > > have had thread-safe libraries for a long time. Under one proposed > > model most can simply be declared already "cancellation-safe", without > > changes. > > Thread-safe code which may perform unbounded computation without > encountering a cancellation-point is not what I would call > cancellation-safe. I can't speak for what you would call cancellation-safe, but the POSIX C binding committee certainly calls libraries with that property cancellation-safe. > So even if code has been written to be thread-safe, you need to > analyze it in detail in order to be sure that it will be properly > cancellation-safe. > > Perhaps we need to talk about two different kinds of cancellation > safety; I'll call them "weakly cancellation safe" and "strongly > cancellation safe". For the former, you'd be guaranteed that it was > OK to send a cancellation request to threads running such code, but > the cancellation request might be delayed indefinitely or ignored. > For the latter, you'd be guaranteed that it was OK to send a > cancellation request, and in addition there would be a guarantee that > the cancellation request would be acted on in a finite amount of time, > i.e. that the cancelled thread would actually terminate. (Suggestions > on terminology welcome. Are there existing terms for these notions?) > > Using this terminology, my point above is that thread safety does not > imply strong cancellation safety. It seems the POSIX people (wisely) gave up on defining any concept of your strong-cancellation-safety. Consider that in C code, even ensuring garden-variety cancellation safety is a hit-or-miss affair: cancellation handlers are subject to rot under the best circumstances. We have better prospects in C++, if we can, as it were, hitch the cancellation-safety wagon to the exception-safety horse. Well-written exception handling code (i.e. implemented mainly in destructors) is much less subject to rot, being exercised on each block exit. Many more people know how to write exception-safe C++ code than C cancellation-safe code. (I'm not sure I would be able to write the latter, maintainably. C simply lacks the tools for it.) Nathan Myers ncm at cantrip.org From ncm at cantrip.org Wed Jan 7 05:26:44 2004 From: ncm at cantrip.org (Nathan Myers) Date: Tue, 6 Jan 2004 21:26:44 -0800 Subject: [c++-pthreads] Re: cancellation points report failure In-Reply-To: <3FFAA301.6080301@hp.com> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> Message-ID: <20040107052644.GO32272@tofu.dreamhost.com> On Tue, Jan 06, 2004 at 06:58:57AM -0500, Dave Butenhof wrote: > Personally, I would be happy to accept a C++ binding with no way to > enable asynchronous cancelability, and to avoid defining any C++ code > as "async cancel-safe". I think we're all agreed on that. This list is about semantics of a C++ binding for synchronous cancellation. Nathan Myers ncm at cantrip.org From ncm at cantrip.org Wed Jan 7 07:17:50 2004 From: ncm at cantrip.org (Nathan Myers) Date: Tue, 6 Jan 2004 23:17:50 -0800 Subject: [c++-pthreads] concrete library-code example In-Reply-To: <3FF9977C.7040003@hp.com> References: <3FE70542.5060104@bourguet.org> <1072111706.3474.17.camel@minax.codesourcery.com> <20031222215712.GB1528@diablo.name> <1072131497.7562.22.camel@doubledemon.codesourcery.com> <20031223160314.GA9780@diablo.name> <20031224170517.GG30780@tofu.dreamhost.com> <3FF9977C.7040003@hp.com> Message-ID: <20040107071750.GP32272@tofu.dreamhost.com> A quick reply... On Mon, Jan 05, 2004 at 11:57:32AM -0500, Dave Butenhof wrote: > Nathan Myers wrote: > > > >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." Very few programmers can identify any POSIX definition by number. They write, and have long written, exception-safe library code that, at most, uses mutexes (wrapped carefully for portability!) to guard global state. Few have even heard of cancellation. Many millions of lines of such code have been running for years on millions of installations, worldwide. It's good code. To pretend that it's all suddenly worthless because it doesn't take into account new (or newly-deployed) standard revision 7834-stroke-"b"-slash- 667-stroke-"a" would simply make _us_ irrelevant. > > 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. No. The code was written to a documented interface. Whoever changes the interface semantics without changing the interface name is responsible for corrupting the 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. Sorry, that's simply disingenuous. To argue that everybody should have coded to an interface that you only just got around to documenting, implementing, and deploying, many years after the code was written, borders on arrogant contempt. Such an attitude may be fine for the POSIX C committee, but I see no reason to match it here. In any case, we have a great deal more already-thread-safe code to preserve, because thread-safety (by the common definition) is the norm, in C++. > 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. Again, that reasoning may be fine for C (did you really ask all those C programmers?), but we need not be bound by it here. Since a cancellation error return swallowed up in library code must surface again at the next cancellation point, eventually (given a well-written library) the failure must propagate upward to the point where it may be turned into an exception. (A library that never propagates system-call failures to its caller isn't anything-safe, and needn't concern us.) > >(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. No. I'm talking about the many millions of lines of existing thread-safe library code. Ordinary thread-safety is the norm in C++ libraries, because it's the natural way to code, in C++. > 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. Exception-safety depends on identifying and guarding against documented sources of possible exceptions. System calls and C library functions are not among those. Also, C++ libraries very frequently rely on underlying C libraries, and are written to depend on their documented behavior. (None of my man(2) or man(3) pages mention unwinding, never mind throwing.) Even if you claim that the threat of "unwinding" from system calls is ancient, and that everything should have been written to assume it, a change to make them throw would be completely new. > 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! No offense intended, but disingenuity makes a poor substitute for responsible design. We can afford to be more responsible here, because we have stronger language semantics to work with, and well-worked-out exception-safety standards. Nathan Myers ncm at cantrip.org From rth at redhat.com Wed Jan 7 09:57:58 2004 From: rth at redhat.com (Richard Henderson) Date: Wed, 7 Jan 2004 01:57:58 -0800 Subject: [c++-pthreads] What are the real issues? In-Reply-To: <20040106224813.GZ30780@tofu.dreamhost.com> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <20040106221551.GA18646@redhat.com> <20040106224813.GZ30780@tofu.dreamhost.com> Message-ID: <20040107095758.GB21007@redhat.com> On Tue, Jan 06, 2004 at 02:48:13PM -0800, Nathan Myers wrote: > This is an example of proposed semantics (fortunately vetoed) that > would have corrupted the process state, making the unwind pointless. Only with stupid C++ users using catch-all when they should have been using destructors in the first place. But that's another debate. r~ From wil at bogo.xs4all.nl Wed Jan 7 11:06:45 2004 From: wil at bogo.xs4all.nl (Wil Evers) Date: Wed, 07 Jan 2004 12:06:45 +0100 Subject: [c++-pthreads] What are the real issues? References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <20040106211236.GX30780@tofu.dreamhost.com> <64F121A7-408F-11D8-B918-00039390D9E0@apple.com> <20040106223316.GY30780@tofu.dreamhost.com> Message-ID: <3FFBE845.9040707@bogo.xs4all.nl> Nathan Myers wrote: > On Tue, Jan 06, 2004 at 01:29:22PM -0800, Matt Austern wrote: >> (2) Your proposal is that the POSIX functions that are currently >> described as cancellation points shouldn't raise any kind of >> exception and shouldn't cause the thread to stop executing, >> but should instead return an error code. Again, this would allow >> a thread to note the error return, check errno to see if it's >> ETHREADCANCELED, set a flag as a result of having received >> the cancelation request, and continue executing. Again, your >> proposal would allow a thread to receive and act on a >> cancellation request without being canceled. > > > No, the thread remains cancelled. The difference between that and > the thread actually discarding the cancellation request is that the > next time a function identified as a cancellation point is called, it > fails, too. The cancellation is not lost, or discarded, or ignored; > it surfaces again and again, indefinitely, as long as the thread fails > to terminate. Furthermore, a subsequent (e.g.) filebuf operation > would actually throw. If that exception were caught and discarded, > the next would throw again. Somebody described this as "sticky > cancellation". OK, that sounds reasonable - the 'sticky cancellation' concept provides some guarantee that a thread that ignores or discards a cancellation request will be reminded later. However, it seems to me that the idea of simply having system calls defined as cancellation points fail until the cancelled thread has finished its job is just too crude. In particular, it would prevent the thread being cancelled from doing any I/O at all, even when that I/O is part of the cleanup the thread is supposed to perform in handling the cancellation request. Your ETHREADCANCELLED proposal relies on higher-level C++ code to turn this errno value into some kind of thread_cancelled exception at a point where such an exception can be reasonably expected. This seems to suggest that the underlying machinery in the system call interface library will be unaware of the actual steps the thread in question takes in honoring the cancellation request: it would simply report that cancellation has been requested, leaving it to higher-level code to take appropriate action. If that is the case, then how do we stop the ETHREADCANCELLED error from resurfacing - possibly triggering a C++ exception or even a call to terminate() - while the thread is busy cleaning up? - Wil From boo at terekhov.de Wed Jan 7 11:47:34 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Wed, 07 Jan 2004 12:47:34 +0100 Subject: [c++-pthreads] Re: cancellation points report failure References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <20040107052644.GO32272@tofu.dreamhost.com> Message-ID: <3FFBF1D6.56ABAC@terekhov.de> Nathan Myers wrote: > > On Tue, Jan 06, 2004 at 06:58:57AM -0500, Dave Butenhof wrote: > > Personally, I would be happy to accept a C++ binding with no way to > > enable asynchronous cancelability, and to avoid defining any C++ code > > as "async cancel-safe". > > I think we're all agreed on that. This list is about semantics of > a C++ binding for synchronous cancellation. Objection. To begin with, asynchronous cancelability IS part of pthreads. Well, void operation(); blabla another_operation( ...blablabla... ) async_cancel_safe; void operation() { // cancel points DO throw here (if expected) 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 ...; } regards, alexander. From boo at terekhov.de Wed Jan 7 11:54:51 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Wed, 07 Jan 2004 12:54:51 +0100 Subject: [c++-pthreads] Re: cancellation points report failure References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> Message-ID: <3FFBF38B.1E19A2F3@terekhov.de> David Abrahams wrote: [...] > it? Interesting thought, if so, but how do you disable just > cancellation without masking other exceptions? no_cancel { } Of course. regards, alexander. From boo at terekhov.de Wed Jan 7 12:16:05 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Wed, 07 Jan 2004 13:16:05 +0100 Subject: [c++-pthreads] What are the real issues? References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <20040106221551.GA18646@redhat.com> Message-ID: <3FFBF885.4B89C2E0@terekhov.de> Matt Austern wrote: > > On Jan 6, 2004, at 2:15 PM, Richard Henderson wrote: > > > On Tue, Jan 06, 2004 at 11:33:47AM -0800, Matt Austern wrote: > >> The Itanium C++ ABI, which gcc adopted, made cancellation a special > >> kind of exception, "forced unwinding", so that a thread can't just > >> catch the cancellation exception and swallow it. > > > > This is not correct. The IA-64 ABI describes "forced unwinding" > > but does not describe its semantics at all. Which is of course > > completely unhelpful. > > That's interesting. Sorry for the goof! > > That sounds like a pretty serious problem that we ought to fix > no matter what else we do. Get rid of it entirely. setjmp()/longjmp() can (and shall) be implemented without "the stop and stop_parameter parameters" which "control the termination of the unwind process, instead of the usual personality routine query", I believe. Ignoring the fact that jumping is severely restricted in C++, compilers are just ought to sort of "recognize" setjmp() and allow rather simple implementation of longjmp() -- "throw std::longjmp_request(val);" (or something like that), oder? regards, alexander. From boo at terekhov.de Wed Jan 7 12:33:26 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Wed, 07 Jan 2004 13:33:26 +0100 Subject: [c++-pthreads] What are the real issues? References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <20040106211236.GX30780@tofu.dreamhost.com> <20040107032438.GB2175@ceres.cs.mu.oz.au> <20040107051511.GM32272@tofu.dreamhost.com> Message-ID: <3FFBFC96.F419A280@terekhov.de> Nathan Myers wrote: [...] > We have better prospects in C++, if we can, as it were, hitch the > cancellation-safety wagon to the exception-safety horse. Well-written > exception handling code (i.e. implemented mainly in destructors) is > much less subject to rot, being exercised on each block exit. Many > more people know how to write exception-safe C++ code than C > cancellation-safe code. (I'm not sure I would be able to write the > latter, maintainably. C simply lacks the tools for it.) P in "pthreads" stands for POSIX, not C, I guess. #define pthread_cleanup_push(routine, arg) \ { \ ScopeGuard _guard = MakeObjGuard(arg, routine); #define pthread_cleanup_pop(execute) \ if (!execute) _guard.Dismiss(); \ } http://www.cuj.com/documents/s=8000/cujcexp1812alexandr (Simplify Your Exception-Safe Code) regards, alexander. From boo at terekhov.de Wed Jan 7 13:25:29 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Wed, 07 Jan 2004 14:25:29 +0100 Subject: [c++-pthreads] What are the real issues? References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <20040106211236.GX30780@tofu.dreamhost.com> <20040107032438.GB2175@ceres.cs.mu.oz.au> Message-ID: <3FFC08C9.F2A93D4A@terekhov.de> Fergus Henderson wrote: [...] > Thread-safe code which may perform unbounded computation without encountering > a cancellation-point is not what I would call cancellation-safe. Yep. > > So even if code has been written to be thread-safe, you need to analyze it > in detail in order to be sure that it will be properly cancellation-safe. http://google.com/groups?selm=WJUO8.15690%24qL5.827507%40news4.srv.hcvlny.cv.net http://google.com/groups?selm=3D0EE2D6.34EE9712%40web.de regards, alexander. From boo at terekhov.de Wed Jan 7 13:29:14 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Wed, 07 Jan 2004 14:29:14 +0100 Subject: [c++-pthreads] What are the real issues? References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <20040106211236.GX30780@tofu.dreamhost.com> <20040107032438.GB2175@ceres.cs.mu.oz.au> <20040107051511.GM32272@tofu.dreamhost.com> <3FFBFC96.F419A280@terekhov.de> <1073479235.28355.97.camel@felix.inria.fr> Message-ID: <3FFC09AA.99BF37A7@terekhov.de> Mathieu Lacage wrote: [...] > > http://www.cuj.com/documents/s=8000/cujcexp1812alexandr > > (Simplify Your Exception-Safe Code) > > File Not Found > cujcexp1812alexandr Try http://www.cuj.com/documents/s=8000/cujcexp1812alexandr/ regards, alexander. From David.Butenhof at hp.com Wed Jan 7 15:41:38 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Wed, 07 Jan 2004 10:41:38 -0500 Subject: [c++-pthreads] concrete library-code example In-Reply-To: <20040107071750.GP32272@tofu.dreamhost.com> References: <3FE70542.5060104@bourguet.org> <1072111706.3474.17.camel@minax.codesourcery.com> <20031222215712.GB1528@diablo.name> <1072131497.7562.22.camel@doubledemon.codesourcery.com> <20031223160314.GA9780@diablo.name> <20031224170517.GG30780@tofu.dreamhost.com> <3FF9977C.7040003@hp.com> <20040107071750.GP32272@tofu.dreamhost.com> Message-ID: <3FFC28B2.707@hp.com> Nathan Myers wrote: >On Mon, Jan 05, 2004 at 11:57:32AM -0500, Dave Butenhof wrote: > > >>Nathan Myers wrote: >> >> >>>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." >> >> >Very few programmers can identify any POSIX definition by number. >They write, and have long written, exception-safe library code that, >at most, uses mutexes (wrapped carefully for portability!) to guard >global state. Few have even heard of cancellation. > >Many millions of lines of such code have been running for years on >millions of installations, worldwide. It's good code. To pretend >that it's all suddenly worthless because it doesn't take into account >new (or newly-deployed) standard revision 7834-stroke-"b"-slash- >667-stroke-"a" would simply make _us_ irrelevant. > > You're right -- most people don't understand the distinctions between POSIX revisions, so perhaps I've presumed too much. So I'll make this clear. Once you're talking about "POSIX" and "threads", there's no such ambiguity. There were no concept at all of "threads" in POSIX before there was cancellation. Cancellation is a required base feature of "pthreads", and has been since well before the first (semi-)public draft of the specification. There is no such thing as "pthreads" without cancellation, and never has been. A library coded to "1003.lb-1993 or before" can't use cancellation... but it can't use (or work with) threads, either. (Among other details, until 1003.1c-1995 [threads], POSIX overruled ANSI C and unconditionally required a single global errno variable; which makes simultaneous or even multiplexed thread execution, er, "interesting".) The only rational or reasonable way to label code "correct" that uses POSIX thread interfaces (e.g., mutexes), and does not address cancellation, is if that code was designed exclusively for a SPECIALIZED (non-general) purpose where it could be known that it would run only in threads that the application WILL NOT cancel. There is no such thing as a CORRECT, general purpose, "POSIX" library recognizing threads that doesn't address cancellation. Period. If the code in question CORRECTLY uses non-cancel-safe threading, then it will continue to be safe no matter what choices C++ makes for cancellation support... because threads running the code will never be cancelled. If this is NOT true, then it was never correct in the first place. Again, as I've said, if you're talking about non-POSIX threaded code, the story may be different. Yes, Win32 doesn't have cancellation. Solaris "UI Threads" (deprecated since Solaris 2.5, which is a long way back) didn't have cancellation. There are undoubtedly embedded system threading systems that don't have cancellation. This goes back to my question "what are the real goals here"? Doing cancellation as error returns instead of exceptions doesn't make anything cancel-safe, either; not unless you analyze every place that calls a cancellable function to be sure it's doing something reasonable with this new return value that means something critically different from other errors. Many presume that the distinction between errors isn't really important, because they all simply mean "it didn't work". Many more simply ignore the error. Even those that try to "decode" the error and do something reasonable don't pass on the same error code to its own caller, for all sorts of reasons. Error returns aren't modular; particularly in complicated layered systems. Exceptions are. That's why exceptions are better, and why cancellation was always intended to be modelled as an exception. >>>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. >> >> >No. The code was written to a documented interface. Whoever changes >the interface semantics without changing the interface name is >responsible for corrupting the program state. > > But the code is NOT written to the documented interface unambiguously applicable to ALL POSIX code that uses threads. If it doesn't use threads, there's no problem -- either in POSIX or in this hypothetical "threaded C++". The only complication is if it was written for some OTHER threading package without cancellation, and if it's really more critical to the C++ goals to protect it from cancellation than to allow the rest of the application to depend on timely and safe cancellation. (And if so, I'd suggest the best way to be safe is to simply omit any support at all for cancellation. ) Once more, as explanation and apology, I wrote my original reply on this list blindly believing the list's name: "c++-pthreads". Not "C++-threads". "pthreads" means "POSIX threads", and "POSIX threads" means modular cancellation cleanup deliberately modelled on exceptions. I ought to have already known from previous discussions that the list name was inaccurate. The question is... how inaccurate, and in what way? If cancellation is important, then you'll be compatible with all CORRECT general POSIX threaded code by closely following the POSIX cancellation semantics. Nothing will magically make "all arbitrary threading package code, or incorrect POSIX threading code" cancel-safe, so I personally think it's a waste of time to worry about that too much. Either way, someone's going to need to analyze the library code and make sure it's doing the right thing when it's cancelled. It's easier to do that with isolated modular cleanup mechanisms like cleanup handlers and destructors than to follow the often twisty passages by which typical code deals with and propagates error codes (if it does at all). >>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. >> >> >Sorry, that's simply disingenuous. To argue that everybody should have >coded to an interface that you only just got around to documenting, >implementing, and deploying, many years after the code was written, >borders on arrogant contempt. > >Such an attitude may be fine for the POSIX C committee, but I see no >reason to match it here. In any case, we have a great deal more >already-thread-safe code to preserve, because thread-safety (by the >common definition) is the norm, in C++. > > Again, either the code is "POSIX threads" or not. If it is, then either it deals with cancellation, knows a-priori that the application will never CHOOSE to cancel the thread that runs it, or it's broken. There are no other options. (Yes, broken code is common. Some code broken in this particular way may have no other problems, though it's not generally the way I'd prefer to bet. Designing a solution around support for broken code, though, without any guarantee that it'll fare any better under the less-modular alternative, doesn't seem to me the best way to start.) If it's NOT "POSIX threads", then it probably has no concept of cancellation in the first place, and cannot be made magically safe for cancellation by any reasonable set of definitions. If it's C then it PROBABLY doesn't handle hierarchical cleanup. But if it's C++ (and at least THAT part of the list's name is presumably accurate), then it's probably as likely to do the right cleanup when an arbitrary new exception propagates as any C or C++ code is to do the right thing on an arbitrary new error status. (Which may well be just a way of saying that neither will work very often; but the real point is that I believe using error returns will make things worse, not better.) >>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. >> >> >Again, that reasoning may be fine for C (did you really ask all those >C programmers?), but we need not be bound by it here. > >Since a cancellation error return swallowed up in library code must >surface again at the next cancellation point, eventually (given a >well-written library) the failure must propagate upward to the point >where it may be turned into an exception. (A library that never >propagates system-call failures to its caller isn't anything-safe, >and needn't concern us.) > > I really don't understand how this is supposed to work. "Pending cancel" remains until the thread terminates. There was some mention somewhere that implied it would be cleared on delivery but "re-set" when the exception object was destroyed -- but I don't see how you'd manage that in any model where the "real" cancellation is a status return that MIGHT sometime later be "converted" to an exception. That means every cancellable call made during cleanup will fail with your ECANCELLED. Which means most things simply cannot be cleaned up. OK, so maybe you defer all attempts to clean up until the exception is first raised, at which point you "unpend" the cancel until and unless the exception object is destroyed. That still breaks your example if the clean_up() routine makes any cancellable calls. (So much for preserving the old code.) Yet if returning the first ECANCEL "unpends", you'll lose the cancel if the code instead does something like: if (result < 0) { clean_up(s, result); return EINVAL; } ... and I thought the whole point of the "sticky cancel" was precisely to avoid that risk? I haven't seen anything so far that seems to offer a way out of this maze of messes. And that's precisely why we made cancellation an exception in the first place. >>>(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. >> >> >No. I'm talking about the many millions of lines of existing >thread-safe library code. Ordinary thread-safety is the norm in C++ >libraries, because it's the natural way to code, in C++. > > "Natural"? No, it's not, unless you presume that all objects and static data are private. And you know that's not true. C++ libraries, like STL, have gone through enormous pains to try to be "basically thread-safe", and there are still conceptual problems, particularly in areas like the interdependencies around iostreams. It's not easy to get any complicated system "right". Threading adds complication. Cancellation and/or exceptions adds complication. Nobody's saying it doesn't. But to be GENERAL PURPOSE C++ code, you need to be exception-safe; and to be GENERAL PURPOSE POSIX thread code, you need to be cancel-safe. To be GENERAL PURPOSE POSIX C++ thread code, you need to be both. And since there's not much difference between POSIX cancel-safe and C++ exception-safe, the combination shouldn't be so difficult, technically. The problems, as always, are more political. What SORTS of changes are required of existing code? (You may like to think it's "WHETHER" changes are required -- but you'll never convince me there's any way to avoid requiring change or at least careful analysis of every affected code path.) WHICH existing code patterns to prefer over others? (And much of this pivots on which patterns each person thinks are common or important, and that's a matter of seriously subjective opinion.) Whether it's OK to lose a cancel? What level of reliability and latency a cancellable application ought to be able to expect? >>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. >> >> >Exception-safety depends on identifying and guarding against documented >sources of possible exceptions. System calls and C library functions >are not among those. Also, C++ libraries very frequently rely on >underlying C libraries, and are written to depend on their documented >behavior. (None of my man(2) or man(3) pages mention unwinding, never >mind throwing.) > > None mention ECANCELLED, either. So if we can't add an undocumented throw, how can we add an undocumented error code? While you might like to think that "just adding a new error code" is "nearly transparent" to "some existing code", you're correct only in some narrow boundary of "nearly" and "some". There'll inevitably be enormous volumes of code that DOESN'T fit that preconceived pattern, and I'm quite sure we've all seen plenty of examples. The only thing that's FREE is NOTHING. So either we do nothing, or we accept a cost. I don't see a whole lot of argument for simply ignoring cancellation entirely, which is nothing, and free. So you simply can't argue against some strategy because there's a cost. There's always a cost. The interesting questions revolve around how much cost is acceptable, and the benefits of each strategy relative to its cost. Currently, ANSI C++ doesn't "support" threads at all. So any use of threads is beyond the boundaries of the standard, and therefore nonportable and subject to the whims of various implementations. On Tru64 UNIX and OpenVMS, cancellation is and always has been an exception. In POSIX cancellation has always worked exactly like a special exception (within the constraints of POSIX and ANSI C, of course, which doesn't allow actually using the word "exception" except in non-binding explanations). Nowhere has it ever been represented as a special error return from general pre-existing system functions. So which is more compatible, and with what? >Even if you claim that the threat of "unwinding" from system calls is >ancient, and that everything should have been written to assume it, >a change to make them throw would be completely new. > > Replace "unwinding" and "throw" by "cancel", or "threads", and the statement still stands. If you've already decided to add threads and cancel, I don't see how this argument is relevant. Yes, things will change. Old assumptions will be broken. Code will need to adapt. If you really CAN change the world so fundamentally without changing the code that runs in it, that'd be great; but I don't believe it. >>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! >> >> >No offense intended, but disingenuity makes a poor substitute for >responsible design. > You clearly like the word "disingenuous". You've used it several times. That's at best a subjective slur that doesn't belong in this discussion. I may disagree with you, but I'm trying hard not to accuse you of being deliberately "not-straightforward", despite the enormous temptation of turning the accusation back at you. Or I could be disingenuous and simply say that I agree completely with your statement... but why should I take offense when it clearly has nothing at all to do with me? >We can afford to be more responsible here, because we have stronger language semantics to work with, and well-worked-out exception-safety standards. > > Yes, let's be responsible. Perhaps we should first define precisely what "responsible" is intended to mean in this context, before we start arguing over which low-level details or discussions might fall under that banner. And I'm not being disingenuous here -- I absolutely mean it. Perhaps the C++ committee people already know exactly the full range of constraints and requirements on this effort, but I, and presumably others involved in this wider discussion group, cannot. If those constraints and requirements aren't to be explicitly and fully shared with us, then the discussion never should have been opened up in the first place... and I might as well just go away. -- /--------------------[ 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 ]---/ From gdr at integrable-solutions.net Wed Jan 7 16:12:43 2004 From: gdr at integrable-solutions.net (Gabriel Dos Reis) Date: 07 Jan 2004 17:12:43 +0100 Subject: [c++-pthreads] concrete library-code example In-Reply-To: <3FFC28B2.707@hp.com> References: <3FE70542.5060104@bourguet.org> <1072111706.3474.17.camel@minax.codesourcery.com> <20031222215712.GB1528@diablo.name> <1072131497.7562.22.camel@doubledemon.codesourcery.com> <20031223160314.GA9780@diablo.name> <20031224170517.GG30780@tofu.dreamhost.com> <3FF9977C.7040003@hp.com> <20040107071750.GP32272@tofu.dreamhost.com> <3FFC28B2.707@hp.com> Message-ID: Dave Butenhof writes: | mean it. Perhaps the C++ committee people already know exactly the | full range of constraints and requirements on this effort, but I, and | presumably others involved in this wider discussion group, cannot. If | those constraints and requirements aren't to be explicitly and fully | shared with us, then the discussion never should have been opened up | in the first place... and I might as well just go away. Well, I would not say that the C++ committee people already know exactly the full range of constraints and requirements. I believe some people have firm opinions on what they would like to have, but those vary from individuals to individual -- you most certainly saw disagreements between C++ committee members on this list. At any rate, I've found your contributions to this discussion highly instructive. -- Gaby From David.Butenhof at hp.com Wed Jan 7 16:44:46 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Wed, 07 Jan 2004 11:44:46 -0500 Subject: [c++-pthreads] concrete library-code example In-Reply-To: References: <3FE70542.5060104@bourguet.org> <1072111706.3474.17.camel@minax.codesourcery.com> <20031222215712.GB1528@diablo.name> <1072131497.7562.22.camel@doubledemon.codesourcery.com> <20031223160314.GA9780@diablo.name> <20031224170517.GG30780@tofu.dreamhost.com> <3FF9977C.7040003@hp.com> <20040107071750.GP32272@tofu.dreamhost.com> <3FFC28B2.707@hp.com> Message-ID: <3FFC377E.60800@hp.com> Gabriel Dos Reis wrote: >Dave Butenhof writes: > >| mean it. Perhaps the C++ committee people already know exactly the >| full range of constraints and requirements on this effort, but I, and >| presumably others involved in this wider discussion group, cannot. If >| those constraints and requirements aren't to be explicitly and fully >| shared with us, then the discussion never should have been opened up >| in the first place... and I might as well just go away. > >Well, I would not say that the C++ committee people already know >exactly the full range of constraints and requirements. I believe >some people have firm opinions on what they would like to have, but >those vary from individuals to individual -- you most certainly saw >disagreements between C++ committee members on this list. > > Well, perhaps there might have been just a tiny element of disingenuity in my paragraph; though I'd prefer to call it "tact" in this instance, that distinction may not really be justifiable. I really do think that IF there are any predefined requirements and constraints, either they need to be explicitly layed out for us "outsiders", or they need to be set aside entirely for these discussions, because we can't be expected to know them. On the other hand, I had already assumed everything you've stated above, and to some extent I suppose I was just teasing Nathan. I really don't like being called, even by implication, "irresponsible", (or "disingenuous") simply on the basis that I happen to disagree with one individual's opinion. For giving into that base temptation at retribution, I must sincerely apologize to all. Even to Nathan. ;-) >At any rate, I've found your contributions to this discussion highly >instructive. > > Thank you. -- /--------------------[ 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 ]---/ From austern at apple.com Wed Jan 7 17:11:36 2004 From: austern at apple.com (Matt Austern) Date: Wed, 7 Jan 2004 09:11:36 -0800 Subject: [c++-pthreads] concrete library-code example In-Reply-To: <3FFC377E.60800@hp.com> References: <3FE70542.5060104@bourguet.org> <1072111706.3474.17.camel@minax.codesourcery.com> <20031222215712.GB1528@diablo.name> <1072131497.7562.22.camel@doubledemon.codesourcery.com> <20031223160314.GA9780@diablo.name> <20031224170517.GG30780@tofu.dreamhost.com> <3FF9977C.7040003@hp.com> <20040107071750.GP32272@tofu.dreamhost.com> <3FFC28B2.707@hp.com> <3FFC377E.60800@hp.com> Message-ID: <8CC625F5-4134-11D8-8CAE-000393B2ABA2@apple.com> On Jan 7, 2004, at 8:44 AM, Dave Butenhof wrote: > Gabriel Dos Reis wrote: > >> Dave Butenhof writes: >> >> | mean it. Perhaps the C++ committee people already know exactly the >> | full range of constraints and requirements on this effort, but I, >> and >> | presumably others involved in this wider discussion group, cannot. >> If >> | those constraints and requirements aren't to be explicitly and fully >> | shared with us, then the discussion never should have been opened up >> | in the first place... and I might as well just go away. >> >> Well, I would not say that the C++ committee people already know >> exactly the full range of constraints and requirements. I believe >> some people have firm opinions on what they would like to have, but >> those vary from individuals to individual -- you most certainly saw >> disagreements between C++ committee members on this list. >> > Well, perhaps there might have been just a tiny element of > disingenuity in my paragraph; though I'd prefer to call it "tact" in > this instance, that distinction may not really be justifiable. I > really do think that IF there are any predefined requirements and > constraints, either they need to be explicitly layed out for us > "outsiders", or they need to be set aside entirely for these > discussions, because we can't be expected to know them. To some extent I don't think anyone knows them. This is a group that consists of people from a lot of different places; some of the people on this list, by no means all, attend C++ committee meetings. The origin of this discussion was on the GCC mailing list, when people were trying to figure out how gcc, glibc, and libstdc++ should handle the intersection of C++ and pthreads. People in that discussion realized that this was a discussion that extended beyond the GNU community and that a solution adopted by gcc/linux wouldn't be nearly as interesting as a solution that was generally recognized as right. In the end, "generally recognized as right" probably means getting approval from the C++ committee and/or The Austin Group. But that's for another day. My goals are (I believe) very similar to yours: figure out what the POSIX C binding should mean for C++. This might mean something as ambitious as coming up with a separate C++ binding, or it might mean making some minor tweaks and clarifications in the existing C binding and/or the existing C++ language specification. In principle I'm agnostic between the two. In practice I suspect we don't have the resources or the vendor buy-in to do anything extremely ambitious. --Matt From David.Butenhof at hp.com Wed Jan 7 18:10:04 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Wed, 07 Jan 2004 13:10:04 -0500 Subject: [c++-pthreads] concrete library-code example In-Reply-To: <8CC625F5-4134-11D8-8CAE-000393B2ABA2@apple.com> References: <3FE70542.5060104@bourguet.org> <1072111706.3474.17.camel@minax.codesourcery.com> <20031222215712.GB1528@diablo.name> <1072131497.7562.22.camel@doubledemon.codesourcery.com> <20031223160314.GA9780@diablo.name> <20031224170517.GG30780@tofu.dreamhost.com> <3FF9977C.7040003@hp.com> <20040107071750.GP32272@tofu.dreamhost.com> <3FFC28B2.707@hp.com> <3FFC377E.60800@hp.com> <8CC625F5-4134-11D8-8CAE-000393B2ABA2@apple.com> Message-ID: <3FFC4B7C.9070507@hp.com> Matt Austern wrote: > On Jan 7, 2004, at 8:44 AM, Dave Butenhof wrote: > >> Gabriel Dos Reis wrote: >> >>> Dave Butenhof writes: >>> >>> | mean it. Perhaps the C++ committee people already know exactly the >>> | full range of constraints and requirements on this effort, but I, and >>> | presumably others involved in this wider discussion group, cannot. If >>> | those constraints and requirements aren't to be explicitly and fully >>> | shared with us, then the discussion never should have been opened up >>> | in the first place... and I might as well just go away. >>> >>> Well, I would not say that the C++ committee people already know >>> exactly the full range of constraints and requirements. I believe >>> some people have firm opinions on what they would like to have, but >>> those vary from individuals to individual -- you most certainly saw >>> disagreements between C++ committee members on this list. >> >> Well, perhaps there might have been just a tiny element of >> disingenuity in my paragraph; though I'd prefer to call it "tact" in >> this instance, that distinction may not really be justifiable. I >> really do think that IF there are any predefined requirements and >> constraints, either they need to be explicitly layed out for us >> "outsiders", or they need to be set aside entirely for these >> discussions, because we can't be expected to know them. > > To some extent I don't think anyone knows them. This is a group that > consists of people from a lot of different places; some of the people > on this list, by no means all, attend C++ committee meetings. About what I'd expected. > The origin of this discussion was on the GCC mailing list, when people > were trying to figure out how gcc, glibc, and libstdc++ should handle > the intersection of C++ and pthreads. People in that discussion > realized that this was a discussion that extended beyond the GNU > community and that a solution adopted by gcc/linux wouldn't be nearly > as interesting as a solution that was generally recognized as right. That's encouraging. I guess it also explains the group name, since "gcc/linux" would likely be focused on C++ and POSIX threads, regardless of any skew between that and the long term goals of the C++ committee. So... "Ah". > In the end, "generally recognized as right" probably means getting > approval from the C++ committee and/or The Austin Group. But that's > for another day. > > My goals are (I believe) very similar to yours: figure out what the > POSIX C binding should mean for C++. This might mean something as > ambitious as coming up with a separate C++ binding, or it might mean > making some minor tweaks and clarifications in the existing C binding > and/or the existing C++ language specification. In principle I'm > agnostic between the two. In practice I suspect we don't have the > resources or the vendor buy-in to do anything extremely ambitious. I would like to think that it means a unique C++ binding, because this would present far too many unique and useful opportunities to ignore. However I also think there ought to be strong compatibility, at least "philosophically" with POSIX; especially in areas like cancellation where there's substantial POSIX existing practice and virtually no existing practice outside POSIX. And most especially considering that the kernel and C runtime will need to coexist with both models. While some changes to the existing C language POSIX specification might be necessary, inevitable, or even desirable, the sort of fundamental change in cancellation model some have proposed here is a non-starter. C++ may be worried about trying to support non-POSIX threaded code (though I've expressed several doubts about precisely what that's supposed to mean and whether it's relevant), but keep in mind that such a fundamental change in the existing POSIX specification would break ALL (correct) existing general-purpose POSIX-based threaded libraries, and many existing applications. To say such a change would be "contentious" would be an absurd understatement. In fact, I would argue that for the Austin group to accept such a change would be truly irresponsible. And also that for any OS to continue to support POSIX threads while also supporting a radically distinct C++ threading model would be onerous and impractical. None of this need necessarily serve as a constraint on the C++ committee. But then, it doesn't hurt to think about things like "vendor buy-in", and that's a lot easier when you're not making radical and contradictory requirements. Of course that's a simplification, because some of the C++ rules and idiom around exceptions presents a different sort of contradiction with "POSIX compatible" model, and that's not trivial to resolve either. I don't mean to discount that, but obviously my focus is more on POSIX than on C++, and I can only contribute what I have to contribute. -- /--------------------[ 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 ]---/ From baker at cs.fsu.edu Wed Jan 7 19:32:23 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Wed, 7 Jan 2004 14:32:23 -0500 Subject: [c++-pthreads] What are the real issues? In-Reply-To: <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> Message-ID: <20040107193223.GA2884@diablo.name> > The Itanium C++ ABI, which gcc adopted, made cancellation a special > kind of exception, "forced unwinding", so that a thread can't just > catch the cancellation exception and swallow it. The idea was that a This "forced unwinding" sounds different from normal exception processing, since you cannot catch the exception. How deeply is this embedded in the gcc C++ implementation? e.g., is the unwinding done by different code (maybe even by a proxy thread?) for cancellation than for normal exception propagation? For example, with GNAT, the task abort exception is processed exactly like other exceptions. The difference is pretty superficial in that tha parser does not allow handlers for abort in user code. This is easily overridden (a GNAT-specific language extension) when one is writing system support code, so it is not a problem for critical libraries. > thread may disable cancellation tempoarily, but that once a > cancellation has begun it can't be thrown away. This was a slightly > controversial decision at the time, and maybe it's at the heart of the > real issue. > Second: the immediate issue was that some people saw a contradiction > between this model and the C++ library specification. For example, > POSIX says that read() is a cancellation point. But it's reasonable to > assume that std::istream:;read() invokes POSIX's read() system call, > and the C++ standard says that (under ordinary circumstances) > std::istream doesn't throw. If the cancellation exception were an > ordinary exception instead of special forced unwinding, what would > happen would be that istream would invoke its streambuf, the streambuf > would call read(), read would throw a cancellation exception, then > istream would catch the exception, swallow it, and set and error flag. > Forced unwinding means that the cancellation exception will propagate > even though istream tries to swallow it. This means that an exception > will propagate from istream when the C++ standard says it's not > supposed to. See question above. Is this normal propagation? If so, then shouldn't it be possible to catch it? > Third, some people simply object to having cancellation be an exception > (or anything that looks like an exception): it means there are some > functions that can throw exceptions in the presence of threads that > wouldn't throw in a single-threaded program. There seems to be no way around this, short of requiring that cancellation be disabled over all suspect library calls. As Dave explained so well, if a system supports thread cancellation it can happen and it behaves virtually like an exception, regardless of what you call it. To ask the pthread library implementors to change this would be a fundamental change to the pthread semantics. > cancellation that's very different from the POSIX. Anything that > doesn't involve thread execution stopping sounds more like a > communication mechanism than a cancellation mechanism. Seems to me > that the only real issues for debate are which functions are > cancellation points, how a thread can enable and disable cancellation, > whether a thread should be allowed to disregard a cancellation request > once it has been received, and what kind of cleanup a thread performs > before it stops. POSIX and the Single Unix Specification cover these. In the SUS, much of the relevant text is in the Base Definitions part, which are intended to be language-independent. ---> whether a thread should be allowed to disregard a cancellation request once it has been received SUS'98, which subsumes the POSIX standards, says: "Whenever a thread has cancelability enabled and a cancellation request has been made with that thread as the target, and the thread then calls any function that is a cancellation point (such as pthread_testcancel() or read()), the cancellation request shall be acted upon before the function returns." [The "shall" means cancellation cannot be ignored at this point.] "... If cancelability is disabled ... all cancellation requests are held pending...". [So, cancellation must be ignored at this point.] This seems to be language-independent semantics. ---> how a thread can enable and disable cancellation The C API defines pthread_setcanceltype() and pthread_setcancelstate(). A C++ API could define additional functions or points at which the above functions are implicitly (virtually) called. ---> what kind of cleanup a thread performs before it stops: SUS'98 says: "When a cancellation request is acted upon, the routines in the list are invoked one by one in LIFO sequence; that is, the last routine pushed onto the list (Last In) is the first to be invoked (First Out). The thread invokes the cancellation cleanup handler with cancellation disabled until the last cancellation cleanup handler returns. When the cancellation cleanup handler for a scope is invoked, the storage for that scope remains valid. If the last cancellation cleanup handler returns, thread execution is terminated and a status of PTHREAD_CANCELED is made available to any threads joining with the target. The symbolic constant PTHREAD_CANCELED expands to a constant expression of type ( void *) whose value matches no pointer to an object in memory nor the value NULL. "The cancellation cleanup handlers are also invoked when the thread calls pthread_exit()." "...When the cancellation is acted on, the cancellation cleanup handlers for thread shall be called. When the last cancellation cleanup handler returns, the thread-specific data destructor functions shall be called for thread. When the last destructor function returns, thread shall be terminated. ..." ---> which functions are cancellation points: SUS'98 (and POSIX) define a raft of functions for which cancellation points "shall" occur, and some more for which cancellation points "may" occur. A C++ binding could define other names for functions with similar effects that are shall *not* be cancellation points. > My feeling: it's just plain inevitable that a multithreaded program has > more functions that might throw than a single-thread program. Dealing > with this is part of what it means to make a program thread-safe. We > might argue that POSIX defines too many cancellation points, and that > cutting it down to a smaller number (and, especially, getting rid of > the functions that POSIX says may or may not be cancellation points) > would make it easier to write correct code. Yes. > But as I said, I think the really fundamental issue is whether a thread > should be allowed to receive a cancellation request, start to do some > work as a result of the request, and then decide that it doesn't want > to be cancelled. If we think that's reasonable then I think what we POSIX and the SUS do not define this. I believe it would be an upward-compatible extension to define it. That is, no existing UNIX/POSIX conformant application could be broken by an extension that extends the API to allow a thread to regain control and "revoke" a cancellation, since no conformant application could presently include such an API call However, the debate will be over one *wants* to export such functionality. > should probably do is abandon the notion of forced unwinding and make > cancellation into an ordinary exception. If we do that then the > std::istream contradiction goes away. The cost, of course, is that it > might become surprisingly hard to cancel some threads. From what I've read here, and my own experience, it seems this is the right thing to do, provided: 1) The only way to handle this new cancellation exception is via a new syntax. 2) You can agree on new C++ API function names for whichever POSIX/Unix cancellation-point system calls you don't think should ever propagate a cancellation exception. To do the latter, you have several choices: (a) Have the wrapper (e.g., for read()) just disable cancellation (and not reenable it). (b) Work with your friendly pthread implementor (the one who manages the code of the library functions that are cancellation points) to develop alternate entry points (e.g., read_nocancel()) or a settable flag (e.g., that a C++ thread or process can call to make read() *not* serve as a cancellation point) for all the cancellation-point system calls that you don't think should really be cancellation points. --Ted From baker at cs.fsu.edu Wed Jan 7 21:26:04 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Wed, 7 Jan 2004 16:26:04 -0500 Subject: [c++-pthreads] What are the real issues? In-Reply-To: <20040106211236.GX30780@tofu.dreamhost.com> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <20040106211236.GX30780@tofu.dreamhost.com> Message-ID: <20040107212604.GB2884@diablo.name> > (3) how to preserve the frankly enormous body of thread-safe code > already written, deployed, and running for years. Just be be clear. You mean by "thread-safe" that the code uses mutexes to protect access to shared data, but that it does not use cancellation state management and cleanup handlers to protect against thread cancellation.... right? If so, the only way out seems to be to require that for C++ programs the initial cancellation state of every thread is *disabled*. If you do that, then no thread can be cancelled unless it contains a call to explicitly enable cancellation. Any application that explicitly enables cancellation either: (1) has not "been running for years" and so can be expected to take steps to deal correctly with the potential cancellation exception; (2) is an old application that already is cancellation-safe using cancellation-cleanup handlers (which should be executed transparently if the exception is not handled); (3) was never thread-safe, and will now get the same behavior as before (i.e., the exception will not be handled). > ...have had thread-safe libraries for a long time. Under one proposed > model most can simply be declared already "cancellation-safe", without > changes. You mean the model where all system call return -1 with errno==ECANCELLED? Is it safe to assume these libraries are not making any assumptions about the possible range of causes for the -1 return? (Maybe you could point me to two or three examples of such libraries, so that can look at how system calls are implemented?) --Ted From baker at cs.fsu.edu Wed Jan 7 21:33:46 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Wed, 7 Jan 2004 16:33:46 -0500 Subject: [c++-pthreads] What are the real issues? In-Reply-To: References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <20040106211236.GX30780@tofu.dreamhost.com> <64F121A7-408F-11D8-B918-00039390D9E0@apple.com> Message-ID: <20040107213346.GC2884@diablo.name> > In a catch block handling a cancellation exception, functions > identified as cancellation points work normally. Do you mean to imply that the implementation of each of these functions looks to see whether the call is coming from inside a catch block? (It seems you are asking the behavior of these calls to vary, depending on the context of the call, working normally in a catch block but failing outside.) > Eventually the inability of the thread to achieve anything leads it > to top-level code equipped to recognize the cancelled condition as > such, and to clean up and die in a controlled way. This assumes the code has been structured with cancellation in mind, so that control passes upward in the way you expect, or that you are lucky. --Ted From baker at cs.fsu.edu Wed Jan 7 21:47:31 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Wed, 7 Jan 2004 16:47:31 -0500 Subject: [c++-pthreads] What are the real issues? In-Reply-To: <20040106223316.GY30780@tofu.dreamhost.com> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <20040106211236.GX30780@tofu.dreamhost.com> <64F121A7-408F-11D8-B918-00039390D9E0@apple.com> <20040106223316.GY30780@tofu.dreamhost.com> Message-ID: <20040107214731.GD2884@diablo.name> > No, the thread remains cancelled. The difference between that and > the thread actually discarding the cancellation request is that the > next time a function identified as a cancellation point is called, it > fails, too. The cancellation is not lost, or discarded, or ignored; > it surfaces again and again, indefinitely, as long as the thread fails > to terminate. Furthermore, a subsequent (e.g.) filebuf operation > would actually throw. If that exception were caught and discarded, > the next would throw again. Somebody described this as "sticky > cancellation". > Under POSIX C, a thread cancellation handler can continue to run > indefinitely, so you're not describing something fundamentally different > from the C model. Likewise, if unwinding were to run catch clauses, > code in a catch clause could continue to run indefinitely. The situation is not exactly the same: You are asking, at least, for some way to poll whether the thread has been cancelled, so that your subsequent cancellation-point operations will know to "fail". You will also need to modify the semantics of cancellation cleanup handlers, so that they are executed on the way out as you exit their scopes (possibly via normal transfers of control). --Ted From fjh at cs.mu.oz.au Thu Jan 8 03:52:42 2004 From: fjh at cs.mu.oz.au (Fergus Henderson) Date: Thu, 8 Jan 2004 14:52:42 +1100 Subject: [c++-pthreads] What are the real issues? In-Reply-To: <20040107193223.GA2884@diablo.name> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <20040107193223.GA2884@diablo.name> Message-ID: <20040108035242.GA9432@jupiter.cs.mu.oz.au> On 07-Jan-2004, Ted Baker wrote: > [someone, I think it was Richard Henderson, wrote:] > > The Itanium C++ ABI, which gcc adopted, made cancellation a special > > kind of exception, "forced unwinding", so that a thread can't just > > catch the cancellation exception and swallow it. The idea was that a > > This "forced unwinding" sounds different from normal exception > processing, since you cannot catch the exception. How deeply is > this embedded in the gcc C++ implementation? As Richard Henderson explained, this uncatchable forced unwinding (for longjmp_unwind() and thread cancellation) was originally proposed and for a brief period was implemented in GCC, but was then removed. In the current GCC implementation, if I understand things correctly, all exceptions are catchable via the C++ catch-elipsis construct "catch (...)". The idea of uncatchable exceptions was dropped because it conflicted with standard C++ semantics which say that any exception can be caught. Some existing C++ code uses this for cleanup handlers, e.g. acquire_resource() try { use_resource(); } catch(...) { release_resource(); throw; /* rethrow the same exception */ } release_resource(); and the people who argued that code like this was important and should be preserved won out over the people who argued that longjmp_unwind() and thread cancellation should be uncatchable. > For example, with GNAT, the task abort exception is processed > exactly like other exceptions. The difference is pretty > superficial in that tha parser does not allow handlers for abort > in user code. If my understanding of GCC is correct, the current situation with GCC is the same. > From what I've read here, and my own experience, it seems this is > the right thing to do, provided: > > 1) The only way to handle this new cancellation exception is via a > new syntax. Unfortunately that would conflict with standard C++ semantics, which say that any exception can be caught and handled using the catch-elipsis construct "catch (...)". -- Fergus Henderson | "I have always known that the pursuit The University of Melbourne | of excellence is a lethal habit" WWW: | -- the last words of T. S. Garp. From fjh at cs.mu.oz.au Thu Jan 8 04:01:12 2004 From: fjh at cs.mu.oz.au (Fergus Henderson) Date: Thu, 8 Jan 2004 15:01:12 +1100 Subject: [c++-pthreads] Re: cancellation points report failure In-Reply-To: <3FFBF1D6.56ABAC@terekhov.de> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <20040107052644.GO32272@tofu.dreamhost.com> <3FFBF1D6.56ABAC@terekhov.de> Message-ID: <20040108040112.GB9432@jupiter.cs.mu.oz.au> On 07-Jan-2004, Alexander Terekhov wrote: > Nathan Myers wrote: > > > > On Tue, Jan 06, 2004 at 06:58:57AM -0500, Dave Butenhof wrote: > > > Personally, I would be happy to accept a C++ binding with no way to > > > enable asynchronous cancelability, and to avoid defining any C++ code > > > as "async cancel-safe". > > > > I think we're all agreed on that. This list is about semantics of > > a C++ binding for synchronous cancellation. > > Objection. To begin with, asynchronous cancelability IS part of > pthreads. Asynchronous cancelability only works if you can be sure that there are no implicit calls to async-cancel-unsafe functions going on. That's not too hard to ensure in C, but it's a lot more difficult in C++. -- Fergus Henderson | "I have always known that the pursuit The University of Melbourne | of excellence is a lethal habit" WWW: | -- the last words of T. S. Garp. From Mathieu.Lacage at sophia.inria.fr Thu Jan 8 10:00:51 2004 From: Mathieu.Lacage at sophia.inria.fr (Mathieu Lacage) Date: Thu, 08 Jan 2004 11:00:51 +0100 Subject: thread-safety definition Message-ID: <1073556051.28360.195.camel@felix.inria.fr> hi all, It looks like what everyone is trying to achieve here is a way for C++ authors to write thread-safe libraries. The major problem which seems to plague this discussion is that different people assume different definitions for thread-safety. Here is my take. Hopefully, without too many factual errors... Definition "thread safety" -------------------------- A thread-safe library is a library which uses locks to protect data which can be potentially accessed from different threads. This kind of code is typically written with a trivial small OS-abstraction layer which implements basic semaphore semantics (or another synchronization primitive). This kind of library was never written to deal with "cancelation" (not the POSIX cancelation primitive but, more generally, externally-triggered death of the thread) and is not safe with regard to either "inside" or any form of "outside" cancelation as defined below. Definition "cancelation" ------------------------ All OSes provide a mean to stop the execution flow of a thread either from the inside of the thread or from the outside. 1) "inside cancelation": This is basically ExitThread (win32 name). It exists on all the platforms which support a form of threads or another I know of. It semantics vary a lot from one platform to the other unfortunatly. On win32, it will not invoke any thread-specific cleanup handlers (neither C++ exceptions nor SEH are involved). On BeOS (exit_thread), it will behave just like on windows. On POSIX (pthread_exit) systems, it will invoke the thread-specific cancelation handlers. 2) "outside cancelation": There are two kinds of "outside cancelation": 2.1) "async cancelation": The OS removes the thread from its list of tasks to schedule and does nothing to cleanup the thread ressources. This is the most extreme useless feature of a thread library. BeOS and win32 provide it. POSIX does not provide it. 2.2) "defered cancelation": I know of only POSIX to implement this. The canceled flag for the target thread is set and the thread cancelation handlers are invoked whenever the thread reaches a cancelation point (that is, it calls one of a set of specific library functions). Definition "Posix thread-safety": --------------------------------- A library is "posix thread-safe" if it is thread-safe and defered-cancelation-safe. I hope the above definitions will help. regards, Mathieu -- Mathieu Lacage From David.Butenhof at hp.com Thu Jan 8 11:34:12 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Thu, 08 Jan 2004 06:34:12 -0500 Subject: [c++-pthreads] thread-safety definition In-Reply-To: <1073556051.28360.195.camel@felix.inria.fr> References: <1073556051.28360.195.camel@felix.inria.fr> Message-ID: <3FFD4034.7050903@hp.com> Mathieu Lacage wrote: >Definition "cancelation" >------------------------ >All OSes provide a mean to stop the execution flow of a thread either >from the inside of the thread or from the outside. > >1) "inside cancelation": This is basically ExitThread (win32 name). It >exists on all the platforms which support a form of threads or another I >know of. It semantics vary a lot from one platform to the other >unfortunatly. On win32, it will not invoke any thread-specific cleanup >handlers (neither C++ exceptions nor SEH are involved). On BeOS >(exit_thread), it will behave just like on windows. On POSIX >(pthread_exit) systems, it will invoke the thread-specific cancelation >handlers. > > The term "cancellation" seems heavy here. This is just a voluntary termination. But, yes, there are similar properties -- certainly from the point of view of the rest of the frames on the call stack at the time. >2) "outside cancelation": There are two kinds of "outside cancelation": > > 2.1) "async cancelation": The OS removes the thread from its list of >tasks to schedule and does nothing to cleanup the thread ressources. >This is the most extreme useless feature of a thread library. BeOS and >win32 provide it. POSIX does not provide it. > > I'd call this "abort", largely because there was at one time a proposal for a POSIX function to achieve this, which would have been called pthread_abort(). POSIX already defines "async cancel", as a mode where posting a cancel to a thread will cause the cancellation to be delivered at any arbitrary time supported by the OS and hardware. (Usually on the next clock tick, though that's a "common implementation" rather than any rule or even recommendation.) > 2.2) "defered cancelation": I know of only POSIX to implement this. The >canceled flag for the target thread is set and the thread cancelation >handlers are invoked whenever the thread reaches a cancelation point >(that is, it calls one of a set of specific library functions). > > "Cancellation" (both deferred and async) come from the Digital "CMA" architecture (where it was called "alert"). The CMA concept derives from a less structured (but fundamentally similar) capability in the SRC research labs' Topaz thread package. >Definition "Posix thread-safety": >--------------------------------- >A library is "posix thread-safe" if it is thread-safe and >defered-cancelation-safe. > > I wouldn't tack cancel-safety onto thread-safety so intimately, although I would agree that it's pointless to declare a general library function "thread safe under POSIX" unless it also supports deferred cancellation. (Async cancel is an oddity; there are, and should be, very few async-cancel-safe functions. Async-cancel regions of code cannot accomodate resource acquisition or release of any sort, as the recovery code is generally unable to determine the state of the resource.) Nevertheless, it's quite reasonable to write a "thread-safe" special purpose application routine that doesn't deal with cancellation simply because the designer KNOWS that a thread running that code cannot be cancelled. One might even make this choice within in a general purpose library in some cases -- say, for a daemon thread that could never run application code nor be identified to the application, and that therefore cannot be cancelled. -- /--------------------[ 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 ]---/ From David.Butenhof at hp.com Thu Jan 8 11:51:47 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Thu, 08 Jan 2004 06:51:47 -0500 Subject: [c++-pthreads] What are the real issues? In-Reply-To: <20040107193223.GA2884@diablo.name> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <20040107193223.GA2884@diablo.name> Message-ID: <3FFD4453.9010204@hp.com> Ted Baker wrote: >>But as I said, I think the really fundamental issue is whether a thread >>should be allowed to receive a cancellation request, start to do some >>work as a result of the request, and then decide that it doesn't want >>to be cancelled. If we think that's reasonable then I think what we >> >> >POSIX and the SUS do not define this. I believe it would be an >upward-compatible extension to define it. That is, no existing >UNIX/POSIX conformant application could be broken by an extension >that extends the API to allow a thread to regain control and >"revoke" a cancellation, since no conformant application could >presently include such an API call > > The POSIX cancellation cleanup mechanism was designed to be implemented easily on top of a native exception mechanism. With the full expectation that these exceptions could ALSO be handled (and finalized, if desired) by native exception syntax -- such as C++ catch, or the C SEH "except" extension. While such extensions are beyond the scope of POSIX, there's no contradiction. (Any application using this is at best "conforming with extensions" rather than "strictly conforming"; but that distinction is of little interest to most developers. Since no "strictly conforming" application can exercise these extensions, or be affected by them, the extension doesn't affect the conformance of the IMPLEMENTATION.) This was in fact a requirement placed on our initial DCE thread implementation by the OSF DCE team. They wanted to be able to write server management threads that would call the application's RPC server code and be able to trap APPLICATION-level cancellation of the server activity. Such a cancel (generally reflected from the client process) would terminate the APPLICATION server code exactly like a normal cancel (because it is), but wouldn't affect the "management infrastructure" because the routine that called the application server would have something like a FINALLY or CATCH_ALL clause (like a C++ "catch (...)"). There would have been other design alternatives, and there's certainly room to question whether this example validates the concept. Nevertheless, there are potential justifications for this sort of thing, and I've never seen any reason to disallow it. >However, the debate will be over one *wants* to export such >functionality. > > My take on that can probably be inferred from my comments above. The only reasonable implementation of cancellation is as an exception -- it was, after all, designed and intended to be implemented that way. Once that's done, I see no reason to artificially prevent specialize applications from handling it as any other exception. There are, however, certain arguments in favor of Ted's description of gnat behavior; that the code must do something special to declare that it really wants to handle this class of exception. The capability might be generally packaged as something like a subclass hierarchy designating "exceptions that should always terminate", which can only be finalized by naming the sub-hierarchy or some particular exception within (e.g., catch(...) might implicitly re-throw). -- /--------------------[ 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 ]---/ From boo at terekhov.de Thu Jan 8 12:05:54 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Thu, 08 Jan 2004 13:05:54 +0100 Subject: [c++-pthreads] Re: cancellation points report failure References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <20040107052644.GO32272@tofu.dreamhost.com> <3FFBF1D6.56ABAC@terekhov.de> <20040108040112.GB9432@jupiter.cs.mu.oz.au> Message-ID: <3FFD47A2.3147DE97@terekhov.de> Fergus Henderson wrote: [...] > > Objection. To begin with, asynchronous cancelability IS part of > > pthreads. > > Asynchronous cancelability only works if you can be sure that there > are no implicit calls to async-cancel-unsafe functions going on. Yep (and probably also C++ things like throw-expressions and "try {"). > That's not too hard to ensure in C, but it's a lot more difficult > in C++. And that's why my previous message was meant to illustrate what I'd call "async-cancel correctness" -- an attempt to have async- cancel-UNsafe operation inside async-cancel scope (async_cancel { /*...*/ }) shall be flagged as compile-time error. A function declared async_cancel_safe is async-cancel-safe (implies async- cancel-safety/correctness for its invocation [I mean "passing" parameter(s) and/or return value, if any] and its body). regards, alexander. From Mathieu.Lacage at sophia.inria.fr Thu Jan 8 13:07:41 2004 From: Mathieu.Lacage at sophia.inria.fr (Mathieu Lacage) Date: Thu, 08 Jan 2004 14:07:41 +0100 Subject: [c++-pthreads] thread-safety definition In-Reply-To: <3FFD4034.7050903@hp.com> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD4034.7050903@hp.com> Message-ID: <1073567261.28360.258.camel@felix.inria.fr> On Thu, 2004-01-08 at 12:34, Dave Butenhof wrote: > >1) "inside cancelation": This is basically ExitThread (win32 name). It > >exists on all the platforms which support a form of threads or another I > >know of. It semantics vary a lot from one platform to the other > >unfortunatly. On win32, it will not invoke any thread-specific cleanup > >handlers (neither C++ exceptions nor SEH are involved). On BeOS > >(exit_thread), it will behave just like on windows. On POSIX > >(pthread_exit) systems, it will invoke the thread-specific cancelation > >handlers. > > > > > The term "cancellation" seems heavy here. This is just a voluntary > termination. But, yes, there are similar properties -- certainly from > the point of view of the rest of the frames on the call stack at the time. Indeed. For a C++ POSIX binding, I would assume you might want to make such a function throw an exception caught by the thread-creation function to unwind properly the stack. Or is this some kind of wild stupid idea ? > >2) "outside cancelation": There are two kinds of "outside cancelation": > > > > 2.1) "async cancelation": The OS removes the thread from its list of > >tasks to schedule and does nothing to cleanup the thread ressources. > >This is the most extreme useless feature of a thread library. BeOS and > >win32 provide it. POSIX does not provide it. I should add: win32 (TerminateThread), BeOS (kill_thread). > POSIX already defines "async cancel", as a mode where posting a cancel > to a thread will cause the cancellation to be delivered at any arbitrary > time supported by the OS and hardware. (Usually on the next clock tick, > though that's a "common implementation" rather than any rule or even > recommendation.) OK. I guess this definition of "POSIX async cancel" was already explained on the list before but I missed it. I believe this POSIX async cancel is similar enough (at least, it feels as unsafe to use) to "abort" that we could count it in section 2.1. What do you think ? > "Cancellation" (both deferred and async) come from the Digital "CMA" > architecture (where it was called "alert"). The CMA concept derives from > a less structured (but fundamentally similar) capability in the SRC > research labs' Topaz thread package. Do you know of other widely used system-level APIs which provide similar features ? > >Definition "Posix thread-safety": > >--------------------------------- > >A library is "posix thread-safe" if it is thread-safe and > >defered-cancelation-safe. > > > I wouldn't tack cancel-safety onto thread-safety so intimately, although I used the POSIX name because I thought it was the only widely deployed system which provides this service. Maybe we should rename this to "strong thread-safety". Maybe "defered-cancel thread-safety" ? > (Async cancel is an oddity; there are, and should be, very few > async-cancel-safe functions. Async-cancel regions of code cannot > accomodate resource acquisition or release of any sort, as the recovery > code is generally unable to determine the state of the resource.) Yes. This is why I don't feel it's necessary to discuss it further since so little code will be concerned with it, we can altogether not deal with it for most C++ libraries. > Nevertheless, it's quite reasonable to write a "thread-safe" special > purpose application routine that doesn't deal with cancellation simply > because the designer KNOWS that a thread running that code cannot be > cancelled. One might even make this choice within in a general purpose > library in some cases -- say, for a daemon thread that could never run > application code nor be identified to the application, and that > therefore cannot be cancelled. Yes. Exactly. I have written a lot of code like that. The core C++ threaded code is hidden far away from the user which cannot therefore "posix-defer-cancel" it. It can't even ever get the C++ exceptions since they are catch (...) and transformed into C error codes. As a conclusion to these (tentative) definitions, I believe the purpose of this mailing list is to find a solution to develop "defered-cancel thread-safe" C++ libraries: simple "thread-safe" libraries do not require special attention. If everyone could agree to the statement above, it would probably make the discussion more productive: other threading models which do not support async cancelation are of no interest to the discussion and can be forgotten. If people agree on this statement, the only issue I can see which delimits the design space for the solution to this problem is whether or not you wish to allow the C++ library calling into C code (which uses pthreads) and/or allow C code to use the C++ library (which uses our C++ threading solution). Maybe it would help to consider the two cases separatly and try to figure out what requirements each case creates: 1) C++ library calls C++ code and is called by C++ code. 2) C++ library calls into C code. 3) C code calls C++ library. The hard part seems to be 2) and 3) where, if you use exceptions to propagate a cancel operation from either a cancelation point or a pthread_exit call, you need to correctly handle the registered cancelation handlers _and_ the C++ catch blocks in the right order. That seems pretty hard (ie: impossible) to me, being just a _user_ of thread libraries. If people are not interested in 2) and 3) and just want to design a solution for 1), then I think it will make the discussion more productive to acknowledge it. Mathieu -- Mathieu Lacage From boo at terekhov.de Thu Jan 8 13:28:54 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Thu, 08 Jan 2004 14:28:54 +0100 Subject: [c++-pthreads] thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> Message-ID: <3FFD5B16.46A628F@terekhov.de> Mathieu Lacage wrote: [...] > The hard part seems to be 2) and 3) where, if you use exceptions to > propagate a cancel operation from either a cancelation point or a > pthread_exit call, you need to correctly handle the registered > cancelation handlers _and_ the C++ catch blocks in the right order. That > seems pretty hard (ie: impossible) to me, being just a _user_ of thread > libraries. You might want to take a look at this thread: http://google.com/groups?th=f98e4fa7052aa25b (Subject: __attribute__((cleanup(function)) versus try/finally) regards, alexander. From jakub at redhat.com Thu Jan 8 13:48:01 2004 From: jakub at redhat.com (Jakub Jelinek) Date: Thu, 8 Jan 2004 08:48:01 -0500 Subject: [c++-pthreads] thread-safety definition In-Reply-To: <1073567261.28360.258.camel@felix.inria.fr> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> Message-ID: <20040108134801.GD24876@devserv.devel.redhat.com> On Thu, Jan 08, 2004 at 02:07:41PM +0100, Mathieu Lacage wrote: > Maybe it would help to consider the two cases separatly and try to > figure out what requirements each case creates: > 1) C++ library calls C++ code and is called by C++ code. > 2) C++ library calls into C code. > 3) C code calls C++ library. > > The hard part seems to be 2) and 3) where, if you use exceptions to > propagate a cancel operation from either a cancelation point or a > pthread_exit call, you need to correctly handle the registered > cancelation handlers _and_ the C++ catch blocks in the right order. That > seems pretty hard (ie: impossible) to me, being just a _user_ of thread > libraries. It is certainly not impossible, since e.g. NPTL implements it. It handles several styles of pthread_cleanup_{push,pop} (chained cleanup structures on the stack, setjmp buffers on the stuck, __attribute__((cleanup ()))) and C++ destructors. Jakub From David.Butenhof at hp.com Thu Jan 8 14:04:05 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Thu, 08 Jan 2004 09:04:05 -0500 Subject: [c++-pthreads] thread-safety definition In-Reply-To: <1073567261.28360.258.camel@felix.inria.fr> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> Message-ID: <3FFD6355.80200@hp.com> Mathieu Lacage wrote: >On Thu, 2004-01-08 at 12:34, Dave Butenhof wrote: > >>>1) "inside cancelation": This is basically ExitThread (win32 name). It >>>exists on all the platforms which support a form of threads or another I >>>know of. It semantics vary a lot from one platform to the other >>>unfortunatly. On win32, it will not invoke any thread-specific cleanup >>>handlers (neither C++ exceptions nor SEH are involved). On BeOS >>>(exit_thread), it will behave just like on windows. On POSIX >>>(pthread_exit) systems, it will invoke the thread-specific cancelation >>>handlers. >>> >>The term "cancellation" seems heavy here. This is just a voluntary >>termination. But, yes, there are similar properties -- certainly from >>the point of view of the rest of the frames on the call stack at the time. >> >> >Indeed. For a C++ POSIX binding, I would assume you might want to make >such a function throw an exception caught by the thread-creation >function to unwind properly the stack. Or is this some kind of wild >stupid idea? > > One example: on Tru64 UNIX and OpenVMS, pthread_exit() raises an exception, which is distinct from the exception provoked by pthread_cancel(), but with similar characteristics. Specifically, that an UNCAUGHT exception will terminate only the thread rather than the process (it's implicitly caught in the thread library's internal "thread base" routine), and that it's "generally improper" (though not impossible nor even illegal) for any other agency to finalize propagation of the exception. It's an exception for exactly the same reason as cancel: so that each active frame on the stack has the opportunity to perform appropriate cleanup of resources before termination. In the "pure POSIX model", without exceptions, both pthread_exit() and cancellation provoke sequential LIFO execution of a stack of "POSIX cleanup handlers" designated by the pthread_cleanup_push() operation. The intended implementation of pthread_cleanup_push() (and our actual implementation) is as a simple macro that initiates an exception scope, analogous to a C++ "try {". >>>2) "outside cancelation": There are two kinds of "outside cancelation": >>> >>> 2.1) "async cancelation": The OS removes the thread from its list of >>>tasks to schedule and does nothing to cleanup the thread ressources. >>>This is the most extreme useless feature of a thread library. BeOS and >>>win32 provide it. POSIX does not provide it. >>> >>> >I should add: win32 (TerminateThread), BeOS (kill_thread). > >>POSIX already defines "async cancel", as a mode where posting a cancel >>to a thread will cause the cancellation to be delivered at any arbitrary >>time supported by the OS and hardware. (Usually on the next clock tick, >>though that's a "common implementation" rather than any rule or even >>recommendation.) >> >> >OK. I guess this definition of "POSIX async cancel" was already >explained on the list before but I missed it. I believe this POSIX async >cancel is similar enough (at least, it feels as unsafe to use) to >"abort" that we could count it in section 2.1. What do you think ? > > No, not really. POSIX async cancel is still an exception, allowing hierarchical isolated cleanup of each active frame on the stack. It's just that, because of the resource ownership dilemma, there's no way to safely use async-cancel in "general code". It has to be restricted to areas of code that do not acquire or release resources, including any calls to external functions that might. Nevertheless, async cancel CAN be used safely if you're careful, without disrupting the operation of the process. This is not true of TerminateThread, or the hypothetical pthread_abort() proposal, which immediately deschedule the victim thread and abandon any resources it might own -- including heap (which can cause memory leaks) and synchronization objects (which, far worse, is almost guaranteed to cause deadlocks). And note that it's OK to allocate heap, or lock a mutex, and then enable async cancel for some section of code, disabling async cancel before freeing the memory or releasing the mutex. In such a sequence, the cleanup handlers invoked by async cancel DO know the state of the resources (they are "acquired"), and can clean up. You simply can't enable async cancel across a call that allocates or frees heap, locks or unlocks a mutex, because the cleanup handler couldn't tell whether the operation had completed. In contrast, ANY use of TerminateThread trashes the process unrecoverably, except in extremely unusual circumstances where an embedded-type application really knows precisely what the victim thread might be doing and can reliably repair any predicates and release or safely discard any resources. You can NEVER do this with a thread that might be running arbitrary library code, because you can't possibly know what resources it might own or the effect of abandoning them. (That's why pthread_abort() was rejected. While it's useful and even essential for some class of embedded system application, it's very nearly useless, and extremely dangerous, in any more general environment. Since the real value of POSIX in true embedded system design is "programmer portability", not full portability of every API, there would have been no point to including this specialized function in the general standard.) >>"Cancellation" (both deferred and async) come from the Digital "CMA" >>architecture (where it was called "alert"). The CMA concept derives from >>a less structured (but fundamentally similar) capability in the SRC >>research labs' Topaz thread package. >> >> >Do you know of other widely used system-level APIs which provide similar >features? > > No; though that's no guarantee that some haven't cropped up somewhere. >>>Definition "Posix thread-safety": >>>--------------------------------- >>>A library is "posix thread-safe" if it is thread-safe and >>>defered-cancelation-safe. >>> >>I wouldn't tack cancel-safety onto thread-safety so intimately, although >> >> >I used the POSIX name because I thought it was the only widely deployed >system which provides this service. Maybe we should rename this to >"strong thread-safety". Maybe "defered-cancel thread-safety"? > > But my point was that it's perfectly reasonable to have POSIX thread-safety without cancel-safety. I don't see how it's relevant whether anything but POSIX also has cancel-safety. >>(Async cancel is an oddity; there are, and should be, very few >>async-cancel-safe functions. Async-cancel regions of code cannot >>accomodate resource acquisition or release of any sort, as the recovery >>code is generally unable to determine the state of the resource.) >> >> >Yes. This is why I don't feel it's necessary to discuss it further since >so little code will be concerned with it, we can altogether not deal >with it for most C++ libraries. > > Introducing asynchronous exceptions into C++ would be pointlessly disruptive, like introducing continuable exceptions. I'd rather not even consider it. Even if it were supported, though, C++ is certainly free to follow the lead of POSIX. We designated only a very few functions to be async-cancel safe; and even at that I think we ended up with more than we really should have had. (I never really figured out why we ended up with pthread_cancel() being async-cancel safe, and I don't think it makes any sense. The guy who write the text couldn't remember either, but in the end we decided not to risk changing it.) Really, in terms of POSIX standard APIs, all you can do with async cancel enabled is to DISABLE async-cancel. I like it that way. There's no reason at all that ANY of the standard C++ runtime should be designated (or coded) to be async-cancel safe. >>Nevertheless, it's quite reasonable to write a "thread-safe" special >>purpose application routine that doesn't deal with cancellation simply >>because the designer KNOWS that a thread running that code cannot be >>cancelled. One might even make this choice within in a general purpose >>library in some cases -- say, for a daemon thread that could never run >>application code nor be identified to the application, and that >>therefore cannot be cancelled. >> >> >Yes. Exactly. I have written a lot of code like that. The core C++ >threaded code is hidden far away from the user which cannot therefore >"posix-defer-cancel" it. It can't even ever get the C++ exceptions since >they are catch (...) and transformed into C error codes. > > This doesn't sound like the same thing, though. Your catch(...) may prevent the cancel from doing what it SHOULD do, but it won't prevent delivery, and you've just ignored the application's cancel request. That's bad, and while it may be "cancel safe" in some trivial respect, (an unexpected cancel request won't corrupt the library state), it's not useful to anyone. If code runs in an application thread, or a thread for which application code might have a valid handle, then that thread can be cancelled at the whim of the application. You can of course simply DOCUMENT that doing this is an error. You can say it'll be ignored, or you can say that it may arbitrarily corrupt application state; but that's not a true general purpose library. What I'm talking about is a separate thread created within the library to which no application code could possibly have a reference. It is physically impossible for the application code to ever REQUEST cancellation. (Yeah, very little is "physically impossible", and a simple uninitialized variable could end up holding the handle of such a thread; but that's an application error against which nobody can reasonably defend.) Anyway, if the application "CAN'T" cancel the thread, and the library knows that it WON'T cancel the thread, there's no point in writing code that runs ONLY within that thread to be cancel safe. >As a conclusion to these (tentative) definitions, I believe the purpose >of this mailing list is to find a solution to develop "defered-cancel >thread-safe" C++ libraries: simple "thread-safe" libraries do not >require special attention. If everyone could agree to the statement >above, it would probably make the discussion more productive: other >threading models which do not support async cancelation are of no >interest to the discussion and can be forgotten. > > Code that cannot ever be subject to cancellation need not be cancel safe, if that's what you mean. If code was written to a thread model without cancellation, or written specifically for an environment where it would not be cancelled, that code can be brought into a new "cancellable C++" environment safely as long as that basic premise continues -- that it will not be run in a thread that's cancelled. >If people agree on this statement, the only issue I can see which >delimits the design space for the solution to this problem is whether or >not you wish to allow the C++ library calling into C code (which uses >pthreads) and/or allow C code to use the C++ library (which uses our C++ >threading solution). > >Maybe it would help to consider the two cases separatly and try to >figure out what requirements each case creates: > 1) C++ library calls C++ code and is called by C++ code. > 2) C++ library calls into C code. > 3) C code calls C++ library. > >The hard part seems to be 2) and 3) where, if you use exceptions to >propagate a cancel operation from either a cancelation point or a >pthread_exit call, you need to correctly handle the registered >cancelation handlers _and_ the C++ catch blocks in the right order. That >seems pretty hard (ie: impossible) to me, being just a _user_ of thread >libraries. > > The impact extends beyond C and C++, to every facility that deals with exceptions; Java, Ada, Modula-2+, or whatever else. The call stack must be unwound once, and all handlers, no matter how declared or in what language, called in the correct sequence. You're right -- it's nearly impossible without exceptions; yet it's trivial, natural, and all but unavoidable if everyone uses the same common exception/unwind package. (And I might point out that any "non exception" mechanism that could accomplish it would be indistinguishable from a common exception infrastructure anyway!) That's precisely why cancellation and thread exit ARE exceptions, were always intended to be exceptions, and cannot practically be anything else. ;-) >If people are not interested in 2) and 3) and just want to design a >solution for 1), then I think it will make the discussion more >productive to acknowledge it. > > The ANSI C++ committee could well do that; just as POSIX and C++ have so far essentially ignored each other. However, we might look back at the recently revealed origin of the name and subject of this mailing list, which is tangled up with actual implementation on a real system, specifically gcc. THEY cannot ignore interoperability between C and C++; and nor can anyone else in the real world. So even if the committee were to decide it cannot or is unwilling to address 2 and 3, I don't think that decision would be relevant to this mailing list! -- /--------------------[ 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 ]---/ From fjh at cs.mu.OZ.AU Thu Jan 8 14:21:50 2004 From: fjh at cs.mu.OZ.AU (Fergus Henderson) Date: Fri, 9 Jan 2004 01:21:50 +1100 Subject: [c++-pthreads] Re: cancellation points report failure In-Reply-To: <3FFD47A2.3147DE97@terekhov.de> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <20040107052644.GO32272@tofu.dreamhost.com> <3FFBF1D6.56ABAC@terekhov.de> <20040108040112.GB9432@jupiter.cs.mu.oz.au> <3FFD47A2.3147DE97@terekhov.de> Message-ID: <20040108142149.GA20652@ceres.cs.mu.oz.au> On 08-Jan-2004, Alexander Terekhov wrote: > Fergus Henderson wrote: > [...] > > > Objection. To begin with, asynchronous cancelability IS part of > > > pthreads. > > > > Asynchronous cancelability only works if you can be sure that there > > are no implicit calls to async-cancel-unsafe functions going on. > > Yep (and probably also C++ things like throw-expressions and "try > {"). "probably" is the word. That's what I mean -- in C++, you can't even tell which language constructs might be implemented with code which under-the-hood is going to be doing dynamic memory allocation or other async-cancel-unsafe operations. "#include "? Constructors for static objects? dynamic_cast? const_cast?? -- Fergus Henderson | "I have always known that the pursuit The University of Melbourne | of excellence is a lethal habit" WWW: | -- the last words of T. S. Garp. From baker at cs.fsu.edu Thu Jan 8 14:36:13 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Thu, 8 Jan 2004 09:36:13 -0500 Subject: [c++-pthreads] What are the real issues? In-Reply-To: <20040108035242.GA9432@jupiter.cs.mu.oz.au> References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <20040107193223.GA2884@diablo.name> <20040108035242.GA9432@jupiter.cs.mu.oz.au> Message-ID: <20040108143613.GB23908@diablo.name> > > From what I've read here, and my own experience, it seems this is > > the right thing to do, provided: > > > > 1) The only way to handle this new cancellation exception is via a > > new syntax. > Unfortunately that would conflict with standard C++ semantics, which > say that any exception can be caught and handled using the catch-elipsis > construct "catch (...)". Ada says the same thing about real exceptions. The difference is that abort is defined separately, semantically analogous enough to exceptions that you can implement it using the same mechanism, but distinct enough that it cannot be caught by an exception handler. The handlers to catch abort are a GNAT-specific extension, which we implementors of language runtime support libraries found it very helpful. The only form of cleanup *users* have for task abort are object finalizers. I think this is still seems closest to the C++ philosophy I've seen expressed by several people on this list, i.e., that the right C++ way to provide clean thread cancellation is to use local objects with finalizers (destructors) that do the cleanup. Of course, a finalizer/destructor could go into an infinite loop, recursion, or in some other way indefinitely postpone cancellation, but that is less likely than if one can provide arbitrary handlers. That is, a person who writes a destructor normally understands that it is supposed to do just a little work, and then return. A person who writes an exception handler is likely to use it to continue a computation, e.g., a handler inside a loop body, that allows the loop to go on to the next iteration. --Ted Baker From boo at terekhov.de Thu Jan 8 14:43:52 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Thu, 08 Jan 2004 15:43:52 +0100 Subject: [c++-pthreads] thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> <3FFD6355.80200@hp.com> Message-ID: <3FFD6CA8.B5CEF27C@terekhov.de> Dave Butenhof wrote: [...] > Nevertheless, async cancel CAN be used safely if you're careful, without > disrupting ... ^^^^^^^ ^^^^^^^^^^ Right. > Introducing asynchronous exceptions into C++ would be pointlessly > disruptive ... ^^^^^^^^^^^ ^^^^^^^^^^ And that's the phobia hits DRB again. > Even if it were supported, though, C++ is certainly free to follow the > lead of POSIX. We designated only a very few functions to be > async-cancel safe; and even at that I think we ended up with more than > we really should have had. (I never really figured out why we ended up > with pthread_cancel() being async-cancel safe, and I don't think it > makes any sense. The guy who write the text couldn't remember either, Could it be that the intent was to make pthread_testcancel(), not pthread_cancel(), async-cancel-safe? ;-) > but in the end we decided not to risk changing it.) Really, in terms of > POSIX standard APIs, all you can do with async cancel enabled is to > DISABLE async-cancel. I like it that way. There's no reason at all that > ANY of the standard C++ runtime should be designated (or coded) to be > async-cancel safe. C++ aside for a moment, I have yet to see async-cancel-UNsafe implementation of, say, strlen(). Care to inspire me? TIA. regards, alexander. From David.Butenhof at hp.com Thu Jan 8 15:06:59 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Thu, 08 Jan 2004 10:06:59 -0500 Subject: [c++-pthreads] thread-safety definition In-Reply-To: <3FFD6CA8.B5CEF27C@terekhov.de> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> <3FFD6355.80200@hp.com> <3FFD6CA8.B5CEF27C@terekhov.de> Message-ID: <3FFD7213.1050903@hp.com> Alexander Terekhov wrote: >Dave Butenhof wrote: >[...] > > >>Nevertheless, async cancel CAN be used safely if you're careful, without >>disrupting ... ^^^^^^^ >> >> > ^^^^^^^^^^ > >Right. > >>Introducing asynchronous exceptions into C++ would be pointlessly >>disruptive ... ^^^^^^^^^^^ >> >> > ^^^^^^^^^^ > >And that's the phobia hits DRB again. > > Several insults come to mind, but I'll bite my tongue. Alexander, you're either deliberately misusing, or just misunderstand, that word. Try looking it up in an English dictionary. Then try using it in a sentence where it actually belongs. After that, perhaps we can get back to talking about cancel and exceptions. >>Even if it were supported, though, C++ is certainly free to follow the >>lead of POSIX. We designated only a very few functions to be >>async-cancel safe; and even at that I think we ended up with more than >>we really should have had. (I never really figured out why we ended up >>with pthread_cancel() being async-cancel safe, and I don't think it >>makes any sense. The guy who write the text couldn't remember either, >> >> >Could it be that the intent was to make pthread_testcancel(), not >pthread_cancel(), async-cancel-safe? ;-) > > No. First off, the only reason to use async-cancel, with the associated complications and risks, is that you're unwilling to pay the cost of calling pthread_testcancel() -- e.g., in a tight computational loop. If you are willing to call pthread_testcancel() anywhere you "really want a cancel" it'd be just silly to bother with async cancel at all. Furthermore, despite the looseness in the standard wording that allows implementations to ignore async cancel entirely or for long periods, this was never the intent. In any implementation of any approximation to what the working group would have accepted as "quality", there'd be no point at all in making the call when async cancel is enabled. >>but in the end we decided not to risk changing it.) Really, in terms of >>POSIX standard APIs, all you can do with async cancel enabled is to >>DISABLE async-cancel. I like it that way. There's no reason at all that >>ANY of the standard C++ runtime should be designated (or coded) to be >>async-cancel safe. >> >> >C++ aside for a moment, I have yet to see async-cancel-UNsafe >implementation of, say, strlen(). Care to inspire me? TIA. > > Perhaps, but that's because you've looked. The point is that there's no way to know without reading the source. And even if you do, it may change on the next edit. You cannot safely PRESUME that anything at all is async cancel safe unless that's a documented characteristic of the function. Or, of course, if you wrote it yourself and are willing to bet that you didn't make any mistakes. ;-) -- /--------------------[ 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 ]---/ From dave at boost-consulting.com Thu Jan 8 15:08:16 2004 From: dave at boost-consulting.com (David Abrahams) Date: Thu, 08 Jan 2004 10:08:16 -0500 Subject: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> Message-ID: Mathieu Lacage writes: > hi all, > > It looks like what everyone is trying to achieve here is a way for C++ > authors to write thread-safe libraries. I think you missed something, or maybe the main thing. C++ authors can already write thread-safe libraries. What I'm trying to achieve is to allow existing thread-safe library code that wasn't written with POSIX cancellations in mind to be used easily in a POSIX environment. -- Dave Abrahams Boost Consulting www.boost-consulting.com From dave at boost-consulting.com Thu Jan 8 15:03:40 2004 From: dave at boost-consulting.com (David Abrahams) Date: Thu, 08 Jan 2004 10:03:40 -0500 Subject: What are the real issues? References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <3FFAC516.2010803@hp.com> <3F190B5E-407F-11D8-B918-00039390D9E0@apple.com> <20040106211236.GX30780@tofu.dreamhost.com> <20040107212604.GB2884@diablo.name> Message-ID: Ted Baker writes: >> (3) how to preserve the frankly enormous body of thread-safe code >> already written, deployed, and running for years. > > Just be be clear. You mean by "thread-safe" that the code > uses mutexes to protect access to shared data, but that it does > not use cancellation state management and cleanup handlers > to protect against thread cancellation.... right? > > If so, the only way out seems to be to require that for C++ > programs the initial cancellation state of every thread is > *disabled*. > > If you do that, then no thread can be cancelled unless it > contains a call to explicitly enable cancellation. That unfortunately misses a great opportunity to allow thread-safe and exception-neutral code to respond to cancellation requests without modification. -- Dave Abrahams Boost Consulting www.boost-consulting.com From David.Butenhof at hp.com Thu Jan 8 15:17:27 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Thu, 08 Jan 2004 10:17:27 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: References: <1073556051.28360.195.camel@felix.inria.fr> Message-ID: <3FFD7487.1000404@hp.com> David Abrahams wrote: >Mathieu Lacage writes: > > >>It looks like what everyone is trying to achieve here is a way for C++ >>authors to write thread-safe libraries. >> >> >I think you missed something, or maybe the main thing. C++ authors >can already write thread-safe libraries. > >What I'm trying to achieve is to allow existing thread-safe library >code that wasn't written with POSIX cancellations in mind to be used >easily in a POSIX environment. > > Right. And I maintain that this is trivial AS LONG AS you can guarantee that threads running that unsafe code will never be cancelled. (This seems reasonable, since obviously threads running that code had never previously been subject to cancellation!) And it's IMPOSSIBLE if you cannot guarantee that. The code must be analyzed and corrected, point by point; and the analysis (and possibly the correction as well) is easier if cancellation is a structured exception than if it's a return status handled in various "unique" ways by each routine. -- /--------------------[ 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 ]---/ From baker at cs.fsu.edu Thu Jan 8 15:23:32 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Thu, 8 Jan 2004 10:23:32 -0500 Subject: [c++-pthreads] thread-safety definition In-Reply-To: <1073556051.28360.195.camel@felix.inria.fr> References: <1073556051.28360.195.camel@felix.inria.fr> Message-ID: <20040108152332.GD23908@diablo.name> > 2.1) "async cancelation": The OS removes the thread from its list of > tasks to schedule and does nothing to cleanup the thread ressources. > This is the most extreme useless feature of a thread library. BeOS and > win32 provide it. POSIX does not provide it. There has been a push from the POSIX real-time people to add a new feature that will kill a thread *instantly* (without executing cleanup handlers). They are concerned that there must be a for-certain way of killing a runaway thread. They also want a way to do forced reinitialization of objects like mutexes and condition variables, to recover from such a disaster. My own first reaction to this was that they are trying to do fault recovery at the wrong level of granularity. Since threads share address space, killing a thread without cleanup is very likely (almost certain?) to corrupt memory and/or hold resources in a way that does not permit reliable continued execution of the threads that share the same address space. It seems they should be doing their fault recovery at the process level. Unfortunately, in some real-time POSIX OS's the multiple-process abstraction is not supported, either to keep the OS footprint small or because there is no virtual memory hardware support. This leaves no choice but to use only threads for concurrency, and then take extreme care in coding and in how resources are allocated, hoping that one can reliably reinitialize the system state and recreate needed threads after killing off rogue threads. --Ted Incidentally, last time I used Linuxthreads it seemed they implement pthread_kill() in a way that causes an uncaught signal to kill just one thread (not like POSIX, which requires killing the whole process). From dave at boost-consulting.com Thu Jan 8 15:26:00 2004 From: dave at boost-consulting.com (David Abrahams) Date: Thu, 08 Jan 2004 10:26:00 -0500 Subject: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> Message-ID: Mathieu Lacage writes: > Indeed. For a C++ POSIX binding, I would assume you might want to make > such a function throw an exception caught by the thread-creation > function to unwind properly the stack. Or is this some kind of wild > stupid idea ? It's not stupid, unless the function isn't allowed to throw any exceptions in standard environments. See http://article.gmane.org/gmane.comp.lang.c%2B%2B.pthreads/28 Functions like read() which the C++ standard allows to throw, but which in practice never do, run a close second in stupidity if you throw cancellation from them. -- Dave Abrahams Boost Consulting www.boost-consulting.com From dave at boost-consulting.com Thu Jan 8 15:32:11 2004 From: dave at boost-consulting.com (David Abrahams) Date: Thu, 08 Jan 2004 10:32:11 -0500 Subject: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> Message-ID: Dave Butenhof writes: > David Abrahams wrote: > >>Mathieu Lacage writes: >> >> >>>It looks like what everyone is trying to achieve here is a way for C++ >>> authors to write thread-safe libraries. >>> >>I think you missed something, or maybe the main thing. C++ authors >>can already write thread-safe libraries. >> >>What I'm trying to achieve is to allow existing thread-safe library >>code that wasn't written with POSIX cancellations in mind to be used >>easily in a POSIX environment. >> >> > Right. And I maintain that this is trivial AS LONG AS you can > guarantee that threads running that unsafe code will never be > cancelled. (This seems reasonable, since obviously threads running > that code had never previously been subject to cancellation!) And it's > IMPOSSIBLE if you cannot guarantee that. Please don't shout, especially to emphasize something I disagree with so fundamentally. ;-) > The code must be analyzed and corrected, point by point; and the > analysis (and possibly the correction as well) is easier if > cancellation is a structured exception than if it's a return status > handled in various "unique" ways by each routine. If you make cancellations behave sufficiently like an ordinary C++ exception (either of Nathan's or Jason's models would do that I think) then it's neither "IMPOSSIBLE" nor even difficult. A great deal of thread-safe exception-safe C++ library code would behave perfectly well under those conditions. -- Dave Abrahams Boost Consulting www.boost-consulting.com From Mathieu.Lacage at sophia.inria.fr Thu Jan 8 15:34:14 2004 From: Mathieu.Lacage at sophia.inria.fr (Mathieu Lacage) Date: Thu, 08 Jan 2004 16:34:14 +0100 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: References: <1073556051.28360.195.camel@felix.inria.fr> Message-ID: <1073576054.28360.333.camel@felix.inria.fr> On Thu, 2004-01-08 at 16:08, David Abrahams wrote: > I think you missed something, or maybe the main thing. C++ authors > can already write thread-safe libraries. I should have written "defered-cancel thread-safe" libraries. > What I'm trying to achieve is to allow existing thread-safe library > code that wasn't written with POSIX cancellations in mind to be used > easily in a POSIX environment. I missed that part. Reading again your emails, this becomes clear to me now. The key here is the definition of "easily". From easiest to hardest: 1) disable cancelability in all threads by default 2) enable cancelability in all new threads, make most standard library functions (as well as the POSIX locking functions used by these libraries) disable cancelation and throw cancelation exceptions from all the other functions marked as cancelable by POSIX 3) enable cancelability in all new threads, throw cancelation exceptions from all other functions marked as cancelable by POSIX. regards, Mathieu -- Mathieu Lacage From David.Butenhof at hp.com Thu Jan 8 15:46:57 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Thu, 08 Jan 2004 10:46:57 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> Message-ID: <3FFD7B71.1010708@hp.com> David Abrahams wrote: >Dave Butenhof writes: > > >>David Abrahams wrote: >> >> >>>Mathieu Lacage writes: >>> >>>>It looks like what everyone is trying to achieve here is a way for C++ >>>>authors to write thread-safe libraries. >>>> >>>I think you missed something, or maybe the main thing. C++ authors >>>can already write thread-safe libraries. >>> >>>What I'm trying to achieve is to allow existing thread-safe library >>>code that wasn't written with POSIX cancellations in mind to be used >>>easily in a POSIX environment. >>> >>Right. And I maintain that this is trivial AS LONG AS you can >>guarantee that threads running that unsafe code will never be >>cancelled. (This seems reasonable, since obviously threads running >>that code had never previously been subject to cancellation!) And it's >>IMPOSSIBLE if you cannot guarantee that. >> >> >Please don't shout, especially to emphasize something I disagree with >so fundamentally. ;-) > > Ah. I see the problem. I believe that you neglected to file in triplicate the official "List of all matters with which I disagree so please don't shout when discussing them" form. Didn't you? ;-) >>The code must be analyzed and corrected, point by point; and the >>analysis (and possibly the correction as well) is easier if >>cancellation is a structured exception than if it's a return status >>handled in various "unique" ways by each routine. >> >> >If you make cancellations behave sufficiently like an ordinary C++ >exception (either of Nathan's or Jason's models would do that I think) >then it's neither "IMPOSSIBLE" nor even difficult. A great deal of >thread-safe exception-safe C++ library code would behave perfectly >well under those conditions. > > Well, it should be no surprise that this discussion has fragmented into too many different little pathways for anyone to keep track of them. If cancel is implemented completely as a "full stature" C++ exception, and none of the "exception safe" code does anything silly like "catch(...)" without a re-throw, then, yes, it's likely to be reasonably cancel-safe. (Though some code might need to be aware that an uncaught cancel will terminate the thread rather than the process!) But I was really responding to the ideas about turning cancel into a return status, which seemed to be associated with the notion that this would somehow make it more transparent to the ported code. (I happen to think the OPPOSITE is true. Am I allowed to shout there? ;-) ) This may also be associated with some implications of a statement about porting "pthread code" into the new C++ environment; and since "pthread" code is by definition C rather than C++ (no C++ in POSIX!), it cannot be made portably exception safe. (Of course the same C extensions on Tru64 and OpenVMS that allow handling of cancel allow cleanup on propagation of any other exception; but that's an extension.) -- /--------------------[ 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 ]---/ From wil at bogo.xs4all.nl Thu Jan 8 15:57:09 2004 From: wil at bogo.xs4all.nl (Wil Evers) Date: Thu, 08 Jan 2004 16:57:09 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> Message-ID: <3FFD7DD5.9090801@bogo.xs4all.nl> Dave Butenhof wrote: > If cancel is implemented completely as a "full stature" C++ exception, > and none of the "exception safe" code does anything silly like > "catch(...)" without a re-throw, then, yes, it's likely to be reasonably > cancel-safe. A catch(...) without a re-throw isn't necessarily a silly thing to do, especially in a destructor. Sometimes, the only alternative is to risk a call to terminate(). - Wil From David.Butenhof at hp.com Thu Jan 8 16:07:34 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Thu, 08 Jan 2004 11:07:34 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <3FFD7DD5.9090801@bogo.xs4all.nl> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFD7DD5.9090801@bogo.xs4all.nl> Message-ID: <3FFD8046.3000702@hp.com> Wil Evers wrote: > Dave Butenhof wrote: > >> If cancel is implemented completely as a "full stature" C++ >> exception, and none of the "exception safe" code does anything silly >> like "catch(...)" without a re-throw, then, yes, it's likely to be >> reasonably cancel-safe. > > A catch(...) without a re-throw isn't necessarily a silly thing to do, > especially in a destructor. Sometimes, the only alternative is to > risk a call to terminate(). But it still breaks cancellation. Again, ignoring cancel entirely may be "cancel safe" in some trivial sense, but it's not useful or reasonable under nearly all circumstances. -- /--------------------[ 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 ]---/ From baker at cs.fsu.edu Thu Jan 8 16:20:36 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Thu, 8 Jan 2004 11:20:36 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <3FFD7DD5.9090801@bogo.xs4all.nl> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFD7DD5.9090801@bogo.xs4all.nl> Message-ID: <20040108162036.GG23908@diablo.name> > A catch(...) without a re-throw isn't necessarily a silly thing to do, > especially in a destructor. Sometimes, the only alternative is to risk > a call to terminate(). For this reason, Ada requires that abort (what we are calling cancellation here) be deferred automatically during the execution of finalizers (what we are calling destructors here). It is important that, as you say, finalizers can catch exceptions and encapsulate them, allowing finalization to continue with the next finalizer. This is an important point, that one may have recursive/nested exceptions, i.e., while handling one exception another exception may be raised and handled, upon completion of which processing of the the first exception resumes. In particular, if one is executing a chain of finalizers/descructors due to exception propagation causing one to exit the scope of an object with finalization, the finalizer/destructor should be able to catch local exceptions that might be raised/thrown by its own code, without re-raising them. On return from the finalizer/destructor the propagation of the other exception then continues, unaffected. This is what you also want with abort/cancellation. In the processing of the cancellation you execute cleanup handlers, finalizers, desctructors (or whatever you choose to call them) as you unwind the stack. Each of those may generate new exceptions, and handle them locally. That does not stop the unwinding of the stack until the original abort/cancellation is caught and handled (if you allow that to be caught and handled) or the thread terminates. --Ted From boo at terekhov.de Thu Jan 8 16:22:12 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Thu, 08 Jan 2004 17:22:12 +0100 Subject: [c++-pthreads] Re: cancellation points report failure References: <3FF18485.93F2D588@terekhov.de> <3FF9A13D.9030105@hp.com> <3FF9B2AD.D7E6C4DD@terekhov.de> <3FFAA301.6080301@hp.com> <20040107052644.GO32272@tofu.dreamhost.com> <3FFBF1D6.56ABAC@terekhov.de> <20040108040112.GB9432@jupiter.cs.mu.oz.au> <3FFD47A2.3147DE97@terekhov.de> <20040108142149.GA20652@ceres.cs.mu.oz.au> Message-ID: <3FFD83B4.2D45ABBD@terekhov.de> Fergus Henderson wrote: [...] > > > Asynchronous cancelability only works if you can be sure that there > > > are no implicit calls to async-cancel-unsafe functions going on. > > > > Yep (and probably also C++ things like throw-expressions and "try > > {"). > > "probably" is the word. I said "probably" because apart from inherently async-cancel-unsafe operations/functions like operator new()/operator delete() (and alike), a smart compiler understanding the meaning of async_cancel{} and sync_cancel{} (and also no_cancel{}) "probably" can translate things like void f() async_cancel_safe { /*...*/ sync_static T t(/*...*/); // synchronizised local static t.async_cancel_safe_operation(); } to void f() async_cancel_safe { /*...*/ sync_cancel { sync_static T t(/*...*/); // synchronizised local static async_cancel { t.async_cancel_safe_operation(); } } } > That's what I mean -- in C++, you can't even > tell which language constructs might be implemented with code which > under-the-hood is going to be doing dynamic memory allocation or other > async-cancel-unsafe operations. So, you really want to me stick to POF*** not only for signal handlers, but also for async-cancel-safe regions? Well, thankyou. regards, alexander. ***) http://groups.google.com/groups?selm=3F169C13.D83AA51E%40web.de (Subject: Re: Thread function and C++) From boo at terekhov.de Thu Jan 8 16:25:48 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Thu, 08 Jan 2004 17:25:48 +0100 Subject: [c++-pthreads] thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> <3FFD6355.80200@hp.com> <3FFD6CA8.B5CEF27C@terekhov.de> <3FFD7213.1050903@hp.com> Message-ID: <3FFD848C.25CAC0E8@terekhov.de> Dave Butenhof wrote: [...] > >Could it be that the intent was to make pthread_testcancel(), not > >pthread_cancel(), async-cancel-safe? ;-) > > > > > No. First off, the only reason to use async-cancel, with the associated > complications and risks, is that you're unwilling to pay the cost of > calling pthread_testcancel() -- e.g., in a tight computational loop. If > you are willing to call pthread_testcancel() anywhere you "really want a > cancel" it'd be just silly to bother with async cancel at all. Not at all. I want to call pthread_testcancel() at "some intervals" (within computational loop) and, at the same time, I want async cancel delivery and do NOT want to bother myself turning async cancel mode off/on just to test for pending cancellation request and trigger guaranteed delivery. > Furthermore, despite the looseness in the standard wording that allows > implementations to ignore async cancel entirely or for long periods, > this was never the intent. In any implementation of any approximation to > what the working group would have accepted as "quality", there'd be no > point at all in making the call when async cancel is enabled. What if I want to have it "react a bit faster" than a "clock tick"? What if all of my async-cancel-regions can be passed within "clock tick"? To me, "may occur" delivery with respect to async-cancel- regions is as bad (sort of problematic) as it is with respect to optional cancellation points. > > >>but in the end we decided not to risk changing it.) Really, in terms of > >>POSIX standard APIs, all you can do with async cancel enabled is to > >>DISABLE async-cancel. I like it that way. There's no reason at all that > >>ANY of the standard C++ runtime should be designated (or coded) to be > >>async-cancel safe. > >> > >> > >C++ aside for a moment, I have yet to see async-cancel-UNsafe > >implementation of, say, strlen(). Care to inspire me? TIA. > > > > > Perhaps, but that's because you've looked. The point is that there's no > way to know without reading the source. And even if you do, it may > change on the next edit. You cannot safely PRESUME that anything at all > is async cancel safe unless that's a documented characteristic of the > function. Or, of course, if you wrote it yourself and are willing to bet > that you didn't make any mistakes. ;-) Heck, let's mandate to have async-cancel-safe strlen()-and-alike stuff "in-addition-to OR instead-of" async-cancel-unsafe stuff and simply use C++ function overloading to pick the right stuff (async_cancel_safe inside async_cancel{}), okay? regards, alexander. From baker at cs.fsu.edu Thu Jan 8 16:32:15 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Thu, 8 Jan 2004 11:32:15 -0500 Subject: [c++-pthreads] thread-safety definition In-Reply-To: <3FFD848C.25CAC0E8@terekhov.de> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> <3FFD6355.80200@hp.com> <3FFD6CA8.B5CEF27C@terekhov.de> <3FFD7213.1050903@hp.com> <3FFD848C.25CAC0E8@terekhov.de> Message-ID: <20040108163215.GI23908@diablo.name> > Heck, let's mandate to have async-cancel-safe strlen()-and-alike > stuff "in-addition-to OR instead-of" async-cancel-unsafe stuff > and simply use C++ function overloading to pick the right stuff > (async_cancel_safe inside async_cancel{}), okay? A joke? (With nested calls and separate compilation you obviously cannot tell what call is "inside" an async_cancel{}.) --Ted From David.Butenhof at hp.com Thu Jan 8 16:36:04 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Thu, 08 Jan 2004 11:36:04 -0500 Subject: [c++-pthreads] thread-safety definition In-Reply-To: <3FFD848C.25CAC0E8@terekhov.de> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> <3FFD6355.80200@hp.com> <3FFD6CA8.B5CEF27C@terekhov.de> <3FFD7213.1050903@hp.com> <3FFD848C.25CAC0E8@terekhov.de> Message-ID: <3FFD86F4.7020003@hp.com> Alexander Terekhov wrote: >Heck, let's mandate to have async-cancel-safe strlen()-and-alike >stuff "in-addition-to OR instead-of" async-cancel-unsafe stuff >and simply use C++ function overloading to pick the right stuff >(async_cancel_safe inside async_cancel{}), okay? > > Let's not. -- /--------------------[ 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 ]---/ From boo at terekhov.de Thu Jan 8 16:52:31 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Thu, 08 Jan 2004 17:52:31 +0100 Subject: [c++-pthreads] thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> <3FFD6355.80200@hp.com> <3FFD6CA8.B5CEF27C@terekhov.de> <3FFD7213.1050903@hp.com> <3FFD848C.25CAC0E8@terekhov.de> <20040108163215.GI23908@diablo.name> Message-ID: <3FFD8ACF.583E5AC3@terekhov.de> Ted Baker wrote: > > > Heck, let's mandate to have async-cancel-safe strlen()-and-alike > > stuff "in-addition-to OR instead-of" async-cancel-unsafe stuff > > and simply use C++ function overloading to pick the right stuff > > (async_cancel_safe inside async_cancel{}), okay? > > A joke? (With nested calls and separate compilation you obviously > cannot tell what call is "inside" an async_cancel{}.) It's pretty much like "const correctness" for member functions but with "execution context" instead of "type of this" (and with "async_cancel_safe"/"async_cancel {}" instead of "const"). regards, alexander. From dave at boost-consulting.com Thu Jan 8 16:52:34 2004 From: dave at boost-consulting.com (David Abrahams) Date: Thu, 08 Jan 2004 11:52:34 -0500 Subject: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> Message-ID: Dave Butenhof writes: > David Abrahams wrote: > >>Dave Butenhof writes: >> >> >>>David Abrahams wrote: >>> >>Please don't shout, especially to emphasize something I disagree with >>so fundamentally. ;-) >> > Ah. I see the problem. I believe that you neglected to file in > triplicate the official "List of all matters with which I disagree so > please don't shout when discussing them" form. Didn't you? ;-) I filed. It was *you* who neglected to check with the department of archival non-shouting topic storage, wasn't it? >>If you make cancellations behave sufficiently like an ordinary C++ >>exception (either of Nathan's or Jason's models would do that I think) >>then it's neither "IMPOSSIBLE" nor even difficult. A great deal of >>thread-safe exception-safe C++ library code would behave perfectly >>well under those conditions. >> >> > Well, it should be no surprise that this discussion has fragmented > into too many different little pathways for anyone to keep track of > them. > > If cancel is implemented completely as a "full stature" C++ exception, > and none of the "exception safe" code does anything silly like > "catch(...)" without a re-throw, then, yes, it's likely to be > reasonably cancel-safe. Even if only *some* of it does that, Nathan and Jason's models are both likely to result in an eventual cancellation. > (Though some code might need to be aware that an uncaught cancel > will terminate the thread rather than the process!) Remember, we're talking about library code. Usually it's only used "on the inside" of threads so it doesn't care whether when it's terminated, it's the process or only a thread. > But I was really responding to the ideas about turning cancel into a > return status ...only for 'C' functions which ordinarily have a return status and don't throw... did you miss that? -- Dave Abrahams Boost Consulting www.boost-consulting.com From baker at cs.fsu.edu Thu Jan 8 17:40:11 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Thu, 8 Jan 2004 12:40:11 -0500 Subject: [c++-pthreads] thread-safety definition In-Reply-To: <3FFD8ACF.583E5AC3@terekhov.de> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> <3FFD6355.80200@hp.com> <3FFD6CA8.B5CEF27C@terekhov.de> <3FFD7213.1050903@hp.com> <3FFD848C.25CAC0E8@terekhov.de> <20040108163215.GI23908@diablo.name> <3FFD8ACF.583E5AC3@terekhov.de> Message-ID: <20040108174011.GA28721@diablo.name> On Thu, Jan 08, 2004 at 05:52:31PM +0100, Alexander Terekhov wrote: > Ted Baker wrote: > > > > > Heck, let's mandate to have async-cancel-safe strlen()-and-alike > > > stuff "in-addition-to OR instead-of" async-cancel-unsafe stuff > > > and simply use C++ function overloading to pick the right stuff > > > (async_cancel_safe inside async_cancel{}), okay? > > > > A joke? (With nested calls and separate compilation you obviously > > cannot tell what call is "inside" an async_cancel{}.) > > It's pretty much like "const correctness" for member functions > but with "execution context" instead of "type of this" (and with > "async_cancel_safe"/"async_cancel {}" instead of "const"). I thought you meant *compile* resolution of the overloading, so as to generate different call contexts of the same function name. Since your async_cancel{} code can have nested calls to arbitrary (separately compiled) code, you can't do that without interprocedural control flow analysis (across separately compiled modules), which I don't believe gcc has any plans to do. If you are talking about dynamic (runtime) dispatching, then you are adding runtime overhead to all calls, and could just as well *not* use overloading; instead, you could just as well have one wrapper function that is the C++ binding, and this wrapper function dynamically decides whether to do the extra work of protecting itself against async cancel, depending on the context implicit parameter. --Ted From austern at apple.com Thu Jan 8 17:45:34 2004 From: austern at apple.com (Matt Austern) Date: Thu, 8 Jan 2004 09:45:34 -0800 Subject: [c++-pthreads] thread-safety definition In-Reply-To: <3FFD7213.1050903@hp.com> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> <3FFD6355.80200@hp.com> <3FFD6CA8.B5CEF27C@terekhov.de> <3FFD7213.1050903@hp.com> Message-ID: <761D3FB4-4202-11D8-AD9A-00039390D9E0@apple.com> On Jan 8, 2004, at 7:06 AM, Dave Butenhof wrote: > Alexander Terekhov wrote: > >> Dave Butenhof wrote: >> [...] >> >>> Nevertheless, async cancel CAN be used safely if you're careful, >>> without >>> disrupting ... >>> ^^^^^^^ >>> >> ^^^^^^^^^^ >> >> Right. >> >>> Introducing asynchronous exceptions into C++ would be pointlessly >>> disruptive ... ^^^^^^^^^^^ >>> >> ^^^^^^^^^^ >> >> And that's the phobia hits DRB again. >> > Several insults come to mind, but I'll bite my tongue. Alexander, > you're either deliberately misusing, or just misunderstand, that word. > Try looking it up in an English dictionary. Then try using it in a > sentence where it actually belongs. After that, perhaps we can get > back to talking about cancel and exceptions. I'm almost willing to accept the word "phobia" to describe my own feelings about asynchronous cancellation in C++. Both as a user and as a compiler implementer, I'm very scared of asynchronous reporting mechanisms in C++. (This is one reason that I've always resisted any attempts to allow throwing an exception from a signal handler, for example.) I have some idea of what kind of review you'd need to make sure that user code is safe in the presence of asynchronous abnormal code paths, and what kinds of things you'd need to do in the compiler to make it reliable, and I have severe doubts that I could do either of those things correctly except in really rare cases. I think we're going to have enough trouble on this list coming to consensus about how C++ should handle POSIX deferred cancellation. Asynchronous cancellation is even harder. Let's leave it for another day. --Matt From terekhov at web.de Thu Jan 8 18:12:19 2004 From: terekhov at web.de (Alexander Terekhov) Date: Thu, 08 Jan 2004 19:12:19 +0100 Subject: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> <3FFD6355.80200@hp.com> <3FFD6CA8.B5CEF27C@terekhov.de> <3FFD7213.1050903@hp.com> <761D3FB4-4202-11D8-AD9A-00039390D9E0@apple.com> Message-ID: <3FFD9D83.DE4D5260@web.de> Matt Austern wrote: [...] > I'm almost willing to accept the word "phobia" to describe my own > feelings about asynchronous cancellation in C++. Why. I know. http://google.com/groups?selm=3C83C428.1B1D36C7%40web.de regards, alexander. From terekhov at web.de Thu Jan 8 18:11:55 2004 From: terekhov at web.de (Alexander Terekhov) Date: Thu, 08 Jan 2004 19:11:55 +0100 Subject: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> <3FFD6355.80200@hp.com> <3FFD6CA8.B5CEF27C@terekhov.de> <3FFD7213.1050903@hp.com> <3FFD848C.25CAC0E8@terekhov.de> <20040108163215.GI23908@diablo.name> <3FFD8ACF.583E5AC3@terekhov.de> <20040108174011.GA28721@diablo.name> Message-ID: <3FFD9D6B.52E97B36@web.de> Ted Baker wrote: [...] > I thought you meant *compile* resolution of the overloading, so as > to generate different call contexts of the same function name. > Since your async_cancel{} code can have nested calls to arbitrary > (separately compiled) code, you can't do that without ^ | async-cancel-safe -----+ (nested sync_cancel {} and no_cancel {} aside for a moment). > interprocedural control flow analysis (across separately compiled > modules), which I don't believe gcc has any plans to do. If you > are talking about dynamic (runtime) dispatching, then you are ... I'm NOT talking about dynamic (runtime) dispatching. Think of const correctness. regards, alexander. From jason at redhat.com Thu Jan 8 18:49:28 2004 From: jason at redhat.com (Jason Merrill) Date: Thu, 08 Jan 2004 13:49:28 -0500 Subject: [c++-pthreads] thread-safety definition In-Reply-To: <1073556051.28360.195.camel@felix.inria.fr> (Mathieu Lacage's message of "Thu, 08 Jan 2004 11:00:51 +0100") References: <1073556051.28360.195.camel@felix.inria.fr> Message-ID: On Thu, 08 Jan 2004 11:00:51 +0100, Mathieu Lacage wrote: > 2.2) "defered cancelation": I know of only POSIX to implement this. The > canceled flag for the target thread is set and the thread cancelation > handlers are invoked whenever the thread reaches a cancelation point > (that is, it calls one of a set of specific library functions). While we're enumerating existing practice... Deferred cancellation is also a feature of the Java (Thread.interrupt) and Ada (task abort) threading models. In Java, cancellation is implemented by throwing an InterruptedException. The cancellation points are Object.wait, Thread.join and Thread.sleep. In particular, an interrupt does not wake up a thread blocked on a lock or I/O. In Ada, cancellation runs cleanups, but is not defined to be an exception, though it is often implemented as one. In particular, it cannot be caught by exception handlers in user code. Jason From baker at cs.fsu.edu Thu Jan 8 21:22:03 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Thu, 8 Jan 2004 16:22:03 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <3FFD9D6B.52E97B36@web.de> References: <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> <3FFD6355.80200@hp.com> <3FFD6CA8.B5CEF27C@terekhov.de> <3FFD7213.1050903@hp.com> <3FFD848C.25CAC0E8@terekhov.de> <20040108163215.GI23908@diablo.name> <3FFD8ACF.583E5AC3@terekhov.de> <20040108174011.GA28721@diablo.name> <3FFD9D6B.52E97B36@web.de> Message-ID: <20040108212203.GC372@diablo.name> OK. I now see from your example that intervened that you seem to intend that functions that are asyn-cancel safe/unsafe have some syntactic tag (like "const") so that the compiler can check them. I was assuming that you would rely on the compiler to infer which regions of code might be subject to asynch cancellation from the embedded async_cancel{} and then work back the call trees to infer which other functions might be unsafe. It did not occur to me that you would be willing to require users to go so far in marking up their code. --Ted On Thu, Jan 08, 2004 at 07:11:55PM +0100, Alexander Terekhov wrote: > > Ted Baker wrote: > [...] > > I thought you meant *compile* resolution of the overloading, so as > > to generate different call contexts of the same function name. > > Since your async_cancel{} code can have nested calls to arbitrary > > (separately compiled) code, you can't do that without > ^ > | > async-cancel-safe -----+ (nested sync_cancel {} and no_cancel {} > aside for a moment). > > > interprocedural control flow analysis (across separately compiled > > modules), which I don't believe gcc has any plans to do. If you > > are talking about dynamic (runtime) dispatching, then you are ... > > I'm NOT talking about dynamic (runtime) dispatching. Think of > const correctness. From rth at redhat.com Thu Jan 8 21:36:55 2004 From: rth at redhat.com (Richard Henderson) Date: Thu, 8 Jan 2004 13:36:55 -0800 Subject: [c++-pthreads] thread-safety definition In-Reply-To: <3FFD6CA8.B5CEF27C@terekhov.de> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> <3FFD6355.80200@hp.com> <3FFD6CA8.B5CEF27C@terekhov.de> Message-ID: <20040108213655.GB31384@redhat.com> On Thu, Jan 08, 2004 at 03:43:52PM +0100, Alexander Terekhov wrote: > C++ aside for a moment, I have yet to see async-cancel-UNsafe > implementation of, say, strlen(). Care to inspire me? TIA. I have yet to see an async-safe exception runtime. It's certainly possible, but it requires the addition of lots of memory barriers to the code. Which no one adds because all C++ exceptions are synchronous [1]. Even Java exceptions are synchronous; the times that they begin propagation from signal handlers are all synchronous signals like SIGSEGV or SIGFPE, and the compiler knows which instructions might produce them. Thus I believe you will find that async cancelation does *not* actually invoke cleanup handlers in existing implementations. r~ [1] Ada exceptions are async, but that language explicitly denys some of the data availability guarantees that other languages make, which allows you to avoid the memory barriers. From baker at cs.fsu.edu Thu Jan 8 21:53:10 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Thu, 8 Jan 2004 16:53:10 -0500 Subject: [c++-pthreads] thread-safety definition In-Reply-To: <20040108213655.GB31384@redhat.com> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> <3FFD6355.80200@hp.com> <3FFD6CA8.B5CEF27C@terekhov.de> <20040108213655.GB31384@redhat.com> Message-ID: <20040108215310.GE372@diablo.name> > [1] Ada exceptions are async, but that language explicitly denys some > of the data availability guarantees that other languages make, which > allows you to avoid the memory barriers. Right. Both in Ada 83 and Ada 95 there was a lot of time and intelletual effort devoted to the rules regarding what one can assume has been done/undone when an exception is raised. We implemented asynchronous transfers of control in GNARL, and they tested out OK to the extent the GNAT regression tests and Ada validation tests exercise them. On the other hand, knowing what goes on underneath (essentially an longjmp, with unwinding, from a signal handler to the recovery point) and how much difficulty we had making the implementation work, I have cooled considerably on the idea of *using* asynchronous exceptions (and, to a lesser extent, exceptions in general). The opportunities for surpises are just too many. --Ted From wil at bogo.xs4all.nl Fri Jan 9 06:01:31 2004 From: wil at bogo.xs4all.nl (Wil Evers) Date: Fri, 09 Jan 2004 07:01:31 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFD7DD5.9090801@bogo.xs4all.nl> <3FFD8046.3000702@hp.com> Message-ID: <3FFE43BB.6040809@bogo.xs4all.nl> Dave Butenhof wrote: > Wil Evers wrote: > >> Dave Butenhof wrote: >> >>> If cancel is implemented completely as a "full stature" C++ >>> exception, and none of the "exception safe" code does anything silly >>> like "catch(...)" without a re-throw, then, yes, it's likely to be >>> reasonably cancel-safe. >> >> A catch(...) without a re-throw isn't necessarily a silly thing to do, >> especially in a destructor. Sometimes, the only alternative is to >> risk a call to terminate(). > > But it still breaks cancellation. Again, ignoring cancel entirely may be > "cancel safe" in some trivial sense, but it's not useful or reasonable > under nearly all circumstances. I'm not suggesting that catching a cancellation request and not rethrowing it is a good thing. I'm trying to point out that - sometimes - the C++ language rules leave with me no other choice. A second exception escaping from a destructor called while unwinding the stack because of some earlier exception will result in program termination. If catching (and not rethrowing) this second exception breaks the cancellation machinery, then it is the cancellation machinery - and not the program in question - that is broken. In other words: we need a way to prevent this scenario. - Wil From rth at redhat.com Fri Jan 9 08:20:36 2004 From: rth at redhat.com (Richard Henderson) Date: Fri, 9 Jan 2004 00:20:36 -0800 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <3FFE43BB.6040809@bogo.xs4all.nl> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFD7DD5.9090801@bogo.xs4all.nl> <3FFD8046.3000702@hp.com> <3FFE43BB.6040809@bogo.xs4all.nl> Message-ID: <20040109082036.GA31026@redhat.com> On Fri, Jan 09, 2004 at 07:01:31AM +0100, Wil Evers wrote: > A second exception escaping from a destructor called while unwinding > the stack because of some earlier exception will result in program > termination. > > If catching (and not rethrowing) this second exception breaks the > cancellation machinery, then it is the cancellation machinery - and not > the program in question - that is broken. In other words: we need a way > to prevent this scenario. It would be trivial to adjust the c++ runtime to disable cancelation while exceptions are in flight. That avoids that scenario entirely. r~ From baker at cs.fsu.edu Fri Jan 9 11:20:28 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Fri, 9 Jan 2004 06:20:28 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <20040109082036.GA31026@redhat.com> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFD7DD5.9090801@bogo.xs4all.nl> <3FFD8046.3000702@hp.com> <3FFE43BB.6040809@bogo.xs4all.nl> <20040109082036.GA31026@redhat.com> Message-ID: <20040109112028.GA12343@diablo.name> > It would be trivial to adjust the c++ runtime to disable cancelation > while exceptions are in flight. That avoids that scenario entirely. That is what Gnat does. --Ted From David.Butenhof at hp.com Fri Jan 9 12:20:18 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Fri, 09 Jan 2004 07:20:18 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> Message-ID: <3FFE9C82.3060500@hp.com> David Abrahams wrote: >>Ah. I see the problem. I believe that you neglected to file in >>triplicate the official "List of all matters with which I disagree so >>please don't shout when discussing them" form. Didn't you? ;-) >> >> >I filed. It was *you* who neglected to check with the department of >archival non-shouting topic storage, wasn't it? > > Aw, shoot. I forgot to pay the bill on my automatic update service, and it expired! Sorry 'bout that. >>>If you make cancellations behave sufficiently like an ordinary C++ >>>exception (either of Nathan's or Jason's models would do that I think) >>>then it's neither "IMPOSSIBLE" nor even difficult. A great deal of >>>thread-safe exception-safe C++ library code would behave perfectly >>>well under those conditions. >>> >>Well, it should be no surprise that this discussion has fragmented >>into too many different little pathways for anyone to keep track of >>them. >> >>If cancel is implemented completely as a "full stature" C++ exception, >>and none of the "exception safe" code does anything silly like >>"catch(...)" without a re-throw, then, yes, it's likely to be >>reasonably cancel-safe. >> >> >Even if only *some* of it does that, Nathan and Jason's models are >both likely to result in an eventual cancellation. > > I need to start keeping a scorecard. Lessee here, Nathan's model I believe could be trivially characterized as "return failure status on any cancellation point until you hit a C++ function allowed to throw". This would apparently disable any cleanup operations involving cancellation points (they'd fail) at least prior to the first throw point -- and it's not precisely clear how the "pending cancel" state is to be managed even beyond that point. While it might eventually lead to cancellation, it won't reliably perform cleanup. This seems to me to be more complicated, less predictable, less compatible, and less reliable than simply using exceptions integrated with the existing POSIX cleanup mechanism. Jason's model, putting together the puzzle pieces from various messages here, appears to be basing cancel on an ordinary ("finalizable") C++ exception, which would be raised by any and all cancellation points; but if the exception object were to be destroyed (by finalization), the "pending cancel" would (might?) be reasserted. I personally feel that it ought to be reasonable to finalize the cancel and continue operation. If that wasn't really what the application intended then it's an application error; but there are cases where it's arguably reasonable, and the capability falls naturally out of the model. Besides, "reasserting cancel" in all the right places and no other places sounds like one of those projects that ends up being a lot more subtle and error-prone than anyone ever expects. ;-) >>(Though some code might need to be aware that an uncaught cancel >>will terminate the thread rather than the process!) >> >> >Remember, we're talking about library code. Usually it's only used >"on the inside" of threads so it doesn't care whether when it's >terminated, it's the process or only a thread. > >>But I was really responding to the ideas about turning cancel into a >>return status >> >> >...only for 'C' functions which ordinarily have a return status and don't >throw... did you miss that? > It doesn't matter. There are many problems lurking here, and only one of the more obvious is that this attempt to avoid breaking C code that depends on 'if (error) {cleanup(); return error;}' will still break if 'cleanup()' depends on any cancellation points... which in fact is quite likely if 'error' originates from a cancellation point (e.g., I/O). I also don't believe that a sufficient body of library code really follows the simple and direct 'if (error) return error;' pattern to make this strategy particularly useful even without the cleanup issues -- but that's a different matter. (It might actually be better if the pending cancel wasn't "sticky", since then the cleanup at least might succeed; but then you'll lose cancels all over the place, and that's definitely not acceptable. It's fine for a thread to CHOOSE not to accept a cancel, either by keeping cancellation disabled or by not calling a cancellation point; but it's not OK to lose a delivered cancel. You can declare this to be the application/library developer's job rather than the implementation's job -- but that just means all this "broken" C code that fails to propagate errors needs to be redesigned; and you haven't solved any real problem. That's the basic point: it needs to be analyzed and likely redesigned to be cancel-safe. You simply can't avoid that requirement.) A) You cannot transparently make non cancel-safe C code cancel-safe, no matter how you try. B) Delivering cancellation as a C++ exception will make much (though not all) exception-safe C++ "translucently" cancel-safe. C) POSIX C cancel-safe code is already cancel-safe, but for an application containing mixed call stacks to remain cancel-safe, POSIX cleanup and C++ exceptions must be integrated into a single unwind; i.e., (by whatever name) basing both on a common exception library. D) There are no useful or interestingly large bodies of rigorously "cancel-safe" C code that are not POSIX cancel-safe. In other words, cancel-as-exception, and pthread_cleanup_push as a C-based "try" extension, gives you all the cancel-safety you can reasonably expect without recoding (and likely redesign). That's all that's needed. No less will suffice, and any more is needless complication. Non cancel-safe code will continue to work just fine as long as it's NOT cancelled -- which ought to be no terrible restriction since it was clearly written in and for an environment where cancellation either did not exist or wasn't needed. -- /--------------------[ 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 ]---/ From boo at terekhov.de Fri Jan 9 13:20:12 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Fri, 09 Jan 2004 14:20:12 +0100 Subject: [c++-pthreads] thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> <3FFD6355.80200@hp.com> <3FFD6CA8.B5CEF27C@terekhov.de> <20040108213655.GB31384@redhat.com> Message-ID: <3FFEAA8C.7085265B@terekhov.de> Richard Henderson wrote: > > On Thu, Jan 08, 2004 at 03:43:52PM +0100, Alexander Terekhov wrote: > > C++ aside for a moment, I have yet to see async-cancel-UNsafe > > implementation of, say, strlen(). Care to inspire me? TIA. > > I have yet to see an async-safe exception runtime. It's certainly > possible, but it requires the addition of lots of memory barriers > to the code. Huh? Let's talk about memory barriers next week, okay? http://www.terekhov.de/pthread_refcount_t/draft-edits.txt http://groups.google.com/groups?selm=3EEF38E8.311EEC77%40web.de http://www.terekhov.de/pthread_refcount_t/experimental/refcount.cpp > Which no one adds because all C++ exceptions are > synchronous [1]. Even Java exceptions are synchronous; the times Well, http://www.rtj.org/rtsj-V1.0.pdf "When a method is declared with AsynchronouslyInterruptedException in its throws clause the platform is expected to asynchronously throw this exception if RealtimeThread.interrupt() is called while the method is executing, or if such an interrupt is pending any time control returns to the method. The interrupt is not thrown while any methods it invokes are executing, unless they are, in turn, declared to throw the exception. This is intended to allow long-running computations to be terminated without the overhead or latency of polling with java.lang.Thread.interrupted(). The throws AsynchronouslyInterruptedException clause is a marker on a stack frame which allows a method to be statically marked as asynchronously interruptible." regards, alexander. P.S. http://tinyurl.com/2lfoz From boo at terekhov.de Fri Jan 9 14:40:59 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Fri, 09 Jan 2004 15:40:59 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> <3FFD6355.80200@hp.com> <3FFD6CA8.B5CEF27C@terekhov.de> <3FFD7213.1050903@hp.com> <3FFD848C.25CAC0E8@terekhov.de> <20040108163215.GI23908@diablo.name> <3FFD8ACF.583E5AC3@terekhov.de> <20040108174011.GA28721@diablo.name> <3FFD9D6B.52E97B36@web.de> <20040108212203.GC372@diablo.name> Message-ID: <3FFEBD7B.FCCAD883@terekhov.de> Ted Baker wrote: > > OK. I now see from your example that intervened that you seem to > intend that functions that are asyn-cancel safe/unsafe have some > syntactic tag (like "const") so that the compiler can check them. > I was assuming that you would rely on the compiler to infer which > regions of code might be subject to asynch cancellation from the > embedded async_cancel{} and then work back the call trees to > infer which other functions might be unsafe. It did not occur > to me that you would be willing to require users to go so far > in marking up their code. http://google.com/groups?threadm=3D293F54.8FFA810%40web.de > How much knowledge about the standard libraries are you > willing to bring into the language? As much as given in the function prototype - no more, no less. The problem is, however, there is no way to give this information in function prototype in current language. regards, alexander. From boo at terekhov.de Fri Jan 9 14:42:35 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Fri, 09 Jan 2004 15:42:35 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFD7DD5.9090801@bogo.xs4all.nl> <3FFD8046.3000702@hp.com> <3FFE43BB.6040809@bogo.xs4all.nl> Message-ID: <3FFEBDDB.673AE752@terekhov.de> Wil Evers wrote: [...] > If catching (and not rethrowing) this second exception breaks the > cancellation machinery, then it is the cancellation machinery - and not > the program in question - that is broken. In other words: we need a way > to prevent this scenario. Here's a sort of "current way" to prevent this scenario: http://www.terekhov.de/DESIGN-futex-CV.cpp class cancel_off_guard { //*** unimplemented since it's non-copyable/non-copy-constructible cancel_off_guard(const cancel_off_guard &); cancel_off_guard & operator=(const cancel_off_guard &); int m_old_cancel_state; public: cancel_off_guard() { int status = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &m_old_cancel_state); assert(!status); } ~cancel_off_guard() { int status = pthread_setcancelstate(m_old_cancel_state, &m_old_cancel_state); assert(!status); } }; //*** class cancel_off_guard ~futex_condvar() { mutex::guard guard(m_mutex); assert(m_waiters[0] == m_wakeups); while (m_waiters[0]) { int ftx = m_futex = EOC(); mutex::release_guard release_guard(guard); cancel_off_guard no_cancel; m_futex.wait(ftx); } } However, the standard mandated mandatory 2-phase EH (with fixed exception specs/implicit throw() ES for dtors) and "intelligent" cancellation points (and async-cancel-regions, of course) is the way to go, I believe strongly. regards, alexander. From jason at redhat.com Sat Jan 10 02:41:14 2004 From: jason at redhat.com (Jason Merrill) Date: Fri, 09 Jan 2004 21:41:14 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <3FFE9C82.3060500@hp.com> (Dave Butenhof's message of "Fri, 09 Jan 2004 07:20:18 -0500") References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> Message-ID: On Fri, 09 Jan 2004 07:20:18 -0500, Dave Butenhof wrote: > Jason's model, putting together the puzzle pieces from various messages > here My original formulation is at http://www.codesourcery.com/archives/c++-pthreads/msg00059.html > , appears to be basing cancel on an ordinary ("finalizable") C++ > exception, which would be raised by any and all cancellation points; but if > the exception object were to be destroyed (by finalization), the "pending > cancel" would (might?) be reasserted. I personally feel that it ought to be > reasonable to finalize the cancel and continue operation. If that wasn't > really what the application intended then it's an application error; but > there are cases where it's arguably reasonable, and the capability falls > naturally out of the model. Too naturally, IMO. There seems to be a substantial amount of C++ code out there which traps and finalizes all exceptions, some of it in the C++ standard library I/O classes. I disagree with this design, and propose to change it in the message cited above, but it's just one example. I don't think it's reasonable for a cancellation request to be silently discarded just because the thread was in the middle of doing some formatted output when the cancel was received. I wouldn't object to a facility for explicitly abandoning the cancel, but I think that allowing it to fall out of the model is far too fragile. > Besides, "reasserting cancel" in all the right places and no other places > sounds like one of those projects that ends up being a lot more subtle > and error-prone than anyone ever expects. ;-) Assuming support for abanoning the cancel, why would it be more complex than pthread_cancel (pthread_self ()); ? Jason From dave at boost-consulting.com Sat Jan 10 04:01:15 2004 From: dave at boost-consulting.com (David Abrahams) Date: Fri, 09 Jan 2004 23:01:15 -0500 Subject: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> Message-ID: Dave Butenhof writes: >>>>If you make cancellations behave sufficiently like an ordinary C++ >>>>exception (either of Nathan's or Jason's models would do that I think) >>>>then it's neither "IMPOSSIBLE" nor even difficult. A great deal of >>>>thread-safe exception-safe C++ library code would behave perfectly >>>>well under those conditions. >>>> >>>Well, it should be no surprise that this discussion has fragmented >>>into too many different little pathways for anyone to keep track of >>>them. >>> >>>If cancel is implemented completely as a "full stature" C++ exception, >>>and none of the "exception safe" code does anything silly like >>>"catch(...)" without a re-throw, then, yes, it's likely to be >>> reasonably cancel-safe. >>> >>Even if only *some* of it does that, Nathan and Jason's models are >>both likely to result in an eventual cancellation. >> > I need to start keeping a scorecard. Lessee here, Nathan's model I > believe could be trivially characterized as "return failure status on > any cancellation point until you hit a C++ function allowed to > throw". This would apparently disable any cleanup operations involving > cancellation points (they'd fail) at least prior to the first throw > point -- and it's not precisely clear how the "pending cancel" state > is to be managed even beyond that point. While it might eventually > lead to cancellation, it won't reliably perform cleanup. I'm having trouble understanding. How does a "pending cancel state" perform cleanup? And what do "reliably" and "cleanup" mean to you? > This seems to me to be more complicated, less predictable, less > compatible, and less reliable than simply using exceptions > integrated with the existing POSIX cleanup mechanism. That's a rather broad sweep of the judgement brush, accompanied by little to lend any insight into why you think it's so. > Jason's model, putting together the puzzle pieces from various > messages here, appears to be basing cancel on an ordinary > ("finalizable") C++ exception, which would be raised by any and all > cancellation points; but if the exception object were to be destroyed > (by finalization), the "pending cancel" would (might?) be > reasserted. I personally feel that it ought to be reasonable to > finalize the cancel and continue operation. If that wasn't really what > the application intended then it's an application error; but there are > cases where it's arguably reasonable, and the capability falls > naturally out of the model. I don't have a strong opinion about that, but I'm sympathetic to your POV. > Besides, "reasserting cancel" in all the right places and no other > places sounds like one of those projects that ends up being a lot > more subtle and error-prone than anyone ever expects. ;-) I don't really see what you think is complicated about this. In Jason's model cancellation gets reasserted as soon as a caught cancellation exception is destroyed by falling off the end of the catch block without rethrowing. >>>But I was really responding to the ideas about turning cancel into a >>>return status >>> >>> >>...only for 'C' functions which ordinarily have a return status and don't >>throw... did you miss that? >> > It doesn't matter. There are many problems lurking here, and only one > of the more obvious is that this attempt to avoid breaking C code that > depends on 'if (error) {cleanup(); return error;}' will still break if > 'cleanup()' depends on any cancellation points... which in fact is > quite likely if 'error' originates from a cancellation point (e.g., > I/O). In what sense will it break? > I also don't believe that a sufficient body of library code > really follows the simple and direct 'if (error) return error;' > pattern to make this strategy particularly useful even without the > cleanup issues -- but that's a different matter. Before I used exceptions I certainly wrote reams and reams of code just like that. > (It might actually be > better if the pending cancel wasn't "sticky", You lost me. What's "sticky"? > since then the cleanup at least might succeed; but then you'll lose > cancels all over the place, and that's definitely not > acceptable. AFAICT you haven't understood Jason's proposal at all, though I may be misinterpreting your response. > It's fine for a thread to CHOOSE not to accept a cancel, > either by keeping cancellation disabled or by not calling a > cancellation point; but it's not OK to lose a delivered cancel. You > can declare this to be the application/library developer's job > rather than the implementation's job -- but that just means all this > "broken" C code that fails to propagate errors needs to be > redesigned; and you haven't solved any real problem. That's the > basic point: it needs to be analyzed and likely redesigned to be > cancel-safe. You simply can't avoid that requirement.) > > A) You cannot transparently make non cancel-safe C code cancel-safe, > no matter how you try. It depends how "cancel" is implemented I guess. > B) Delivering cancellation as a C++ exception will make much (though > not all) exception-safe C++ "translucently" cancel-safe. Sure, provided that you only deliver it from functions which could otherwise throw. > C) POSIX C cancel-safe code is already cancel-safe, but for an > application containing mixed call stacks to remain cancel-safe, POSIX > cleanup and C++ exceptions must be integrated into a single unwind; > i.e., (by whatever name) basing both on a common exception library. > D) There are no useful or interestingly large bodies of rigorously > "cancel-safe" C code that are not POSIX cancel-safe. > > In other words, cancel-as-exception, and pthread_cleanup_push as a > C-based "try" extension, gives you all the cancel-safety you can > reasonably expect without recoding (and likely redesign). That's all > that's needed. No less will suffice, and any more is needless > complication. Except for the fact that we have normally-non-throwing functions that have started to throw, I don't think anyone's disagreeing with you. -- Dave Abrahams Boost Consulting www.boost-consulting.com From wil at bogo.xs4all.nl Sat Jan 10 08:19:43 2004 From: wil at bogo.xs4all.nl (Wil Evers) Date: Sat, 10 Jan 2004 09:19:43 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> Message-ID: <3FFFB59F.60002@bogo.xs4all.nl> David Abrahams wrote: > Dave Butenhof writes: > >>Besides, "reasserting cancel" in all the right places and no other >>places sounds like one of those projects that ends up being a lot >>more subtle and error-prone than anyone ever expects. ;-) > > I don't really see what you think is complicated about this. In > Jason's model cancellation gets reasserted as soon as a caught > cancellation exception is destroyed by falling off the end of the > catch block without rethrowing. What if, at that point, the thread has disabled cancellation? - Wil From dave at boost-consulting.com Sat Jan 10 13:47:18 2004 From: dave at boost-consulting.com (David Abrahams) Date: Sat, 10 Jan 2004 08:47:18 -0500 Subject: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <3FFFB59F.60002@bogo.xs4all.nl> Message-ID: Wil Evers writes: > David Abrahams wrote: > >> Dave Butenhof writes: >> >>>Besides, "reasserting cancel" in all the right places and no other >>>places sounds like one of those projects that ends up being a lot >>>more subtle and error-prone than anyone ever expects. ;-) >> I don't really see what you think is complicated about this. In >> Jason's model cancellation gets reasserted as soon as a caught >> cancellation exception is destroyed by falling off the end of the >> catch block without rethrowing. > > What if, at that point, the thread has disabled cancellation? Naturally, no cancellation exceptions are thrown until cancellation is re-enabled (I think). -- Dave Abrahams Boost Consulting www.boost-consulting.com From baker at cs.fsu.edu Sat Jan 10 15:10:40 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Sat, 10 Jan 2004 10:10:40 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <3FFFB59F.60002@bogo.xs4all.nl> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <3FFFB59F.60002@bogo.xs4all.nl> Message-ID: <20040110151040.GB13609@diablo.name> > >I don't really see what you think is complicated about this. In > >Jason's model cancellation gets reasserted as soon as a caught > >cancellation exception is destroyed by falling off the end of the > >catch block without rethrowing. > > What if, at that point, the thread has disabled cancellation? First, if the thread had disabled cancellation before the the cancellation exception was raised, you could not get to this point. Therefore, you are only asking about the case where a thread disables cancellation in a cleanup routine or destructor and does not reenable it. In this case you still want to "reassert" the cancellation at the next point control leaves a handler/catch block without rethrowing. --Ted From baker at cs.fsu.edu Sat Jan 10 15:20:08 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Sat, 10 Jan 2004 10:20:08 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <3FFFB59F.60002@bogo.xs4all.nl> Message-ID: <20040110152008.GC13609@diablo.name> > >> Jason's model cancellation gets reasserted as soon as a caught > >> cancellation exception is destroyed by falling off the end of the > >> catch block without rethrowing. > > > > What if, at that point, the thread has disabled cancellation? > > Naturally, no cancellation exceptions are thrown until cancellation is > re-enabled (I think). BTW, it is important to distinguish between user-level enabling/disabling of cancellation and what may go on in the language implementation. You do not want some erroneous user code (e.g., a cleanup routine that disables cancellation) to break the language implementation of exception propagation. Therefore, the implementor may provide a lower-level mechanism, that supersedes or ignores whatever cancellation state the user set. The user-level cancellation enable/disable bit essentially just tells whether the various cancellation point routines should initiate cancellation processing if there is a pending cancellation, e.g., if (self->cancellation_enabled && self->cancellation_state == pending) { self->cancellation_state = cancelling; throw_exception (cancellation); // a runtime system call } This does not prevent the language runtime from *re*initiating cancellation processing at other points where it makes sense, e.g., if (self->cancellation_state == cancelling) throw_exception (cancellation); --Ted From baker at cs.fsu.edu Sat Jan 10 15:39:50 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Sat, 10 Jan 2004 10:39:50 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> Message-ID: <20040110153950.GD13609@diablo.name> > Except for the fact that we have normally-non-throwing functions that > have started to throw, I don't think anyone's disagreeing with you. This still perplexes me. How does a "normally-non-throwing function" "start to throw"? The examples I recall seeing here are of C/C++ library functions that make system calls via C library routines that are cancellation points. 1) It is normal for these C library routines to be cancellation points, in any POSIX/Unix/DCE multithreaded program. Cancellation is semantically equivalent to an exception, so these are not normally-non-throwing functions. 2) It may be that the existing implementations of some other C++ library functions call such potentially-throwing C library functions, and these C++ library functions are not documented as potentially throwing exceptions. These functions do start to throw exceptions if you move from a multithreading environment that does not support cancellation to one that does. If that is the only problem, it seems you have a few possible alternative solutions: A) Modify the C++ library function implementations so that they call "safe" versions of the C library functions, that are not cancellation points. This could be done by either changing the sources of the C++ libraries to call the safe versions by name, or by using macro or linker-level renaming to do the substitution. B) Change the documentations (maybe standards) for the affected C++ library functions, to specify that they may throw thread cancellation when cancellation is enabled. C) Disable cancellation by default in all C++ threads, and warn users that enabling cancellation may cause some functions, not documented as throwing, to throw cancellation. Beyond that, I get the impression this group is asking for something that is impossible because it is self-contradictory, i.e., for a way to (1) allow cancellation, (2) ensure (1) cancellation cannot be ignored for long or lost, (3) not allow throwing of cancellation from certain library calls, (4) not make any change to existing code for those library functions (which were written without cancellation in mind). --Ted From dave at boost-consulting.com Sat Jan 10 16:15:45 2004 From: dave at boost-consulting.com (David Abrahams) Date: Sat, 10 Jan 2004 11:15:45 -0500 Subject: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040110153950.GD13609@diablo.name> Message-ID: Ted Baker writes: >> Except for the fact that we have normally-non-throwing functions that >> have started to throw, I don't think anyone's disagreeing with you. > > This still perplexes me. How does a "normally-non-throwing function" > "start to throw"? You port the code which calls the function into an environment which changes the rules about what can throw exceptions. -- Dave Abrahams Boost Consulting www.boost-consulting.com From wil at bogo.xs4all.nl Sat Jan 10 21:19:41 2004 From: wil at bogo.xs4all.nl (Wil Evers) Date: Sat, 10 Jan 2004 22:19:41 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFD7DD5.9090801@bogo.xs4all.nl> <3FFD8046.3000702@hp.com> <3FFE43BB.6040809@bogo.xs4all.nl> <20040109082036.GA31026@redhat.com> Message-ID: <40006C6D.5090707@bogo.xs4all.nl> Richard Henderson wrote: > On Fri, Jan 09, 2004 at 07:01:31AM +0100, Wil Evers wrote: > >>A second exception escaping from a destructor called while unwinding >>the stack because of some earlier exception will result in program >>termination. >> >>If catching (and not rethrowing) this second exception breaks the >>cancellation machinery, then it is the cancellation machinery - and not >>the program in question - that is broken. In other words: we need a way >>to prevent this scenario. > > It would be trivial to adjust the c++ runtime to disable cancelation > while exceptions are in flight. That avoids that scenario entirely. It obviously does. But does that imply we don't need 'catch-and-dont-rethrow' blocks in destructors anymore? Please consider this example: remote_resource_holder::~remote_resource_holder() { remote_resource->release(); } Disabling cancellation while exceptions are 'in flight' means we can't run into a double fault because of a cancellation request detected in remote_resource->release() while unwinding on behalf of some previously raised exception. However, we may also be unwinding on behalf of some previously detected cancellation request, and it that case, a networking exception thrown from within remote_resource->release() *will* cause the program to terminate. Please note that this problem is not caused by the use of exceptions for handling cancellation; any other previously raised exception could lead to the same disaster scenario - which is why thinking about cancellation as some special kind of exception is unsufficient. Instead, this example serves to show how dangerous it is to let exceptions escape from destructors. Especially when we don't now which exceptions may be thrown from remote_resource->release(), about the only reasonable implementation of this destructor is: remote_resource_holder::~remote_resource_holder() { try { remote_resource->release(); } catch (...) { // We may try to log what happened, // but can't let the exception escape! } } I've noticed that some people on this list object to this design, but so far, I haven't seen an alternative. In other words, I think we should be prepared for threads that discard cancellation exceptions - which is why Nathan's 'sticky cancellation' makes sense to me. - Wil From boo at terekhov.de Sat Jan 10 22:27:29 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Sat, 10 Jan 2004 23:27:29 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFD7DD5.9090801@bogo.xs4all.nl> <3FFD8046.3000702@hp.com> <3FFE43BB.6040809@bogo.xs4all.nl> <20040109082036.GA31026@redhat.com> <40006C6D.5090707@bogo.xs4all.nl> Message-ID: <40007C51.FF744536@terekhov.de> Wil Evers wrote: [...] > remote_resource_holder::~remote_resource_holder() > { > try { > remote_resource->release(); > } catch (...) { > // We may try to log what happened, > // but can't let the exception escape! > } > } > > I've noticed that some people on this list object to this design, but so > far, I haven't seen an alternative. http://google.com/groups?selm=3E734A5F.8A7F5B56%40web.de http://google.com/groups?selm=3E76FB8A.A9C88386%40web.de regards, alexander. From baker at cs.fsu.edu Sat Jan 10 22:44:22 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Sat, 10 Jan 2004 17:44:22 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <40006C6D.5090707@bogo.xs4all.nl> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFD7DD5.9090801@bogo.xs4all.nl> <3FFD8046.3000702@hp.com> <3FFE43BB.6040809@bogo.xs4all.nl> <20040109082036.GA31026@redhat.com> <40006C6D.5090707@bogo.xs4all.nl> Message-ID: <20040110224422.GA19106@diablo.name> > Instead, this example serves to show how dangerous it is to let > exceptions escape from destructors. Especially when we don't now which > exceptions may be thrown from remote_resource->release(), about the only > reasonable implementation of this destructor is: > > remote_resource_holder::~remote_resource_holder() > { > try { > remote_resource->release(); > } catch (...) { > // We may try to log what happened, > // but can't let the exception escape! > } > } > > I've noticed that some people on this list object to this design, but so > far, I haven't seen an alternative. In other words, I think we should > be prepared for threads that discard cancellation exceptions - which is > why Nathan's 'sticky cancellation' makes sense to me. There is no real problem here. 1) Yes, you want to be able to catch all *local* exceptions, and not rethrow them, in destructors and cancellation cleanup routines. If you disable cancellation during the execution of such routines you will never have a cancellation exception that would be caught by the "catch" of your destructor example above. 2) This does not mean you can't disable the *generation* of a (new) cancellation exception during execution of such a destructor or cleanup routine. 3) This does not mean you will lose an old cancellation exception, if that is what caused you to execute the destructor or cleanup routine. On return from this routine, execution will continue along the path that took you into this routine, i.e., the processing of the old cancellation exception. 4) This does not mean you will lose a new cancellation exception, if you were executing the destructor or cleanup routine for some other reason. Whenever you next get to a point where cancellation is enabled again, the cancellation processing will begin. It does mean that you need to be able to keep track of nested cancellation-disabled sections, so that you can disable cancellation during the entire exception processing sequence, up until you are in the "catch", and you can *also* (nestedly) disable cancellation during destructors. --Ted From wil at bogo.xs4all.nl Sun Jan 11 08:20:37 2004 From: wil at bogo.xs4all.nl (Wil Evers) Date: Sun, 11 Jan 2004 09:20:37 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFD7DD5.9090801@bogo.xs4all.nl> <3FFD8046.3000702@hp.com> <3FFE43BB.6040809@bogo.xs4all.nl> <20040109082036.GA31026@redhat.com> <40006C6D.5090707@bogo.xs4all.nl> <20040110224422.GA19106@diablo.name> Message-ID: <40010755.30702@bogo.xs4all.nl> Ted Baker wrote: >>Instead, this example serves to show how dangerous it is to let >>exceptions escape from destructors. Especially when we don't now which >>exceptions may be thrown from remote_resource->release(), about the only >>reasonable implementation of this destructor is: >> >>remote_resource_holder::~remote_resource_holder() >>{ >> try { >> remote_resource->release(); >> } catch (...) { >> // We may try to log what happened, >> // but can't let the exception escape! >> } >>} >> >>I've noticed that some people on this list object to this design, but so >>far, I haven't seen an alternative. In other words, I think we should >>be prepared for threads that discard cancellation exceptions - which is >>why Nathan's 'sticky cancellation' makes sense to me. > > > There is no real problem here. > > 1) Yes, you want to be able to catch all *local* exceptions, and > not rethrow them, in destructors and cancellation cleanup routines. > > If you disable cancellation during the execution of such routines > you will never have a cancellation exception that would be caught > by the "catch" of your destructor example above. > > 2) This does not mean you can't disable the *generation* of a (new) > cancellation exception during execution of such a destructor or > cleanup routine. > > 3) This does not mean you will lose an old cancellation exception, > if that is what caused you to execute the destructor or cleanup > routine. On return from this routine, execution will continue > along the path that took you into this routine, i.e., the > processing of the old cancellation exception. > > 4) This does not mean you will lose a new cancellation exception, if > you were executing the destructor or cleanup routine for some other > reason. Whenever you next get to a point where cancellation is > enabled again, the cancellation processing will begin. > > It does mean that you need to be able to keep track of nested > cancellation-disabled sections, so that you can disable > cancellation during the entire exception processing sequence, up > until you are in the "catch", and you can *also* (nestedly) > disable cancellation during destructors. Please note that the example destructor above is also called on a regular (non-exceptional) exit from the block in which the remote_resource_holder lives. Since, in that case, there is no exception 'in flight', a cancellation exception thrown from remote_source->release() *will* be discarded, unless - in *all* such destructors - the code is changed to explictly disable cancellation. In other words, non-sticky cancellation will break existing code, and the replacement code will not be pretty. IMHO, this is highly undesirable - writing robust destructors is hard enough without these complications. - Wil From jason at redhat.com Sun Jan 11 19:05:01 2004 From: jason at redhat.com (Jason Merrill) Date: Sun, 11 Jan 2004 14:05:01 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <40006C6D.5090707@bogo.xs4all.nl> (Wil Evers's message of "Sat, 10 Jan 2004 22:19:41 +0100") References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFD7DD5.9090801@bogo.xs4all.nl> <3FFD8046.3000702@hp.com> <3FFE43BB.6040809@bogo.xs4all.nl> <20040109082036.GA31026@redhat.com> <40006C6D.5090707@bogo.xs4all.nl> Message-ID: On Sat, 10 Jan 2004 22:19:41 +0100, Wil Evers wrote: > Richard Henderson wrote: > >> On Fri, Jan 09, 2004 at 07:01:31AM +0100, Wil Evers wrote: >> >>>A second exception escaping from a destructor called while unwinding >>>the stack because of some earlier exception will result in program >>>termination. >>> >>> If catching (and not rethrowing) this second exception breaks the >>> cancellation machinery, then it is the cancellation machinery - and not >>> the program in question - that is broken. In other words: we need a way >>> to prevent this scenario. >> It would be trivial to adjust the c++ runtime to disable cancelation >> while exceptions are in flight. That avoids that scenario entirely. > > It obviously does. But does that imply we don't need > 'catch-and-dont-rethrow' blocks in destructors anymore? No, of course not; we can get double faults in non-threaded programs. It does, however, mean that a cancellation exception will never be caught by such a block. >[...] > I've noticed that some people on this list object to this design, but so > far, I haven't seen an alternative. In other words, I think we should be > prepared for threads that discard cancellation exceptions - which is why > Nathan's 'sticky cancellation' makes sense to me. I agree that cancellation should be sticky, and it is in my model as well as Nathan's. But I don't think that this particular example would discard cancellation exceptions. Jason From wil at bogo.xs4all.nl Sun Jan 11 21:03:58 2004 From: wil at bogo.xs4all.nl (Wil Evers) Date: Sun, 11 Jan 2004 22:03:58 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFD7DD5.9090801@bogo.xs4all.nl> <3FFD8046.3000702@hp.com> <3FFE43BB.6040809@bogo.xs4all.nl> <20040109082036.GA31026@redhat.com> <40006C6D.5090707@bogo.xs4all.nl> Message-ID: <4001BA3E.7010100@bogo.xs4all.nl> Jason Merrill wrote: > On Sat, 10 Jan 2004 22:19:41 +0100, Wil Evers wrote: > >>Richard Henderson wrote: >> >>>On Fri, Jan 09, 2004 at 07:01:31AM +0100, Wil Evers wrote: >>> >>>>A second exception escaping from a destructor called while unwinding >>>>the stack because of some earlier exception will result in program >>>>termination. >>>> >>>>If catching (and not rethrowing) this second exception breaks the >>>>cancellation machinery, then it is the cancellation machinery - and not >>>>the program in question - that is broken. In other words: we need a way >>>>to prevent this scenario. >>> >>> It would be trivial to adjust the c++ runtime to disable cancelation >>>while exceptions are in flight. That avoids that scenario entirely. >> >>It obviously does. But does that imply we don't need >>'catch-and-dont-rethrow' blocks in destructors anymore? > > No, of course not; we can get double faults in non-threaded programs. It > does, however, mean that a cancellation exception will never be caught by > such a block. > >>[...] >>I've noticed that some people on this list object to this design, but so >>far, I haven't seen an alternative. In other words, I think we should be >>prepared for threads that discard cancellation exceptions - which is why >>Nathan's 'sticky cancellation' makes sense to me. > > I agree that cancellation should be sticky, and it is in my model as well > as Nathan's. But I don't think that this particular example would discard > cancellation exceptions. I think it will. Even if cancellation is disabled while exceptions are in flight, a 'catch-everything-and-never-rethrow' block in a destructor will discard a cancellation exception when that destructor is triggered by a regular (non-exceptional, non-unwinding) exit from a block scope. Call me paranoid, but as a C++ programmer, the thing that scares me most in the discussions on this list is a certain (perceived?) pressure to open the door for letting exceptions escape from destructors. Speaking for myself, all I can say is that such a license would break nearly every piece of non-trivial code I've written over the last eight years or so. - Wil From fjh at cs.mu.oz.au Mon Jan 12 01:02:13 2004 From: fjh at cs.mu.oz.au (Fergus Henderson) Date: Mon, 12 Jan 2004 12:02:13 +1100 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> Message-ID: <20040112010213.GB14371@jupiter.cs.mu.oz.au> On 09-Jan-2004, David Abrahams wrote: > Dave Butenhof writes: > > > Lessee here, Nathan's model I > > believe could be trivially characterized as "return failure status on > > any cancellation point until you hit a C++ function allowed to > > throw". This would apparently disable any cleanup operations involving > > cancellation points (they'd fail) at least prior to the first throw > > point -- and it's not precisely clear how the "pending cancel" state > > is to be managed even beyond that point. While it might eventually > > lead to cancellation, it won't reliably perform cleanup. > > I'm having trouble understanding. How does a "pending cancel state" > perform cleanup? And what do "reliably" and "cleanup" mean to you? I think "it" in the last sentence quote above refers to Nathan's model, not to the pending cancel state. The problem is that cleanup -- e.g. execution of C++ destructors on stack unwinding -- may involve calls to I/O routines that are themselves cancellation points. If these routines fail, then the cleanup functions will not achieve their intended purpose; they will not be able to release the resources that the thread holds. Nathan's model implies that they will fail. > > It doesn't matter. There are many problems lurking here, and only one > > of the more obvious is that this attempt to avoid breaking C code that > > depends on 'if (error) {cleanup(); return error;}' will still break if > > 'cleanup()' depends on any cancellation points... which in fact is > > quite likely if 'error' originates from a cancellation point (e.g., > > I/O). > > In what sense will it break? In this example, the function "cleanup()" may need to do I/O to properly release the resources that the thread holds, but this I/O will fail. > > (It might actually be > > better if the pending cancel wasn't "sticky", > > You lost me. What's "sticky"? When a thread gets a cancellation request, the first cancellation point encountered thereafter will act on that request (by unwinding the stack, or in Nathan's model by returning an error return status and setting errno = ECANCELLED). If another cancellation point is encountered after that, and there has not been another cancellation request, then the cancellation point will normally not have any effect. With "sticky" cancels, after a cancellation request, *every* subsequent cancellation point will act on the cancel (by unwinding the stack, or failing with ECANCELLED). "stick" cancels make it difficult/impossible to clean up properly, since cleaning up may involve doing I/O, and in particular by calling functions that Posix specifies are cancellation points. These functions will fail to do the I/O, because they will instead attempt to act on the cancellation again (i.e. re-raising the cancellation exception or failing with ECANCELLED). -- Fergus Henderson | "I have always known that the pursuit The University of Melbourne | of excellence is a lethal habit" WWW: | -- the last words of T. S. Garp. From dave at boost-consulting.com Mon Jan 12 01:46:24 2004 From: dave at boost-consulting.com (David Abrahams) Date: Sun, 11 Jan 2004 20:46:24 -0500 Subject: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> Message-ID: Fergus Henderson writes: > When a thread gets a cancellation request, the first > cancellation point encountered thereafter will act on that request > (by unwinding the stack, or in Nathan's model by returning an error > return status and setting errno = ECANCELLED). If another > cancellation point is encountered after that, and there has not > been another cancellation request, then the cancellation point > will normally not have any effect. > > With "sticky" cancels, after a cancellation request, > *every* subsequent cancellation point will act on the cancel > (by unwinding the stack, or failing with ECANCELLED). > > "stick" cancels make it difficult/impossible to clean up properly, > since cleaning up may involve doing I/O, and in particular by calling > functions that Posix specifies are cancellation points. These functions > will fail to do the I/O, because they will instead attempt to act on > the cancellation again (i.e. re-raising the cancellation exception > or failing with ECANCELLED). OK, thanks for the explanation. Sounds like an argument for Jason's exceptions that re-assert cancel when they're destroyed. -- Dave Abrahams Boost Consulting www.boost-consulting.com From boo at terekhov.de Mon Jan 12 10:34:30 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Mon, 12 Jan 2004 11:34:30 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> Message-ID: <40027836.7B9D03BC@terekhov.de> David Abrahams wrote: > > Fergus Henderson writes: > > > When a thread gets a cancellation request, the first > > cancellation point encountered thereafter will act on that request > > (by unwinding the stack, or in Nathan's model by returning an error > > return status and setting errno = ECANCELLED). If another > > cancellation point is encountered after that, and there has not > > been another cancellation request, then the cancellation point > > will normally not have any effect. > > > > With "sticky" cancels, after a cancellation request, > > *every* subsequent cancellation point will act on the cancel > > (by unwinding the stack, or failing with ECANCELLED). > > > > "stick" cancels make it difficult/impossible to clean up properly, In the current POSIX/C++ environment one must disable/restore-old cancellation state in the destructors that call POSIX cancellation points anyway (I mean "general cancel-safe library" stuff). That does ensure proper cleanup. > > since cleaning up may involve doing I/O, and in particular by calling > > functions that Posix specifies are cancellation points. These functions > > will fail to do the I/O, because they will instead attempt to act on > > the cancellation again (i.e. re-raising the cancellation exception > > or failing with ECANCELLED). > > OK, thanks for the explanation. Sounds like an argument for Jason's > exceptions that re-assert cancel when they're destroyed. Making broken code a bit less broken is hardly the right design rationale. Presuming that we'll get a standard named thread cancel request exception, his "fix" (with respect to broken C++ libraries) is nothing but catch(...) { /* ... no re-throw here ... */ try { throw; } catch(std::thread_cancel_request const &) { std::thread_enable_cancel(); // re-enable cancel std::thread_self().cancel(); // re-inject cancel request } catch(...) { } } (unless I'm just missing and/or misunderstanding something). I don't think that the standard should mandate that. The standard should mandate mandatory 2-phase ES and intelligent cancellation points plus async-cancel regions, of course. regards, alexander. From fjh at cs.mu.oz.au Mon Jan 12 11:28:21 2004 From: fjh at cs.mu.oz.au (Fergus Henderson) Date: Mon, 12 Jan 2004 22:28:21 +1100 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <40027836.7B9D03BC@terekhov.de> References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <40027836.7B9D03BC@terekhov.de> Message-ID: <20040112112821.GD27442@jupiter.cs.mu.oz.au> On 12-Jan-2004, Alexander Terekhov wrote: > > Fergus Henderson writes: > > > > > When a thread gets a cancellation request, the first > > > cancellation point encountered thereafter will act on that request > > > (by unwinding the stack, or in Nathan's model by returning an error > > > return status and setting errno = ECANCELLED). If another > > > cancellation point is encountered after that, and there has not > > > been another cancellation request, then the cancellation point > > > will normally not have any effect. > > > > > > With "sticky" cancels, after a cancellation request, > > > *every* subsequent cancellation point will act on the cancel > > > (by unwinding the stack, or failing with ECANCELLED). > > > > > > "sticky" cancels make it difficult/impossible to clean up properly, > > In the current POSIX/C++ environment one must disable/restore-old > cancellation state in the destructors that call POSIX cancellation > points anyway (I mean "general cancel-safe library" stuff). That > does ensure proper cleanup. Yes, to the extent that there _is_ a de facto standard current POSIX/C++ environment, it does not use sticky cancels, and so it is possible to do proper cleanup. > David Abrahams wrote: > > OK, thanks for the explanation. Sounds like an argument for Jason's > > exceptions that re-assert cancel when they're destroyed. > > Making broken code a bit less broken is hardly the right design > rationale. Presuming that we'll get a standard named thread cancel > request exception, I hope that we get a standard base class for thread cancel request exceptions, and that we provide a way for code which requests a thread cancellation to specify an object of any class derived from this standard base class, and have that object (or a copy of it) be the object thrown in the cancelled thread. > his "fix" (with respect to broken C++ libraries) > is nothing but > > catch(...) { > > /* ... no re-throw here ... */ > > try { > throw; > } > catch(std::thread_cancel_request const &) { > std::thread_enable_cancel(); // re-enable cancel > std::thread_self().cancel(); // re-inject cancel request > } > catch(...) { } > > } There's a big difference between doing that automatically and doing it manually. If it is done automatically, you can be sure that the programmer won't accidentally forget to do it. -- Fergus Henderson | "I have always known that the pursuit The University of Melbourne | of excellence is a lethal habit" WWW: | -- the last words of T. S. Garp. From baker at cs.fsu.edu Mon Jan 12 11:35:34 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Mon, 12 Jan 2004 06:35:34 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <40010755.30702@bogo.xs4all.nl> References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFD7DD5.9090801@bogo.xs4all.nl> <3FFD8046.3000702@hp.com> <3FFE43BB.6040809@bogo.xs4all.nl> <20040109082036.GA31026@redhat.com> <40006C6D.5090707@bogo.xs4all.nl> <20040110224422.GA19106@diablo.name> <40010755.30702@bogo.xs4all.nl> Message-ID: <20040112113534.GA5193@diablo.name> > Please note that the example destructor above is also called on a > regular (non-exceptional) exit from the block in which the > remote_resource_holder lives. > > Since, in that case, there is no exception 'in flight', a cancellation > exception thrown from remote_source->release() *will* be discarded, There can be no cancellation exception thrown in this case, because cancellation is disabled during the destructor execution. (This what is required in Ada, by the way.) > unless - in *all* such destructors - the code is changed to explictly > disable cancellation. Yes. You must diable cancellation in *all* destructors. > In other words, non-sticky cancellation will break existing code, and > the replacement code will not be pretty. IMHO, this is highly > undesirable - writing robust destructors is hard enough without these > complications. Disabling cancellation in all destructors can be done automagically by the compiler, so it will not break existing code. The code will only need to be recompiled. --Ted From baker at cs.fsu.edu Mon Jan 12 11:37:39 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Mon, 12 Jan 2004 06:37:39 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <4001BA3E.7010100@bogo.xs4all.nl> References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFD7DD5.9090801@bogo.xs4all.nl> <3FFD8046.3000702@hp.com> <3FFE43BB.6040809@bogo.xs4all.nl> <20040109082036.GA31026@redhat.com> <40006C6D.5090707@bogo.xs4all.nl> <4001BA3E.7010100@bogo.xs4all.nl> Message-ID: <20040112113739.GB5193@diablo.name> > I think it will. Even if cancellation is disabled while exceptions are > in flight, a 'catch-everything-and-never-rethrow' block in a destructor > will discard a cancellation exception when that destructor is triggered > by a regular (non-exceptional, non-unwinding) exit from a block scope. That is why you need to disable cancellation during the execution of destructors (and C cancellation cleanup handlers). > Call me paranoid, but as a C++ programmer, the thing that scares me most > in the discussions on this list is a certain (perceived?) pressure to > open the door for letting exceptions escape from destructors. Speaking > for myself, all I can say is that such a license would break nearly > every piece of non-trivial code I've written over the last eight years > or so. There is no need to let exceptions escape from destructors if you disable cancellation during desctructor execution. > - Wil --Ted From baker at cs.fsu.edu Mon Jan 12 11:40:14 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Mon, 12 Jan 2004 06:40:14 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <20040112010213.GB14371@jupiter.cs.mu.oz.au> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> Message-ID: <20040112114014.GC5193@diablo.name> > The problem is that cleanup -- e.g. execution of C++ destructors on > stack unwinding -- may involve calls to I/O routines that are themselves > cancellation points. If these routines fail, then the cleanup functions > will not achieve their intended purpose; they will not be able to release > the resources that the thread holds. Nathan's model implies that they > will fail. This cannot happen if cancellation is automagically disabled during execution of (all) destructors, along with the stack unwinding code that is performed during exception processing. --Ted From boo at terekhov.de Mon Jan 12 11:56:20 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Mon, 12 Jan 2004 12:56:20 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <40027836.7B9D03BC@terekhov.de> <20040112112821.GD27442@jupiter.cs.mu.oz.au> Message-ID: <40028B64.E906234B@terekhov.de> Fergus Henderson wrote: [...] > > In the current POSIX/C++ environment one must disable/restore-old > > cancellation state in the destructors that call POSIX cancellation > > points anyway (I mean "general cancel-safe library" stuff). That > > does ensure proper cleanup. > > Yes, to the extent that there _is_ a de facto standard current POSIX/C++ > environment, it does not use sticky cancels, and so it is possible to > do proper cleanup. I suppose that even under the Nathan's "sticky cancels model", cancellation points are meant to be idle (with respect to cancellation) when cancellation state is set to PTHREAD_CANCEL_DISABLE. > > > David Abrahams wrote: > > > OK, thanks for the explanation. Sounds like an argument for Jason's > > > exceptions that re-assert cancel when they're destroyed. > > > > Making broken code a bit less broken is hardly the right design > > rationale. Presuming that we'll get a standard named thread cancel > > request exception, > > I hope that we get a standard base class for thread cancel request > exceptions, and that we provide a way for code which requests a thread > cancellation to specify an object of any class derived from this standard > base class, and have that object (or a copy of it) be the object thrown > in the cancelled thread. This would extend the current POSIX model and would also mean that "C++ version" of pthread_cancel() and cancel request delivery "in C++" would need to have "release" and "acquire" memory synchronization semantics respectively. Well, I guess that I should now better send my forthcoming reply to your recent private message to the list. Oder? ;-) > > > his "fix" (with respect to broken C++ libraries) > > is nothing but > > > > catch(...) { > > > > /* ... no re-throw here ... */ > > > > try { > > throw; > > } > > catch(std::thread_cancel_request const &) { > > std::thread_enable_cancel(); // re-enable cancel > > std::thread_self().cancel(); // re-inject cancel request > > } > > catch(...) { } > > > > } > > There's a big difference between doing that automatically > and doing it manually. If it is done automatically, you > can be sure that the programmer won't accidentally forget > to do it. I don't wont it to be done automatically because there is "just a few" valid scenarios where it would make sense to do it... and broken C++ libraries ala iostream-stuff is not one of them, IMO. regards, alexander. From boo at terekhov.de Mon Jan 12 12:05:47 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Mon, 12 Jan 2004 13:05:47 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> Message-ID: <40028D9B.988E8C81@terekhov.de> Ted Baker wrote: > > > The problem is that cleanup -- e.g. execution of C++ destructors on > > stack unwinding -- may involve calls to I/O routines that are themselves > > cancellation points. If these routines fail, then the cleanup functions > > will not achieve their intended purpose; they will not be able to release > > the resources that the thread holds. Nathan's model implies that they > > will fail. > > This cannot happen if cancellation is automagically disabled during > execution of (all) destructors, along with the stack unwinding code > that is performed during exception processing. Except that it would pretty much preclude SAFE exploitation of cancellation in the destructors (stuff ala "~temp_dataset()" which can simply catch cancel request exception and "re-enable/re-inject"). Burning processing cycles on cancel enable/disable is another issue here, BTW. Intelligent cancellation that would check the dynamic context (2-pase ES with exception specs acting like "fences" NOT causing unnecessary unwinding) would work much better, I guess. regards, alexander. From boo at terekhov.de Mon Jan 12 12:56:58 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Mon, 12 Jan 2004 13:56:58 +0100 Subject: [c++-pthreads] thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> <3FFD6355.80200@hp.com> <3FFD6CA8.B5CEF27C@terekhov.de> <20040108213655.GB31384@redhat.com> <3FFEAA8C.7085265B@terekhov.de> <20040111211904.GA13141@jupiter.cs.mu.oz.au> <400278B8.112140FB@terekhov.de> <20040112111321.GA4300@jupiter.cs.mu.oz.au> Message-ID: <4002999A.485DF416@terekhov.de> Fergus Henderson wrote: > > On 12-Jan-2004, Alexander Terekhov wrote: > > Fergus Henderson wrote: > > > > > > On 09-Jan-2004, Alexander Terekhov wrote: > > > > Richard Henderson wrote: > > > > > > > > > > On Thu, Jan 08, 2004 at 03:43:52PM +0100, Alexander Terekhov wrote: > > > > > > C++ aside for a moment, I have yet to see async-cancel-UNsafe > > > > > > implementation of, say, strlen(). Care to inspire me? TIA. > > > > > > > > > > I have yet to see an async-safe exception runtime. It's certainly > > > > > possible, but it requires the addition of lots of memory barriers > > > > > to the code. > > > > > > > > Huh? Let's talk about memory barriers next week, okay? > > > > > > > > http://www.terekhov.de/pthread_refcount_t/draft-edits.txt > > > > http://groups.google.com/groups?selm=3EEF38E8.311EEC77%40web.de > > > > http://www.terekhov.de/pthread_refcount_t/experimental/refcount.cpp > > > > > > What is the relevance of those URLs? > > > > This stuff is about memory barriers aka memory synchronization. > > Well, I know what memory barriers are. I doubt you know what "hoist" and "sink" memory barriers are. ;-) > Is there any reason why I should > bother to read the stuff at those URLs? In particular, do those URLs > have any relevance to Richard Henderson's comment, In POSIX, cancellation runs in the context of canceled thread. There's a context switch, so to speak. And, more importantly, cancellation (just like pthread_kill() signaling) does NOT have any memory synchronization effects at all. > or to how to combine > Posix threads and C++? Your latest comment implied to me that you want pthread_cancel() (and/or "the C++ version" of it) and cancellation request delivery be added to the "XBD 4.10" (in "POSIX.C++", at least). regards, alexander. From fjh at cs.mu.oz.au Mon Jan 12 13:05:46 2004 From: fjh at cs.mu.oz.au (Fergus Henderson) Date: Tue, 13 Jan 2004 00:05:46 +1100 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <20040112114014.GC5193@diablo.name> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> Message-ID: <20040112130546.GA29001@jupiter.cs.mu.oz.au> On 12-Jan-2004, Ted Baker wrote: > > The problem is that cleanup -- e.g. execution of C++ destructors on > > stack unwinding -- may involve calls to I/O routines that are themselves > > cancellation points. If these routines fail, then the cleanup functions > > will not achieve their intended purpose; they will not be able to release > > the resources that the thread holds. Nathan's model implies that they > > will fail. > > This cannot happen if cancellation is automagically disabled during > execution of (all) destructors, along with the stack unwinding code > that is performed during exception processing. If all cleanup is done via destructors (or pthread_cleanup_push, which can be handled similarly), you are right. But recall that in Nathan's model, Posix functions that return status values will indicate cancellation by returning a failure status and setting errno to ECANCELLED. In this situation, cleanup actions will often be performed directly, rather than via destructors. Once some code has called a cancellation point that fails with ECANCELLED, there is no way for the implementation to tell whether any further code which is executed is cleanup code or just a resumption of the thread's main loop. For cleanup code, cancellation must be disabled, so that the cleanup code can perform I/O to release resources. But for any long-running non-cleanup code, cancellation must be reenabled; otherwise, we would be failing to provide the limited assurance that cancellation will not be accidentally ignored, which stickiness of cancellation was intended to ensure. Since the implementation can't tell, the code must be modified to explicitly enable and/or disable cancellation at appropriate points. Nathan's model was intended to preserve existing code that propagated error returns, and that did not expect functions like read() or printf() to throw exceptions. However, because of the problems described above, such existing code would still need to be changed to make cleanup work properly. So I don't think Nathan's model achieves his intended aim. -- Fergus Henderson | "I have always known that the pursuit The University of Melbourne | of excellence is a lethal habit" WWW: | -- the last words of T. S. Garp. From boo at terekhov.de Mon Jan 12 13:20:42 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Mon, 12 Jan 2004 14:20:42 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> Message-ID: <40029F2A.7C13BD76@terekhov.de> Fergus Henderson wrote: [...] > > This cannot happen if cancellation is automagically disabled during > > execution of (all) destructors, along with the stack unwinding code > > that is performed during exception processing. > > If all cleanup is done via destructors (or pthread_cleanup_push, > which can be handled similarly), you are right. AFAIK, POSIX doesn't disable cancellation when thread cleanup handler is invoked by pthread_cleanup_pop() call with a non-zero execute argument (cancellation is disabled when the thread exits [that is, calls pthread_exit()] or acts upon a cancellation request delivery). I do it "manually". regards, alexander. From fjh at cs.mu.oz.au Mon Jan 12 13:36:56 2004 From: fjh at cs.mu.oz.au (Fergus Henderson) Date: Tue, 13 Jan 2004 00:36:56 +1100 Subject: [c++-pthreads] thread-safety definition In-Reply-To: <4002999A.485DF416@terekhov.de> References: <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> <3FFD6355.80200@hp.com> <3FFD6CA8.B5CEF27C@terekhov.de> <20040108213655.GB31384@redhat.com> <3FFEAA8C.7085265B@terekhov.de> <20040111211904.GA13141@jupiter.cs.mu.oz.au> <400278B8.112140FB@terekhov.de> <20040112111321.GA4300@jupiter.cs.mu.oz.au> <4002999A.485DF416@terekhov.de> Message-ID: <20040112133656.GB29001@jupiter.cs.mu.oz.au> On 12-Jan-2004, Alexander Terekhov wrote: > Fergus Henderson wrote: > > > > On 12-Jan-2004, Alexander Terekhov wrote: > > > Fergus Henderson wrote: > > > > > > > On 09-Jan-2004, Alexander Terekhov wrote: > > > > > Richard Henderson wrote: > > > > > > > > > > > > On Thu, Jan 08, 2004 at 03:43:52PM +0100, Alexander Terekhov wrote: > > > > > > > C++ aside for a moment, I have yet to see async-cancel-UNsafe > > > > > > > implementation of, say, strlen(). Care to inspire me? TIA. > > > > > > > > > > > > I have yet to see an async-safe exception runtime. It's certainly > > > > > > possible, but it requires the addition of lots of memory barriers > > > > > > to the code. > > > > > > > > > > Huh? Let's talk about memory barriers next week, okay? > > > > > > > > > > http://www.terekhov.de/pthread_refcount_t/draft-edits.txt > > > > > http://groups.google.com/groups?selm=3EEF38E8.311EEC77%40web.de > > > > > http://www.terekhov.de/pthread_refcount_t/experimental/refcount.cpp > > > > > > > > What is the relevance of those URLs? > > > > > > This stuff is about memory barriers aka memory synchronization. The first URL was a 23-page slab of standardese, apparently "additions to the XBD 4.10 Memory Synchronization" and maybe some other stuff. There was no explanatory text to give any hint of the rationale for these changes. I see no point reading 23 pages of standardese if you can't be bothered telling us what you are trying to achieve with those 23 pages of standardese. > > or to how to combine > > Posix threads and C++? > > Your latest comment implied to me that you want pthread_cancel() (and/or > "the C++ version" of it) and cancellation request delivery be added to > the "XBD 4.10" (in "POSIX.C++", at least). If thread cancellation involves passing data between the canceler and the cancellee, as I have suggested would make sense for C++, then clearly some memory synchronization will be required. I haven't analyzed it in great detail, but at first glance I don't see that being a problem. After all, cancellation should be a very rare event. -- Fergus Henderson | "I have always known that the pursuit The University of Melbourne | of excellence is a lethal habit" WWW: | -- the last words of T. S. Garp. From David.Butenhof at hp.com Mon Jan 12 13:41:39 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Mon, 12 Jan 2004 08:41:39 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> Message-ID: <4002A413.4090908@hp.com> Jason Merrill wrote: >On Fri, 09 Jan 2004 07:20:18 -0500, Dave Butenhof wrote: > > >>Jason's model, putting together the puzzle pieces from various messages >>here >> >> >My original formulation is at > > http://www.codesourcery.com/archives/c++-pthreads/msg00059.html > > >>, appears to be basing cancel on an ordinary ("finalizable") C++ >>exception, which would be raised by any and all cancellation points; but if >>the exception object were to be destroyed (by finalization), the "pending >>cancel" would (might?) be reasserted. I personally feel that it ought to be >>reasonable to finalize the cancel and continue operation. If that wasn't >>really what the application intended then it's an application error; but >>there are cases where it's arguably reasonable, and the capability falls >>naturally out of the model. >> >> >Too naturally, IMO. There seems to be a substantial amount of C++ code out >there which traps and finalizes all exceptions, some of it in the C++ >standard library I/O classes. I disagree with this design, and propose to >change it in the message cited above, but it's just one example. I don't >think it's reasonable for a cancellation request to be silently discarded >just because the thread was in the middle of doing some formatted output >when the cancel was received. > >I wouldn't object to a facility for explicitly abandoning the cancel, but I >think that allowing it to fall out of the model is far too fragile. > I think I disagree, at least philsophically, with the characterization of the model as "fragile". But I think I also understand what you mean; and the problem isn't with the model, but rather with the effect of that model on existing code that all-too-casually and agressively eats exceptions it doesn't understand. I think there are vanishingly few circumstances where a blind catch(...) without an unconditional re-throw should be considered "legitimate". If you don't completely understand what an exception means, you cannot claim to have completely recovered, and therefore cannot reasonably finalize propagation. (And when you catch anonymously, you can't possibly understand what they mean since you can't even identify them.) On the other hand, regardless of whether the semantics are meaningful or reasonable, the syntax is legal and apparently (unfortunately) in common use, so I can't dispute that you need to consider that. >>Besides, "reasserting cancel" in all the right places and no other places >>sounds like one of those projects that ends up being a lot more subtle >>and error-prone than anyone ever expects. ;-) >> >> >Assuming support for abanoning the cancel, why would it be more complex than > > pthread_cancel (pthread_self ()); > > While I didn't have anything specific in mind, just a general concern, one example that occurs to me is "catch(...) {...; throw;}". One must be careful about specifying the "cancelled" state of the thread here; it shouldn't be possible to consider the exception destroyed during the body of the catch prior to the re-throw. I don't know if the current wording in the standard would allow this interpretation: but for "traditional" exceptions that have no "state" it shouldn't really matter, and it seems plausible that the standard doesn't tie this down explicitly. (It's enough in practice that the 'throw' continues propagation "as if". Now, though, the distinction might become critical.) -- /--------------------[ 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 ]---/ From boo at terekhov.de Mon Jan 12 14:30:10 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Mon, 12 Jan 2004 15:30:10 +0100 Subject: [c++-pthreads] thread-safety definition References: <3FFD4034.7050903@hp.com> <1073567261.28360.258.camel@felix.inria.fr> <3FFD6355.80200@hp.com> <3FFD6CA8.B5CEF27C@terekhov.de> <20040108213655.GB31384@redhat.com> <3FFEAA8C.7085265B@terekhov.de> <20040111211904.GA13141@jupiter.cs.mu.oz.au> <400278B8.112140FB@terekhov.de> <20040112111321.GA4300@jupiter.cs.mu.oz.au> <4002999A.485DF416@terekhov.de> <20040112133656.GB29001@jupiter.cs.mu.oz.au> Message-ID: <4002AF72.C1CD6F98@terekhov.de> Fergus Henderson wrote: [...] > > > > > > http://www.terekhov.de/pthread_refcount_t/draft-edits.txt > > > > > > http://groups.google.com/groups?selm=3EEF38E8.311EEC77%40web.de > > > > > > http://www.terekhov.de/pthread_refcount_t/experimental/refcount.cpp > > > > > > > > > > What is the relevance of those URLs? > > > > > > > > This stuff is about memory barriers aka memory synchronization. > > The first URL was a 23-page slab of standardese, apparently "additions > to the XBD 4.10 Memory Synchronization" and maybe some other stuff. > There was no explanatory text to give any hint of the rationale for these > changes. I see no point reading 23 pages of standardese if you can't be > bothered telling us what you are trying to achieve with those 23 pages of > standardese. Well, thread-safe reference counting (hello tr1::shared_ptr and tr1::weak_ptr) aside for a moment, see http://groups.google.com/groups?selm=3EE75F28.76EC29B4%40web.de http://groups.google.com/groups?selm=3FAA2DC0.42F602DB%40web.de http://groups.google.com/groups?selm=3FD09072.BB1BEAF1%40web.de > > > > or to how to combine > > > Posix threads and C++? > > > > Your latest comment implied to me that you want pthread_cancel() (and/or > > "the C++ version" of it) and cancellation request delivery be added to > > the "XBD 4.10" (in "POSIX.C++", at least). > > If thread cancellation involves passing data between the canceler and the > cancellee, as I have suggested would make sense for C++, then clearly > some memory synchronization will be required. I haven't analyzed it > in great detail, but at first glance I don't see that being a problem. > After all, cancellation should be a very rare event. Agreed. Oh, BTW, with respect to pthread_kill()... perhaps you might want (reason: sort of "similar issue" with respect to msync plus cancellation/errno silliness in the context of async handlers) to take a look at the following thread: http://groups.google.com/groups?threadm=3F60A3B0.7E035CBD%40web.de What do you think? regards, alexander. From boo at terekhov.de Mon Jan 12 15:28:28 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Mon, 12 Jan 2004 16:28:28 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> Message-ID: <4002BD1C.A034BAD0@terekhov.de> Dave Butenhof wrote: [...] > While I didn't have anything specific in mind, just a general concern, > one example that occurs to me is "catch(...) {...; throw;}". One must be > careful about specifying the "cancelled" state of the thread here; it > shouldn't be possible to consider the exception destroyed during the > body of the catch prior to the re-throw. I don't know if the current > wording in the standard would allow this interpretation: .... That's 15.1/6 and 15.1/7 (no changes in TC1-2003 edition, AFAIK). 6 A throw-expression with no operand rethrows the exception being handled. The exception is reactivated with the existing temporary; no new temporary exception object is created. The exception is no longer considered to be caught; therefore, the value of uncaught_exception() will again be true. [Example: code that must be executed because of an exception yet cannot completely handle the exception can be written like this: try { // ... } catch (...) { // catch all exceptions // respond (partially) to exception throw; // pass the exception to some // other handler } ?end example] 7 The exception thrown is the one most recently caught and not finished. An exception is considered caught when initialization is complete for the formal parameter of the corresponding catch clause, or when terminate() or unexpected() is entered due to a throw. An exception is considered finished when the corresponding catch clause exits or when unexpected() exits after being entered due to a throw. regards, alexander. From boo at terekhov.de Mon Jan 12 15:43:43 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Mon, 12 Jan 2004 16:43:43 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4002BD1C.A034BAD0@terekhov.de> Message-ID: <4002C0AF.266D6744@terekhov.de> Alexander Terekhov wrote: > > Dave Butenhof wrote: > [...] > > While I didn't have anything specific in mind, just a general concern, > > one example that occurs to me is "catch(...) {...; throw;}". One must be > > careful about specifying the "cancelled" state of the thread here; it > > shouldn't be possible to consider the exception destroyed during the > > body of the catch prior to the re-throw. I don't know if the current > > wording in the standard would allow this interpretation: .... > > That's 15.1/6 and 15.1/7 (no changes in TC1-2003 edition, AFAIK). ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > > 6 A throw-expression with no operand rethrows the exception being > handled. The exception is reactivated with the existing temporary; > no new temporary exception object is created. The exception is no > longer considered to be caught; therefore, the value of > uncaught_exception() will again be true. [Example: code that must ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ That's not necessarily true (it depends), BTW. > be executed because of an exception yet cannot completely handle > the exception can be written like this: > > try { > // ... > } > catch (...) { // catch all exceptions > > // respond (partially) to exception > > throw; // pass the exception to some > // other handler > } > > ?end example] http://groups.google.com/groups?selm=3D75E040.469A103E%40web.de regards, alexander. From r-smith at ihug.co.nz Mon Jan 12 15:52:19 2004 From: r-smith at ihug.co.nz (Ross Smith) Date: Tue, 13 Jan 2004 04:52:19 +1300 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <4002A413.4090908@hp.com> References: <1073556051.28360.195.camel@felix.inria.fr> <4002A413.4090908@hp.com> Message-ID: <200401130452.19605.r-smith@ihug.co.nz> On Tuesday 13 January 2004 02:41, Dave Butenhof wrote: > > I think I disagree, at least philsophically, with the > characterization of the model as "fragile". But I think I also > understand what you mean; and the problem isn't with the model, but > rather with the effect of that model on existing code that > all-too-casually and agressively eats exceptions it doesn't > understand. I think there are vanishingly few circumstances where a > blind catch(...) without an unconditional re-throw should be > considered "legitimate". I'm beginning to get the same worried feeling Wil Evers expressed a few messages back. It sounds as though a lot of people are seriously considering the idea of allowing exceptions to escape from destructors. Dave, while I have the greatest respect for your knowledge and understanding of threads, I think the above shows that you don't really understand C++. Please try to understand that it is _absolutely vital_ that destructors never be allowed to throw under any circumstances. If a destructor calls a function that may throw some unknown exception (a very common case, especially in template classes whose destructors will often call member functions of some arbitrary user-supplied type), the call _must_ be wrapped in a catch-and-discard-all block. At most you can log the error somewhere. Any attempt to standardise a solution that doesn't take "Destructors Must Not Throw" as axiomatic is simply going to be a non-starter as far as C++ programmers are concerned. I'd really like to see this list explicitly address this issue rather than continuing to casually talk about uncatchable exceptions and similar horrors. -- Ross Smith ......... r-smith at ihug.co.nz ......... Auckland, New Zealand "This world is like a burnt steak: small, tough, and the chips are always stacked against you." -- Mike From wil at bogo.xs4all.nl Mon Jan 12 16:04:14 2004 From: wil at bogo.xs4all.nl (Wil Evers) Date: Mon, 12 Jan 2004 17:04:14 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> Message-ID: <4002C57E.8030805@bogo.xs4all.nl> Dave Butenhof wrote: > But I think I also understand what you mean; > and the problem isn't with the model, but rather with the effect of that > model on existing code that all-too-casually and agressively eats > exceptions it doesn't understand. I think there are vanishingly few > circumstances where a blind catch(...) without an unconditional re-throw > should be considered "legitimate". If you don't completely understand > what an exception means, you cannot claim to have completely recovered, > and therefore cannot reasonably finalize propagation. (And when you > catch anonymously, you can't possibly understand what they mean since > you can't even identify them.) Let me try again. Doing a blind catch(...) without an unconditional rethrow is not always a matter of sloppiness. Sometimes, the only alternative is a crash - the C++ standard clearly says that a second exception escaping from a destructor invoked by the stack unwinding machinery will result in program termination. There is nothing the poor programmer can do about this. To reiterate: a few days ago, I posted the following example - remote_resource_holder::~remote_resource_holder() { remote_resource->release(); } Now what do you suggest I should do when an exception (say, a networking error, but it might just as well be a cancellation request) is thrown from somewhere within remote_resource->release()? The way I see it, either the original exception or the one that was just thrown at me will have to be finalized. - Wil From boo at terekhov.de Mon Jan 12 16:27:41 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Mon, 12 Jan 2004 17:27:41 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <4002A413.4090908@hp.com> <200401130452.19605.r-smith@ihug.co.nz> Message-ID: <4002CAFD.706264D8@terekhov.de> Ross Smith wrote: [...] > I'm beginning to get the same worried feeling Wil Evers expressed a few > messages back. It sounds as though a lot of people are seriously > considering the idea of allowing exceptions to escape from destructors. > > Dave, while I have the greatest respect for your knowledge and Hey Ross, Dave wrote: "One of those idle thoughts was that the concept of "cancel state" (enable or disable) might even be meaningless (or at least irrelevant) for a pure C++ binding. This might simply be a dynamic property of the current call tree; depending on the nested throw specs. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > understanding of threads, I think the above shows that you don't really > understand C++. Please try to understand that it is _absolutely vital_ > that destructors never be allowed to throw under any circumstances. If Then why don't you complain that destructors are allowed to throw anything by default instead of having implicit throw() ("by default") ES/throw spec. imposed on them? > a destructor calls a function that may throw some unknown exception (a > very common case, especially in template classes whose destructors will > often call member functions of some arbitrary user-supplied type), the > call _must_ be wrapped in a catch-and-discard-all block. Not really. regards, alexander. From David.Butenhof at hp.com Mon Jan 12 16:31:09 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Mon, 12 Jan 2004 11:31:09 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <200401130452.19605.r-smith@ihug.co.nz> References: <1073556051.28360.195.camel@felix.inria.fr> <4002A413.4090908@hp.com> <200401130452.19605.r-smith@ihug.co.nz> Message-ID: <4002CBCD.4040701@hp.com> Ross Smith wrote: >On Tuesday 13 January 2004 02:41, Dave Butenhof wrote: > > >>I think I disagree, at least philsophically, with the >>characterization of the model as "fragile". But I think I also >>understand what you mean; and the problem isn't with the model, but >>rather with the effect of that model on existing code that >>all-too-casually and agressively eats exceptions it doesn't >>understand. I think there are vanishingly few circumstances where a >>blind catch(...) without an unconditional re-throw should be >>considered "legitimate". >> >> >I'm beginning to get the same worried feeling Wil Evers expressed a few >messages back. It sounds as though a lot of people are seriously >considering the idea of allowing exceptions to escape from destructors. > >Dave, while I have the greatest respect for your knowledge and >understanding of threads, I think the above shows that you don't really >understand C++. > While I wouldn't dispute the statement that "I don't really understand C++", I absolutely do understand objects, encapsulation, modularity, exceptions, cancellation, and resource ownership; and what you're saying seems essentially "obvious and self-evident". I never argued, or suggested, or assumed, that exceptions would propagate out of destructors. But I'm talking about cancel, not exceptions. >Please try to understand that it is _absolutely vital_ >that destructors never be allowed to throw under any circumstances. If >a destructor calls a function that may throw some unknown exception (a >very common case, especially in template classes whose destructors will >often call member functions of some arbitrary user-supplied type), the >call _must_ be wrapped in a catch-and-discard-all block. At most you >can log the error somewhere. > >Any attempt to standardise a solution that doesn't take "Destructors >Must Not Throw" as axiomatic is simply going to be a non-starter as far >as C++ programmers are concerned. I'd really like to see this list >explicitly address this issue rather than continuing to casually talk >about uncatchable exceptions and similar horrors. > > This discussion is a swirling mix of different issues. They're often related, but please don't assume relations where they're not stated! Generally, any code that cannot raise (or propagate) an exception SHOULD NOT BE A CANCELLATION POINT. (Note to Dave Abrahams: Sorry, was that shout approved? ;-) ) In the context of cancellation, I don't care about whether exceptions can propagate out of destructors. As has already been said, while a destructor might be activated as part of handling and cleaning up from a cancel, that's not the same as propagating a local exception out of the destructor. If destructors are not cancellable, then no cancel exception can BEGIN within the destructor and the question of whether it could propagate out of the destructor is moot. (For what it's worth, I don't believe that they can or should.) Furthermore, a no-cancel destructor, regardless of whether it has a catch(...) with or without a re-throw, cannot possibily "eat" a cancel, because it can never catch a cancel that's not thrown within the dynamic scope of its try block. I suspect that destructors should be implicitly "no cancel zones". (Someone expressed a concern about runtime overhead; but the cost of enabling and disabling cancel CAN be made extremely low by collusion between C++ and the thread library. That's an implementation detail the standard doesn't necessarily need to explicitly address.) There might be cases where someone really wants an indefinite blocking operation in a destructor (perhaps to disconnect from a remote server), and might want that to be cancellable. I think that's a rare special case, and could probably be handled by manually enabling cancel over that range of code, catching/finalizing the cancel if it occurs, cleaning up, restoring the original "cancel disabled" state, and then calling pthread_cancel(pthread_self()) to re-pend the cancel. Perhaps (though it seems doubtful) this might be common enough to justify some special syntax to encapsulate the mechanism and avoid errors. -- /--------------------[ 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 ]---/ From baker at cs.fsu.edu Mon Jan 12 16:45:33 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Mon, 12 Jan 2004 11:45:33 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <40028D9B.988E8C81@terekhov.de> References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <40028D9B.988E8C81@terekhov.de> Message-ID: <20040112164533.GA10141@diablo.name> > > This cannot happen if cancellation is automagically disabled during > > execution of (all) destructors, along with the stack unwinding code > > that is performed during exception processing. > > Except that it would pretty much preclude SAFE exploitation of > cancellation in the destructors (stuff ala "~temp_dataset()" which > can simply catch cancel request exception and "re-enable/re-inject"). There is no need for a destructor to be aware of cancellation. Either the cancellation happens before the destructor, and the destructor executes as part of the cancellation processing (obliviously), or if the cancellation is called for while a destructor is executing (or an exception propagating) the cancellation processing begins after destructor has finished (or the exception has been handled successfully). The issues of delaying processing the cancellation is not significant here, because you will need to finish executing the destructor in any case. Even in the case of some (other) exception being in flight, it makes sense to allow the other exception processing to finish before cancelling (even if the other exception ends up terminating the thread). > Burning processing cycles on cancel enable/disable is another issue > here, BTW. Yes, but it need not be a *huge* amount of cycles. Protection against cancellation can be done via the equivalent of setting one bit in a thread descriptor. The cancellation protection bit is per-thread, and only accessed by the thread itself, and so does not require any form of lock to protect it. Likewise, a pending cancellation bit is per-thread and monotonic, so though it may be set from zero to one by any thread, it does not need a lock. Protection can be done very efficiency if the compiler knows how to get the current thread's descriptor efficiently. For example, on Solaris threads this can be done as a single store operation (there was, maybe still is, a register that always points to a per-thread location that can be used with appropriate offset to access such state). Unprotecting takes a bit longer, since you also need to check whether cancellation has been attempted while the protect bit was set, essentially a load, store, and conditional branch. You can also cut it down by doing only one disable/enable pair per *chain* of destructors, when you have a bunch of them being executed together. BTW, on general principles one generally would not like to have cancellation occur during operations that manipulate global storage, and in particular during execution of destructors. > Intelligent cancellation that would check the dynamic > context (2-pase ES with exception specs acting like "fences" NOT > causing unnecessary unwinding) would work much better, I guess. > > regards, > alexander. --Ted From David.Butenhof at hp.com Mon Jan 12 16:47:12 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Mon, 12 Jan 2004 11:47:12 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <4002C57E.8030805@bogo.xs4all.nl> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4002C57E.8030805@bogo.xs4all.nl> Message-ID: <4002CF90.7000503@hp.com> Wil Evers wrote: > Dave Butenhof wrote: > >> But I think I also understand what you mean; and the problem isn't >> with the model, but rather with the effect of that model on existing >> code that all-too-casually and agressively eats exceptions it doesn't >> understand. I think there are vanishingly few circumstances where a >> blind catch(...) without an unconditional re-throw should be >> considered "legitimate". If you don't completely understand what an >> exception means, you cannot claim to have completely recovered, and >> therefore cannot reasonably finalize propagation. (And when you catch >> anonymously, you can't possibly understand what they mean since you >> can't even identify them.) > > Let me try again. > > Doing a blind catch(...) without an unconditional rethrow is not > always a matter of sloppiness. Sometimes, the only alternative is a > crash - the C++ standard clearly says that a second exception escaping > from a destructor invoked by the stack unwinding machinery will result > in program termination. There is nothing the poor programmer can do > about this. > > To reiterate: a few days ago, I posted the following example - > > remote_resource_holder::~remote_resource_holder() > { > remote_resource->release(); > } > > Now what do you suggest I should do when an exception (say, a > networking error, but it might just as well be a cancellation request) > is thrown from somewhere within remote_resource->release()? The way I > see it, either the original exception or the one that was just thrown > at me will have to be finalized. Again, I don't think it should be possible to have cancellation within a destructor unless the destructor "does something special and unusual". In your example, you might want to make the release message cancellable, because the remote release might take a long time. Depends on the consequences of abandoning that particular remote resource, I suppose. If you did choose to make it cancellable, then, yes, you'd need to finalize the cancel within the destructor to avoid confusing propagation problems; which means that it needs to resurface later. This does tend to favor Jason's model. I remain vaguely uncomfortable with the notion of automatically re-pending the cancel at the right points, but I never intended to pretend to having any certain knowledge that there really are problems. ;-) -- /--------------------[ 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 ]---/ From baker at cs.fsu.edu Mon Jan 12 16:48:55 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Mon, 12 Jan 2004 11:48:55 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <20040112130546.GA29001@jupiter.cs.mu.oz.au> References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> Message-ID: <20040112164855.GB10141@diablo.name> > > This cannot happen if cancellation is automagically disabled during > > execution of (all) destructors, along with the stack unwinding code > > that is performed during exception processing. > > If all cleanup is done via destructors (or pthread_cleanup_push, > which can be handled similarly), you are right. > > But recall that in Nathan's model, Posix functions that return status > values will indicate cancellation by returning a failure status and > setting errno to ECANCELLED. In this situation, cleanup actions will I consider that a bad model. (1) It is inconsistent with current POSIX/Unix threads standards and practice, and so unlikely to get vendor buy-in. (2) It makes it too easy for an application to lose/forget/ignore cancellation. I'm assuming cancellation will *only* be reported via an exception. > often be performed directly, rather than via destructors. Once some > code has called a cancellation point that fails with ECANCELLED, there is > no way for the implementation to tell whether any further code which is > executed is cleanup code or just a resumption of the thread's main loop. > For cleanup code, cancellation must be disabled, so that the cleanup > code can perform I/O to release resources. But for any long-running > non-cleanup code, cancellation must be reenabled; otherwise, we would > be failing to provide the limited assurance that cancellation will not > be accidentally ignored, which stickiness of cancellation was intended > to ensure. > > Since the implementation can't tell, the code must be modified to > explicitly enable and/or disable cancellation at appropriate points. > > Nathan's model was intended to preserve existing code that propagated > error returns, and that did not expect functions like read() or printf() > to throw exceptions. However, because of the problems described above, > such existing code would still need to be changed to make cleanup work > properly. So I don't think Nathan's model achieves his intended aim. Yes. > -- > Fergus Henderson | "I have always known that the pursuit > The University of Melbourne | of excellence is a lethal habit" > WWW: | -- the last words of T. S. Garp. --Ted From r-smith at ihug.co.nz Mon Jan 12 17:42:20 2004 From: r-smith at ihug.co.nz (Ross Smith) Date: Tue, 13 Jan 2004 06:42:20 +1300 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <4002CBCD.4040701@hp.com> References: <1073556051.28360.195.camel@felix.inria.fr> <200401130452.19605.r-smith@ihug.co.nz> <4002CBCD.4040701@hp.com> Message-ID: <200401130642.20048.r-smith@ihug.co.nz> On Tuesday 13 January 2004 05:31, Dave Butenhof wrote: > > While I wouldn't dispute the statement that "I don't really > understand C++", I absolutely do understand objects, encapsulation, > modularity, exceptions, cancellation, and resource ownership; and > what you're saying seems essentially "obvious and self-evident". I > never argued, or suggested, or assumed, that exceptions would > propagate out of destructors. You may not have said so explicitly, but your repeated statements that catch-all-and-discard was almost always a bad idea certainly implied that. If you meant it to be qualified with "except in destructors", which I gather from the above is what you really meant, then I wish you'd said so instead of leaving us to get the wrong impression. > I suspect that destructors should be implicitly "no cancel zones". Thank you. We now appear to be in agreement :-) -- Ross Smith ......... r-smith at ihug.co.nz ......... Auckland, New Zealand "This world is like a burnt steak: small, tough, and the chips are always stacked against you." -- Mike From dave at boost-consulting.com Mon Jan 12 17:49:20 2004 From: dave at boost-consulting.com (David Abrahams) Date: Mon, 12 Jan 2004 12:49:20 -0500 Subject: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> Message-ID: Dave Butenhof writes: > I think I disagree, at least philsophically, with the characterization > of the model as "fragile". But I think I also understand what you > mean; and the problem isn't with the model, but rather with the effect > of that model on existing code that all-too-casually and agressively > eats exceptions it doesn't understand. I think there are vanishingly > few circumstances where a blind catch(...) without an unconditional > re-throw should be considered "legitimate". If you don't completely > understand what an exception means, you cannot claim to have > completely recovered, and therefore cannot reasonably finalize > propagation. The problems with catch(...) eating all exceptions are maybe not as bad as you think. As a matter of fact, there are vanishingly few exceptions that demand special recovery actions that wouldn't work for all other exceptions. Systems designed that way tend towards fragility. -- Dave Abrahams Boost Consulting www.boost-consulting.com From boo at terekhov.de Mon Jan 12 18:19:42 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Mon, 12 Jan 2004 19:19:42 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <40028D9B.988E8C81@terekhov.de> <20040112164533.GA10141@diablo.name> Message-ID: <4002E53E.8840B94B@terekhov.de> Ted Baker wrote: [...] > There is no need for a destructor to be aware of cancellation. my_file::~my_file() throw() { fclose(/*...*/); // doesn't throw; cancel is unexpected (even if thread // cancel state is equal here to PTHREAD_CANCEL_ENABLE) } void jason() throw() { std::enable_thread_cancel(); // re-enable cancel state std::thread_self().cancel(); // re-inject cancel request } my_other_file::~my_other_file() throw() { bool canceled_before = std::unwinding(this); try { if (canceled_before) jason(); fclose(/*...*/); // can (should "if (canceled before)") throw /*std::*/pthread_testcancel(); // fix "may occur" mess } catch (std::thread_cancel_request const &) { if (!canceled_before) jason(); } } Now, in your model with cancellation ALWAYS disabled while running destructors (not only when acting upon a cancel request delivery... thread_exit aside for a moment), I'd have to add enable/disable RAII guard object (and that's in addition to save-disable/restore internal "C++ runtime" managment cost)... and I'd probably have to get rid of ability to control cancelability of ~my_other_file() internal stuff via thread cancel state set outside. Oder? I really don't like that. [...] > > Burning processing cycles on cancel enable/disable is another issue > > here, BTW. > > Yes, but it need not be a *huge* amount of cycles. ... ES (throw specs) should have no runtime cost other than some extra info for the search phase. OTOH, save-disable/restore burns cycles (keystrokes aside for a moment) no matter whether you hit some cancellation point [or async-cancel region] *with cancel request pending* or not. regards, alexander. From jason at redhat.com Mon Jan 12 18:23:22 2004 From: jason at redhat.com (Jason Merrill) Date: Mon, 12 Jan 2004 13:23:22 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <4002A413.4090908@hp.com> (Dave Butenhof's message of "Mon, 12 Jan 2004 08:41:39 -0500") References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> Message-ID: On Mon, 12 Jan 2004 08:41:39 -0500, Dave Butenhof wrote: > Jason Merrill wrote: >>On Fri, 09 Jan 2004 07:20:18 -0500, Dave Butenhof wrote: >>>Besides, "reasserting cancel" in all the right places and no other places >>>sounds like one of those projects that ends up being a lot more subtle >>>and error-prone than anyone ever expects. ;-) >>> >>Assuming support for abanoning the cancel, why would it be more complex than >> pthread_cancel (pthread_self ()); >> > While I didn't have anything specific in mind, just a general concern, one > example that occurs to me is "catch(...) {...; throw;}". One must be > careful about specifying the "cancelled" state of the thread here; it > shouldn't be possible to consider the exception destroyed during the body > of the catch prior to the re-throw. That shouldn't be a problem; a C++ exception is not destroyed until the last active handler for that exception exits other than with a rethrow. Jason From jason at redhat.com Mon Jan 12 18:26:28 2004 From: jason at redhat.com (Jason Merrill) Date: Mon, 12 Jan 2004 13:26:28 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <4001BA3E.7010100@bogo.xs4all.nl> (Wil Evers's message of "Sun, 11 Jan 2004 22:03:58 +0100") References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFD7DD5.9090801@bogo.xs4all.nl> <3FFD8046.3000702@hp.com> <3FFE43BB.6040809@bogo.xs4all.nl> <20040109082036.GA31026@redhat.com> <40006C6D.5090707@bogo.xs4all.nl> <4001BA3E.7010100@bogo.xs4all.nl> Message-ID: On Sun, 11 Jan 2004 22:03:58 +0100, Wil Evers wrote: > Jason Merrill wrote: >> I agree that cancellation should be sticky, and it is in my model as well >> as Nathan's. But I don't think that this particular example would discard >> cancellation exceptions. > > I think it will. Even if cancellation is disabled while exceptions are in > flight, a 'catch-everything-and-never-rethrow' block in a destructor will > discard a cancellation exception when that destructor is triggered by a > regular (non-exceptional, non-unwinding) exit from a block scope. True. > Call me paranoid, but as a C++ programmer, the thing that scares me most in > the discussions on this list is a certain (perceived?) pressure to open the > door for letting exceptions escape from destructors. I don't think anyone is advocating that. I can see how you could infer that from general opposition to catch(...) { }, but destructors are a situation where it is appropriate, as the alternative is terminate(). This is why I want the cancel to return to pending status if the exception is destroyed--so that exception-safety code like this doesn't result in the unexpected loss of a cancel. Jason From austern at apple.com Mon Jan 12 18:29:00 2004 From: austern at apple.com (Matt Austern) Date: Mon, 12 Jan 2004 10:29:00 -0800 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <4002A413.4090908@hp.com> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> Message-ID: <30BBBA2F-452D-11D8-ABB6-00039390D9E0@apple.com> On Jan 12, 2004, at 5:41 AM, Dave Butenhof wrote: > I think I disagree, at least philsophically, with the characterization > of the model as "fragile". But I think I also understand what you > mean; and the problem isn't with the model, but rather with the effect > of that model on existing code that all-too-casually and agressively > eats exceptions it doesn't understand. I think there are vanishingly > few circumstances where a blind catch(...) without an unconditional > re-throw should be considered "legitimate". If you don't completely > understand what an exception means, you cannot claim to have > completely recovered, and therefore cannot reasonably finalize > propagation. (And when you catch anonymously, you can't possibly > understand what they mean since you can't even identify them.) On the > other hand, regardless of whether the semantics are meaningful or > reasonable, the syntax is legal and apparently (unfortunately) in > common use, so I can't dispute that you need to consider that. One legitimate use for that pattern is in mixed-language programming, when you write a little adapter to turn the error reporting mechanism used by the C++ part into an error reporting mechanism that the C part can understand. I think that's reasonable for most of the things exceptions are used for. It isn't reasonable for thread cancellation, because C needs to know about thread cancellation too. (Maybe this means that thread cancellation isn't quite "error reporting".) It looks to me like most of the recent discussion has come back to catch(...). I wonder if that's really the fundamental problem we have to solve? --Matt From jason at redhat.com Mon Jan 12 18:37:14 2004 From: jason at redhat.com (Jason Merrill) Date: Mon, 12 Jan 2004 13:37:14 -0500 Subject: [c++-pthreads] Re: cancellation points report failure In-Reply-To: (David Abrahams's message of "Tue, 06 Jan 2004 18:31:23 -0500") References: <2A8DB02E3018D411901B009027FD3A3F01CAAF6A@mchp905a.mch.sbs.de> <2E98D8A4-3251-11D8-B7E5-00039390D9E0@apple.com> <20031219194503.GA31795@tofu.dreamhost.com> <20031219205746.GA12322@diablo.name> <20031219225650.GA13735@diablo.name> <20031220045618.GA27836@tofu.dreamhost.com> <1071946483.25325.69.camel@doubledemon.codesourcery.com> Message-ID: On Tue, 06 Jan 2004 18:31:23 -0500, David Abrahams wrote: > AFAICT, the difference between your proposal and Nathan's, once > cancellation is thrown, is that your proposal can prevent > cancellation from being thrown unexpectedly in a catch block (**) > that's part of an unwind sequence? > > (**) From a destructor during unwinding also, but that could be > prevented using std::unhandled_exception(). As others have suggested, I think that we want to disable cancellation during unwinding. It's not clear to me that we also want to disable it during the execution of a catch block (operating on some non-cancellation exception). Jason From baker at cs.fsu.edu Mon Jan 12 19:06:51 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Mon, 12 Jan 2004 14:06:51 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <4002E53E.8840B94B@terekhov.de> References: <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <40028D9B.988E8C81@terekhov.de> <20040112164533.GA10141@diablo.name> <4002E53E.8840B94B@terekhov.de> Message-ID: <20040112190651.GA13854@diablo.name> On Mon, Jan 12, 2004 at 07:19:42PM +0100, Alexander Terekhov wrote: > Ted Baker wrote: > [...] > > There is no need for a destructor to be aware of cancellation. > > my_file::~my_file() throw() { > fclose(/*...*/); // doesn't throw; cancel is unexpected (even if thread > // cancel state is equal here to PTHREAD_CANCEL_ENABLE) > } Yes, in my model, cancel state would *always* be equal to PTHREAD_CANCEL_DISABLE in the my_file (and my_other_file) destructors, and so fclose() cannot throw cancellation. > void jason() throw() { > std::enable_thread_cancel(); // re-enable cancel state > std::thread_self().cancel(); // re-inject cancel request > } > my_other_file::~my_other_file() throw() { > bool canceled_before = std::unwinding(this); > try { > if (canceled_before) jason(); > fclose(/*...*/); // can (should "if (canceled before)") throw > /*std::*/pthread_testcancel(); // fix "may occur" mess > } > catch (std::thread_cancel_request const &) { > if (!canceled_before) jason(); > } > } What is the purpose of the above? That is, why are you intentionally shooting yourself in the foot, by enabling cancellation and then explicitly catching it in ~my_other_file()? I guess you have a reason, but I don't see it. This seems to just makes ugliness without any apparent benefit. You can just call fclose() by itself (without the thry...catch), since cancellation is disabled, as in ~my_file(). (BTW, I would prefer to see code that does this sort of explicit playing with cancellation state and catching of cancellation required to provide some sort of syntactic override, so that people are less likely to inadvertently shoot their feet.) > Now, in your model with cancellation ALWAYS disabled while running > destructors (not only when acting upon a cancel request delivery... > thread_exit aside for a moment), I'd have to add enable/disable RAII Please translate "RAII" for me? > guard object (and that's in addition to save-disable/restore internal Why do you say that would require you to add a guard object? > "C++ runtime" managment cost)... and I'd probably have to get rid of > ability to control cancelability of ~my_other_file() internal stuff > via thread cancel state set outside. Oder? I really don't like that. > [...] > > > Burning processing cycles on cancel enable/disable is another issue > > > here, BTW. > > > > Yes, but it need not be a *huge* amount of cycles. ... > > ES (throw specs) should have no runtime cost other than some extra > info for the search phase. OTOH, save-disable/restore burns cycles > (keystrokes aside for a moment) no matter whether you hit some > cancellation point [or async-cancel region] *with cancel request > pending* or not. The overhead for enabling/disabling cancellation is not paid on what you are calling "throw specs" until an exception is thrown and the code is actually executed to process the exception. It would be paid, however, on all (groups of) destructors, regardless of when they are executed. This overhead could be reduced if one is willing to make cancellation-disabled the default thread state, and use syntactic markers to identify cancellation-enabled subprograms. In that case, you would only need to do the save-disable/restore code for destructors (and other non-cancellation-enabled subprograms) that are called from cancellation-enabled subprograms. --Ted From baker at cs.fsu.edu Mon Jan 12 19:14:29 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Mon, 12 Jan 2004 14:14:29 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <4002A413.4090908@hp.com> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> Message-ID: <20040112191429.GB13854@diablo.name> > ... the problem isn't with the model, but rather with the effect of that > model on existing code that all-too-casually and agressively eats > exceptions it doesn't understand. I think there are vanishingly few > circumstances where a blind catch(...) without an unconditional re-throw > should be considered "legitimate". If you don't completely understand > what an exception means, you cannot claim to have completely recovered, > and therefore cannot reasonably finalize propagation. (And when you > catch anonymously, you can't possibly understand what they mean since > you can't even identify them.) On the other hand, regardless of whether > the semantics are meaningful or reasonable, the syntax is legal and > apparently (unfortunately) in common use, so I can't dispute that you > need to consider that. This problem could be eliminated by specifying that cancellation is a special case, that cannot be caught by catch(...), i.e., that it can only be caught by a handler that names it explicitly, or that it cannot be caught at all. --Ted From baker at cs.fsu.edu Mon Jan 12 19:18:39 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Mon, 12 Jan 2004 14:18:39 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <200401130452.19605.r-smith@ihug.co.nz> References: <1073556051.28360.195.camel@felix.inria.fr> <4002A413.4090908@hp.com> <200401130452.19605.r-smith@ihug.co.nz> Message-ID: <20040112191839.GC13854@diablo.name> > ... It sounds as though a lot of people are seriously > considering the idea of allowing exceptions to escape from destructors. > .... it is _absolutely vital_ > that destructors never be allowed to throw under any circumstances. Right! This is what we decided with Ada. That is why cancellation (task abort) is disabled during exception propagation and finalization routines of controlled object (execution of destructors). This means we do not need to have any way to catch cancellation/abort, that is, cancellation/abort is not allowed to happen in the places where we otherwise would need to catch it. > a destructor calls a function that may throw some unknown exception (a > very common case, especially in template classes whose destructors will > often call member functions of some arbitrary user-supplied type), the > call _must_ be wrapped in a catch-and-discard-all block. At most you > can log the error somewhere. > > Any attempt to standardise a solution that doesn't take "Destructors > Must Not Throw" as axiomatic is simply going to be a non-starter as far > as C++ programmers are concerned. I'd really like to see this list > explicitly address this issue rather than continuing to casually talk > about uncatchable exceptions and similar horrors. > > -- > Ross Smith ......... r-smith at ihug.co.nz ......... Auckland, New Zealand > > "This world is like a burnt steak: small, tough, and the chips > are always stacked against you." -- Mike --Ted From austern at apple.com Mon Jan 12 19:25:33 2004 From: austern at apple.com (Matt Austern) Date: Mon, 12 Jan 2004 11:25:33 -0800 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <20040112191429.GB13854@diablo.name> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <20040112191429.GB13854@diablo.name> Message-ID: <17A58ACA-4535-11D8-ABB6-00039390D9E0@apple.com> On Jan 12, 2004, at 11:14 AM, Ted Baker wrote: >> ... the problem isn't with the model, but rather with the effect of >> that >> model on existing code that all-too-casually and agressively eats >> exceptions it doesn't understand. I think there are vanishingly few >> circumstances where a blind catch(...) without an unconditional >> re-throw >> should be considered "legitimate". If you don't completely understand >> what an exception means, you cannot claim to have completely >> recovered, >> and therefore cannot reasonably finalize propagation. (And when you >> catch anonymously, you can't possibly understand what they mean since >> you can't even identify them.) On the other hand, regardless of >> whether >> the semantics are meaningful or reasonable, the syntax is legal and >> apparently (unfortunately) in common use, so I can't dispute that you >> need to consider that. > > This problem could be eliminated by specifying that cancellation > is a special case, that cannot be caught by catch(...), i.e., that > it can only be caught by a handler that names it explicitly, or that > it cannot be caught at all. That was originally proposed, and it's a bad idea. There's too much code of the form catch(...) { do_some_partial_cleanup(); throw; } This is important, and it's recommended style. Uncatchable exceptions would be a major change in the C++ exception model. --Matt From baker at cs.fsu.edu Mon Jan 12 19:26:32 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Mon, 12 Jan 2004 14:26:32 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <4002CBCD.4040701@hp.com> References: <1073556051.28360.195.camel@felix.inria.fr> <4002A413.4090908@hp.com> <200401130452.19605.r-smith@ihug.co.nz> <4002CBCD.4040701@hp.com> Message-ID: <20040112192632.GD13854@diablo.name> > I suspect that destructors should be implicitly "no cancel zones". > (Someone expressed a concern about runtime overhead; but the cost of > enabling and disabling cancel CAN be made extremely low by collusion > between C++ and the thread library. That's an implementation detail the Good point. If the overhead of maintaining a per-thread cancellability bit is too high for you, you can use what I called "static mapping" in my 1986 paper on how to implement Ada exceptions (sorry I don't know the C++ term for it), e.g., the linker and compiler provide a table of address ranges over which cancellation is permitted. This has no runtime overhead until you reach a cancellation point, at which point you need to check the stack of return addresses to decide whether it is safe to cancel. This increases the cost of polling for cancellation, but only that. --Ted > standard doesn't necessarily need to explicitly address.) There might be > cases where someone really wants an indefinite blocking operation in a > destructor (perhaps to disconnect from a remote server), and might want > that to be cancellable. I think that's a rare special case, and could > probably be handled by manually enabling cancel over that range of code, > catching/finalizing the cancel if it occurs, cleaning up, restoring the > original "cancel disabled" state, and then calling > pthread_cancel(pthread_self()) to re-pend the cancel. Perhaps (though it > seems doubtful) this might be common enough to justify some special > syntax to encapsulate the mechanism and avoid errors. > > -- > /--------------------[ 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 ]---/ From baker at cs.fsu.edu Mon Jan 12 19:35:28 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Mon, 12 Jan 2004 14:35:28 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <17A58ACA-4535-11D8-ABB6-00039390D9E0@apple.com> References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <20040112191429.GB13854@diablo.name> <17A58ACA-4535-11D8-ABB6-00039390D9E0@apple.com> Message-ID: <20040112193528.GE13854@diablo.name> > >This problem could be eliminated by specifying that cancellation > >is a special case, that cannot be caught by catch(...), i.e., that > >it can only be caught by a handler that names it explicitly, or that > >it cannot be caught at all. > > That was originally proposed, and it's a bad idea. There's too much > code of the form > catch(...) { > do_some_partial_cleanup(); > throw; > } > This is important, and it's recommended style. Uncatchable > exceptions would be a major change in the C++ exception model. This existing code does not rely on being able to catch thread cancellation, since it was written with only normal exceptions in mind. It is precisely because existing code does no know about cancellation that we are having this dicussion, I thought. The main point is that one does not want cancellation to be caught by catch(...), *because* (1) we do not want it swallowed up; (2) if we disable cancellation implicitly over destructors (and explicitly over other sections of code that should not propagate cancellation), we don't need to catch it Making an exception to the sematics of exceptions for cancellation is not a huge change. Ada made this more palatable by defining cancellation (task abort) to be a new thing, like an exception, but different. C++ has a chance to do this more elegantly, by just adding a special-case rule for catch(...). --Ted From baker at cs.fsu.edu Mon Jan 12 19:36:37 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Mon, 12 Jan 2004 14:36:37 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: References: <3FFD7B71.1010708@hp.com> <3FFD7DD5.9090801@bogo.xs4all.nl> <3FFD8046.3000702@hp.com> <3FFE43BB.6040809@bogo.xs4all.nl> <20040109082036.GA31026@redhat.com> <40006C6D.5090707@bogo.xs4all.nl> <4001BA3E.7010100@bogo.xs4all.nl> Message-ID: <20040112193637.GF13854@diablo.name> > > I think it will. Even if cancellation is disabled while exceptions are in > > flight, a 'catch-everything-and-never-rethrow' block in a destructor will > > discard a cancellation exception when that destructor is triggered by a > > regular (non-exceptional, non-unwinding) exit from a block scope. > > True. False, if cancellation is disabled during destructors. --Ted From baker at cs.fsu.edu Mon Jan 12 20:01:49 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Mon, 12 Jan 2004 15:01:49 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <40029F2A.7C13BD76@terekhov.de> References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <40029F2A.7C13BD76@terekhov.de> Message-ID: <20040112200149.GA15536@diablo.name> The 2003 Single Unix Specification, and POSIX, say: "...The thread invokes the cancellation cleanup handler with cancellation disabled until the last cancellation cleanup handler returns...." --Ted On Mon, Jan 12, 2004 at 02:20:42PM +0100, Alexander Terekhov wrote: > Fergus Henderson wrote: > [...] > > > This cannot happen if cancellation is automagically disabled during > > > execution of (all) destructors, along with the stack unwinding code > > > that is performed during exception processing. > > > > If all cleanup is done via destructors (or pthread_cleanup_push, > > which can be handled similarly), you are right. > > AFAIK, POSIX doesn't disable cancellation when thread cleanup handler > is invoked by pthread_cleanup_pop() call with a non-zero execute > argument (cancellation is disabled when the thread exits [that is, > calls pthread_exit()] or acts upon a cancellation request delivery). > > I do it "manually". > > regards, > alexander. From austern at apple.com Mon Jan 12 20:10:43 2004 From: austern at apple.com (Matt Austern) Date: Mon, 12 Jan 2004 12:10:43 -0800 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <20040112193528.GE13854@diablo.name> References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <20040112191429.GB13854@diablo.name> <17A58ACA-4535-11D8-ABB6-00039390D9E0@apple.com> <20040112193528.GE13854@diablo.name> Message-ID: <66A232D0-453B-11D8-ABB6-00039390D9E0@apple.com> On Jan 12, 2004, at 11:35 AM, Ted Baker wrote: >>> This problem could be eliminated by specifying that cancellation >>> is a special case, that cannot be caught by catch(...), i.e., that >>> it can only be caught by a handler that names it explicitly, or that >>> it cannot be caught at all. >> >> That was originally proposed, and it's a bad idea. There's too much >> code of the form >> catch(...) { >> do_some_partial_cleanup(); >> throw; >> } >> This is important, and it's recommended style. Uncatchable >> exceptions would be a major change in the C++ exception model. > > This existing code does not rely on being able to catch thread > cancellation, since it was written with only normal exceptions in > mind. It is precisely because existing code does no know about > cancellation that we are having this dicussion, I thought. That's a fair point. I suspect you're right that we'll be wasting our time if we hope we can do something that will make C++ code that doesn't know about cancellations work correctly even in the presence of multiple threads and cancellation. The real question we should be asking ourselves is how invasive the necessary modifications would be under whatever proposal we're looking at. (Modifications both to the C++ standard and to user code.) I still think that uncatchable exceptions aren't the right answer. Think about the scope of the modifications that would be required: 1. To a good first approximation, all code that catches and rethrows exceptions, and that might be used in a cancellable thread, would have to be modified. (The basis of my claim: if you have to do some cleanup whenever an exception passes through your code, you'll still have to do it for cancellations too.) 1a. This isn't too different from saying that all code that catches exceptions will have to be modified. Most code that catches an exception ends up rethrowing it. 2. Now think about what kind of modification we're talking about. Again, to a good first approximation, cleanup is cleanup. So the example I've give above would be rewritten: catch(...) { do_some_partial_cleanup(); throw; } catch (CancellationException) { do_some_partial_cleanup(); throw; } We don't really want that to become recommended C++ style, do we? --Matt From jason at redhat.com Mon Jan 12 20:33:25 2004 From: jason at redhat.com (Jason Merrill) Date: Mon, 12 Jan 2004 15:33:25 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <20040112193637.GF13854@diablo.name> (Ted Baker's message of "Mon, 12 Jan 2004 14:36:37 -0500") References: <3FFD7B71.1010708@hp.com> <3FFD7DD5.9090801@bogo.xs4all.nl> <3FFD8046.3000702@hp.com> <3FFE43BB.6040809@bogo.xs4all.nl> <20040109082036.GA31026@redhat.com> <40006C6D.5090707@bogo.xs4all.nl> <4001BA3E.7010100@bogo.xs4all.nl> <20040112193637.GF13854@diablo.name> Message-ID: On Mon, 12 Jan 2004 14:36:37 -0500, Ted Baker wrote: >> > I think it will. Even if cancellation is disabled while exceptions are in >> > flight, a 'catch-everything-and-never-rethrow' block in a destructor will >> > discard a cancellation exception when that destructor is triggered by a >> > regular (non-exceptional, non-unwinding) exit from a block scope. >> >> True. > > False, if cancellation is disabled during destructors. Well, sure. But that's a different "if" than the above. Jason From jason at redhat.com Mon Jan 12 20:39:46 2004 From: jason at redhat.com (Jason Merrill) Date: Mon, 12 Jan 2004 15:39:46 -0500 Subject: Restating the Jason model In-Reply-To: <20040112164855.GB10141@diablo.name> (Ted Baker's message of "Mon, 12 Jan 2004 11:48:55 -0500") References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> Message-ID: 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. * Which bits of the C++ library are cancellation points? I would think pretty much all I/O code, and nothing else. 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. * Should cancellation also be disabled in destructors run during normal execution? In catch blocks? IMO, no and no. * 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. * 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. Anything else? Jason From mark at codesourcery.com Mon Jan 12 20:51:21 2004 From: mark at codesourcery.com (Mark Mitchell) Date: 12 Jan 2004 12:51:21 -0800 Subject: [c++-pthreads] Restating the Jason model In-Reply-To: References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> Message-ID: <1073940680.3458.82.camel@minax.codesourcery.com> On Mon, 2004-01-12 at 12:39, 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. Thank you for summing up. I believe that I would support all elements of your proposal, with the possible exception of: > My preference is still to amend the C++ standard to allow stdio functions > to throw cancellation. However, I don't see that as a particularly big deal one way or the other. The observation has already been made that the goal of preserving existing C++ code is probably incompatible with introducing cancellation, although Nathan's ETHREADCANCELLED idea is designed to get around that. > * Should cancellation also be disabled in destructors run during normal > execution? In catch blocks? > > IMO, no and no. Agreed. > * 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. Agreed. > * 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. I have no strong feeling here, but calling "abort" would be fine by me. -- Mark Mitchell CodeSourcery, LLC From jason at redhat.com Mon Jan 12 21:12:27 2004 From: jason at redhat.com (Jason Merrill) Date: Mon, 12 Jan 2004 16:12:27 -0500 Subject: [c++-pthreads] Restating the Jason model In-Reply-To: <1073940680.3458.82.camel@minax.codesourcery.com> (Mark Mitchell's message of "12 Jan 2004 12:51:21 -0800") References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> <1073940680.3458.82.camel@minax.codesourcery.com> Message-ID: On 12 Jan 2004 12:51:21 -0800, Mark Mitchell wrote: > On Mon, 2004-01-12 at 12:39, Jason Merrill wrote: > I believe that I would support all elements of your proposal, with the > possible exception of: > >> My preference is still to amend the C++ standard to allow stdio functions >> to throw cancellation. > > However, I don't see that as a particularly big deal one way or the > other. The observation has already been made that the goal of > preserving existing C++ code is probably incompatible with introducing > cancellation, although Nathan's ETHREADCANCELLED idea is designed to get > around that. As others have pointed out, the ECANCELLED idea doesn't work; i/o calls from cleanup code would also fail with ECANCELLED, so (some of) the cleanup wouldn't actually happen. Failing that, if we want cancellation to abort blocked stdio calls, we need to allow them to throw. Jason From mark at codesourcery.com Mon Jan 12 21:16:58 2004 From: mark at codesourcery.com (Mark Mitchell) Date: 12 Jan 2004 13:16:58 -0800 Subject: [c++-pthreads] Restating the Jason model In-Reply-To: References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> <1073940680.3458.82.camel@minax.codesourcery.com> Message-ID: <1073942218.3458.91.camel@minax.codesourcery.com> On Mon, 2004-01-12 at 13:12, Jason Merrill wrote: > On 12 Jan 2004 12:51:21 -0800, Mark Mitchell wrote: > > On Mon, 2004-01-12 at 12:39, Jason Merrill wrote: > > > I believe that I would support all elements of your proposal, with the > > possible exception of: > > > >> My preference is still to amend the C++ standard to allow stdio functions > >> to throw cancellation. > > > > However, I don't see that as a particularly big deal one way or the > > other. The observation has already been made that the goal of > > preserving existing C++ code is probably incompatible with introducing > > cancellation, although Nathan's ETHREADCANCELLED idea is designed to get > > around that. > > As others have pointed out, the ECANCELLED idea doesn't work; i/o calls > from cleanup code would also fail with ECANCELLED, so (some of) the cleanup > wouldn't actually happen. Yes, I'm not disagreeing. I've always thought exceptions were the natural thing here, although I very much appreciate Nathan taking the time to put together a nicely written proposal. -- Mark Mitchell CodeSourcery, LLC From jason at redhat.com Mon Jan 12 21:56:39 2004 From: jason at redhat.com (Jason Merrill) Date: Mon, 12 Jan 2004 16:56:39 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <20040112191839.GC13854@diablo.name> (Ted Baker's message of "Mon, 12 Jan 2004 14:18:39 -0500") References: <1073556051.28360.195.camel@felix.inria.fr> <4002A413.4090908@hp.com> <200401130452.19605.r-smith@ihug.co.nz> <20040112191839.GC13854@diablo.name> Message-ID: On Mon, 12 Jan 2004 14:18:39 -0500, Ted Baker wrote: >> ... It sounds as though a lot of people are seriously >> considering the idea of allowing exceptions to escape from destructors. >> .... it is _absolutely vital_ >> that destructors never be allowed to throw under any circumstances. > > Right! This is what we decided with Ada. That is why > cancellation (task abort) is disabled during exception propagation > and finalization routines of controlled object (execution of > destructors). Can a finalization routine throw a normal exception in Ada? Jason From boo at terekhov.de Tue Jan 13 11:26:54 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Tue, 13 Jan 2004 12:26:54 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <40029F2A.7C13BD76@terekhov.de> <20040112200149.GA15536@diablo.name> Message-ID: <4003D5FE.6F48AA0E@terekhov.de> Ted Baker wrote: > > The 2003 Single Unix Specification, and POSIX, say: > > "...The thread invokes the cancellation cleanup handler with > cancellation disabled until the last cancellation cleanup handler > returns...." This is about POSIX thread termination, not pthread_cleanup_pop() with a non-zero execute argument. See also TC2 (Change Number: XSH/TC2/D6/7 [XSH ERN 77]). The latest draft is available here: www.opengroup.org/austin/restricted/tc2d6/P1003_1-2001-Corr-2-d6.pdf regards, alexander. > > --Ted > > On Mon, Jan 12, 2004 at 02:20:42PM +0100, Alexander Terekhov wrote: > > Fergus Henderson wrote: > > [...] > > > > This cannot happen if cancellation is automagically disabled during > > > > execution of (all) destructors, along with the stack unwinding code > > > > that is performed during exception processing. > > > > > > If all cleanup is done via destructors (or pthread_cleanup_push, > > > which can be handled similarly), you are right. > > > > AFAIK, POSIX doesn't disable cancellation when thread cleanup handler > > is invoked by pthread_cleanup_pop() call with a non-zero execute > > argument (cancellation is disabled when the thread exits [that is, > > calls pthread_exit()] or acts upon a cancellation request delivery). > > > > I do it "manually". > > > > regards, > > alexander. From boo at terekhov.de Tue Jan 13 11:35:15 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Tue, 13 Jan 2004 12:35:15 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <40028D9B.988E8C81@terekhov.de> <20040112164533.GA10141@diablo.name> <4002E53E.8840B94B@terekhov.de> <20040112190651.GA13854@diablo.name> Message-ID: <4003D7F3.1D5E3C32@terekhov.de> Ted Baker wrote: > > On Mon, Jan 12, 2004 at 07:19:42PM +0100, Alexander Terekhov wrote: > > Ted Baker wrote: > > [...] > > > There is no need for a destructor to be aware of cancellation. > > > > my_file::~my_file() throw() { > > fclose(/*...*/); // doesn't throw; cancel is unexpected (even if thread > > // cancel state is equal here to PTHREAD_CANCEL_ENABLE) > > } > > Yes, in my model, cancel state would *always* be equal to > PTHREAD_CANCEL_DISABLE in the my_file (and my_other_file) And I say that this sucks. > destructors, and so fclose() cannot throw cancellation. > > > void jason() throw() { > > std::enable_thread_cancel(); // re-enable cancel state > > std::thread_self().cancel(); // re-inject cancel request > > } > > > my_other_file::~my_other_file() throw() { > > bool canceled_before = std::unwinding(this); > > try { > > if (canceled_before) jason(); > > fclose(/*...*/); // can (should "if (canceled before)") throw > > /*std::*/pthread_testcancel(); // fix "may occur" mess > > } > > catch (std::thread_cancel_request const &) { > > if (!canceled_before) jason(); > > } > > } > > What is the purpose of the above? That is, why are you > intentionally shooting yourself in the foot, by enabling > cancellation and then explicitly catching it in ~my_other_file()? Because I want to interrupt fclose (the flushing part of it, of course). > I guess you have a reason, but I don't see it. This seems to just > makes ugliness without any apparent benefit. You can just call > fclose() by itself (without the thry...catch), since cancellation > is disabled, as in ~my_file(). > > (BTW, I would prefer to see code that does this sort of explicit > playing with cancellation state and catching of cancellation > required to provide some sort of syntactic override, so that > people are less likely to inadvertently shoot their feet.) > > > Now, in your model with cancellation ALWAYS disabled while running > > destructors (not only when acting upon a cancel request delivery... > > thread_exit aside for a moment), I'd have to add enable/disable RAII > > Please translate "RAII" for me? http://www.terekhov.de/DESIGN-futex-CV.cpp > > > guard object (and that's in addition to save-disable/restore internal > > Why do you say that would require you to add a guard object? See above. regards, alexander. From David.Butenhof at hp.com Tue Jan 13 11:35:47 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Tue, 13 Jan 2004 06:35:47 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <200401130642.20048.r-smith@ihug.co.nz> References: <1073556051.28360.195.camel@felix.inria.fr> <200401130452.19605.r-smith@ihug.co.nz> <4002CBCD.4040701@hp.com> <200401130642.20048.r-smith@ihug.co.nz> Message-ID: <4003D813.6060507@hp.com> Ross Smith wrote: >On Tuesday 13 January 2004 05:31, Dave Butenhof wrote: > > >>While I wouldn't dispute the statement that "I don't really >>understand C++", I absolutely do understand objects, encapsulation, >>modularity, exceptions, cancellation, and resource ownership; and >>what you're saying seems essentially "obvious and self-evident". I >>never argued, or suggested, or assumed, that exceptions would >>propagate out of destructors. >> >> >You may not have said so explicitly, but your repeated statements that >catch-all-and-discard was almost always a bad idea certainly implied >that. If you meant it to be qualified with "except in destructors", >which I gather from the above is what you really meant, then I wish >you'd said so instead of leaving us to get the wrong impression. > > Not to beat a dead horse (bash! take THAT, dobbins), but as we've already established I am not a "heart and soul" C++ person. I'm really in essence talking about CANCEL exceptions, in C/POSIX, and trying to relate them to C++ syntax and semantics. And while I have never overlooked destructors, they simply are not always floating around on the top of my mind and the tip of my tongue. ;-) >>I suspect that destructors should be implicitly "no cancel zones". >> >> >Thank you. We now appear to be in agreement :-) > > Well, that's good. -- /--------------------[ 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 ]---/ From David.Butenhof at hp.com Tue Jan 13 11:43:24 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Tue, 13 Jan 2004 06:43:24 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> Message-ID: <4003D9DC.6060705@hp.com> David Abrahams wrote: >Dave Butenhof writes: > >>I think I disagree, at least philsophically, with the characterization >>of the model as "fragile". But I think I also understand what you >>mean; and the problem isn't with the model, but rather with the effect >>of that model on existing code that all-too-casually and agressively >>eats exceptions it doesn't understand. I think there are vanishingly >>few circumstances where a blind catch(...) without an unconditional >>re-throw should be considered "legitimate". If you don't completely >>understand what an exception means, you cannot claim to have >>completely recovered, and therefore cannot reasonably finalize >>propagation. >> >> >The problems with catch(...) eating all exceptions are maybe not as >bad as you think. As a matter of fact, there are vanishingly few >exceptions that demand special recovery actions that wouldn't work for >all other exceptions. Systems designed that way tend towards >fragility. > > I see an immense difference between a pragmatic statement that "in practice there seem to be few exceptions" and something on which cross-platform, mixed-language, modular environment programmers can depend as a law. C++ does not say that "all exceptions can be finalized and recovered fully by performing these steps". To presume they can is fragile. -- /--------------------[ 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 ]---/ From boo at terekhov.de Tue Jan 13 11:58:42 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Tue, 13 Jan 2004 12:58:42 +0100 Subject: [c++-pthreads] Restating the Jason model References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> Message-ID: <4003DD72.EDA14CDE@terekhov.de> 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. Agreed. > * If a cancellation exception is destroyed, the cancellation request > is re-entered, and acted on again at the next cancellation point. Disagreed. > * Cancellation is disabled during unwinding. Only when propagating thread termination request exception (base class for both thread_cancel_request and thread_exit_value<>). See also POSIX TC2 (Change Number: XSH/TC2/D6/7 [XSH ERN 77]). > > But there are still some open questions: > > * Which of the POSIX cancellation points are cancellation points in C++? All. [...] > * Which bits of the C++ library are cancellation points? > > I would think pretty much all I/O code, and nothing else. I would think C++ library should have a lot of async-cancel-safe stuff. [...] > * Should cancellation also be disabled in destructors run during normal > execution? Not by the implementation (2-phase EH aside for a moment). > In catch blocks? That depends. > > IMO, no and no. See POSIX TC2 (Change Number: XSH/TC2/D6/7 [XSH ERN 77]). > > * 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. Yes. > > * What about pthread_exit? http://www.codesourcery.com/archives/c++-pthreads/msg00005.html > > I'm happy with the g++ status quo whereby destroying a pthread_exit > exception calls terminate. I'm not happy with that. Why should the g++ care what I do with *MY* exceptions? regards, alexander. From David.Butenhof at hp.com Tue Jan 13 12:07:24 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Tue, 13 Jan 2004 07:07:24 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <20040112191429.GB13854@diablo.name> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <20040112191429.GB13854@diablo.name> Message-ID: <4003DF7C.8050206@hp.com> Ted Baker wrote: >>... the problem isn't with the model, but rather with the effect of that >>model on existing code that all-too-casually and agressively eats >>exceptions it doesn't understand. I think there are vanishingly few >>circumstances where a blind catch(...) without an unconditional re-throw >>should be considered "legitimate". If you don't completely understand >>what an exception means, you cannot claim to have completely recovered, >>and therefore cannot reasonably finalize propagation. (And when you >>catch anonymously, you can't possibly understand what they mean since >>you can't even identify them.) On the other hand, regardless of whether >>the semantics are meaningful or reasonable, the syntax is legal and >>apparently (unfortunately) in common use, so I can't dispute that you >>need to consider that. >> >> >This problem could be eliminated by specifying that cancellation >is a special case, that cannot be caught by catch(...), i.e., that >it can only be caught by a handler that names it explicitly, or that >it cannot be caught at all. > > Yes; that's been considered in the past discussions. Along with many other alternatives. (The same had been proposed for DCE/C exceptions reporting SIGSEGV and other "hardware" failures.) Generally, most people end up thinking that's a little too syntactically asymmetric; there are uses of catch(...) that really do need to isolate a subsystem by catching everything, and breaking that model seems extreme. But it's also true that might be the lesser of two evils, since it should be uncommon. And so the argument sways back and forth, thus far without authoritative resolution. I've been debating some of these exact issues with various people since threading became common enough that C++ people wanted to use it, armed with their newly defined exception model that actually worked on some of the common implementations of C++. (Yeah, that long ago.) The problem, of course, is that everyone and nobody is "right" -- and it's hard to converge on one middle ground without someone suddenly realizing, "but then THAT wouldn't work". And then again, since the C++ standard committee was never even peripherally involved before, everyone knew that it was little more than idle speculation. -- /--------------------[ 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 ]---/ From wil at bogo.xs4all.nl Tue Jan 13 12:44:39 2004 From: wil at bogo.xs4all.nl (Wil Evers) Date: Tue, 13 Jan 2004 13:44:39 +0100 Subject: [c++-pthreads] Restating the Jason model References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> Message-ID: <4003E837.7060308@bogo.xs4all.nl> 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. I can't help wondering about the difference between this design and Nathan's sticky cancellation model. It seems to me that in your model, I can do something like: catch (const cancellation_request&) { some_socket.write("Thread cancelled\n"); } and expect the write operation to succeed, whereas in Nathan's sticky cancellation design, the write operation will throw another cancellation_request (unless, of course, I disable cancellation in the catch block). If so, is this intentional? And what about this one: try { some_socket.connect("www.ibm.com"); } catch (const cancellation_request&) { throw my_fancy_exception(__FILE__, __LINE__, "Cancellation request"); } Here, unwinding continues, but the cancellation request is mapped onto some other - presumably legacy - exception hierarchy. Do we really want the cancellation request to be re-entered here? To me, it seems like Nathan's sticky cancellation model uses a tried-and-tested design - it simply puts the thread object into an error state, causing subsequent operations to fail - whereas your design is quite innovative. I'm not suggesting there is anything wrong with that, but I do believe it needs to be justified. > * Cancellation is disabled during unwinding. > > But there are still some open questions: [snip] > * Should cancellation also be disabled in destructors run during normal > execution? In catch blocks? > > IMO, no and no. This implies a difference in how destructors behave, depending on why they are invoked. If such a difference can be avoided, I think it should be: we have Ted Baker's model (destructors always disable cancellation), and the other obvious choice is to leave it to the user to take the necessary precautions when writing destructors. Good C++ programmers already know how to do that. - Wil From David.Butenhof at hp.com Tue Jan 13 12:47:17 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Tue, 13 Jan 2004 07:47:17 -0500 Subject: [c++-pthreads] Restating the Jason model In-Reply-To: References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> Message-ID: <4003E8D5.1030600@hp.com> 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 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 ]---/ From boo at terekhov.de Tue Jan 13 13:28:45 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Tue, 13 Jan 2004 14:28:45 +0100 Subject: [c++-pthreads] Restating the Jason model References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> <4003E8D5.1030600@hp.com> Message-ID: <4003F28D.E6DD6DD6@terekhov.de> Dave Butenhof wrote: [...] > >Anything else? > > > > > Yes; but I don't yet know what it is. ;-) 2-phase EH (with fixed throw spec semantics) and "intelligent" cancel request delivery. I thought you were in favor of it... http://groups.google.com/groups?selm=3ec0f83b%40usenet01.boi.hp.com OK, fine. Let's standardize on a low-level exception support environment with DETACHED 2-phase PC-mapped exceptions. On the first "search" phase, the handler (if any) associated with each PC region that's active on the call stack is presented (in order from most recent frame) with the cause of the exception; say, a status code and arbitrary counted argument list. At any point, a handler may determine that an UNWIND is necessary, targeting a particular active stack frame. All handlers from the "inmost" out to that frame's will be activated a second time with a status that indicates an UNWIND is in progress. If the search phase reaches the base frame with no unwind, then a core dump is taken. Only on the UNWIND phase should any handler make actual changes in any program state, consistent with the unwinding of that frame. Thus, cleanup occurs only when an UNWIND has been ordered -- and of course it may also be conditionalized on the identity of the exception that's unwinding. This provides everything you need for your conditional "cleanup only if unwind will occur" syntax, in C++ and/or C. It still provides sufficient support for existing exception models like Ada and Modula-2+ that already have 'finally' and depend on a traditional interpretation. (Having any of these frames active will cause your conditional cleanup for any "inner frame" to fire because there WILL be an unwind, but that's life.) (What I've described, by the way, is essentially how the OpenVMS condition handling facility has worked since 1977, though many users have always ignored the unenforced recommendation that cleanup be done only on unwind.) regards, alexander. From David.Butenhof at hp.com Tue Jan 13 13:31:51 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Tue, 13 Jan 2004 08:31:51 -0500 Subject: [c++-pthreads] Restating the Jason model In-Reply-To: <4003E837.7060308@bogo.xs4all.nl> References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> <4003E837.7060308@bogo.xs4all.nl> Message-ID: <4003F347.8030203@hp.com> Wil Evers wrote: > 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. > > I can't help wondering about the difference between this design and > Nathan's sticky cancellation model. It seems to me that in your > model, I can do something like: > > catch (const cancellation_request&) { > some_socket.write("Thread cancelled\n"); > } > > and expect the write operation to succeed, whereas in Nathan's sticky > cancellation design, the write operation will throw another > cancellation_request (unless, of course, I disable cancellation in the > catch block). If so, is this intentional? Absolutely; because the correct behavior of the application (which might be distributed) could depend on being able to notify the remote partner that the local thread is shutting down. Nathan's proposal doesn't allow that, and that's the big weakness. Jason's proposal will cause the cancel to be re-asserted when the exception object is destroyed, on exit from the catch(), after local cleanup has been done. This makes the catch() behave (more or less) like a destructor. > And what about this one: > > try { > some_socket.connect("www.ibm.com"); > } catch (const cancellation_request&) { > throw my_fancy_exception(__FILE__, __LINE__, > "Cancellation request"); > } > > Here, unwinding continues, but the cancellation request is mapped onto > some other - presumably legacy - exception hierarchy. Do we really > want the cancellation request to be re-entered here? Yes, perhaps; because propagation of my_fancy_exception would either be swallowed later (probably, since nobody would have thrown it without a catch) or else it'll terminate the process. Neither meets the semantic definition of cancellation, or the expectations of the application code that sent the cancel. Of course, you could get arbitrarily convoluted here, my somehow making the propagation of my_fancy_exception inherit the "progagating cancel" state so that when THAT object is destroyed the original cancel will be re-raised, or so detection of THAT unhandled exception will terminate the thread rather than the process. (Though I'm not at all convinced that's what we'd want to happen.) One alternative is to simply say that such code is NOT cancel-safe; and that the behavior of a thread running such code when cancelled is inherently unreliable. A lot of existing code won't be cancel-safe, and I don't believe that there's anything the standard or any implementation can do about that. > To me, it seems like Nathan's sticky cancellation model uses a > tried-and-tested design - it simply puts the thread object into an > error state, causing subsequent operations to fail - whereas your > design is quite innovative. I'm not suggesting there is anything > wrong with that, but I do believe it needs to be justified. There's no such thing, currently, as an error condition that will cause ALL subsequent blocking operations to fail. It's not at all "tried and tested". Aside from the "sticky" characteristic, which seems to be an almost necessary consequence of the C++ catch(...) syntax and idiom, Jason's proposal is exactly the model for which POSIX cancellation was designed, and which has been in wide use for up to 12 years. (Starting with our CMA library on VMS and ULTRIX, and extending out through POSIX standardization to most other UNIX-based systems.) >> * Should cancellation also be disabled in destructors run during normal >> execution? In catch blocks? >> >> IMO, no and no. > > This implies a difference in how destructors behave, depending on why > they are invoked. If such a difference can be avoided, I think it > should be: we have Ted Baker's model (destructors always disable > cancellation), and the other obvious choice is to leave it to the user > to take the necessary precautions when writing destructors. Good C++ > programmers already know how to do that. They may know how, but most haven't previously had any need to worry about that. It seems to be fairly widely agreed here that propagating an exception out of a destructor is "bad". Since that's what will happen if cancellation strikes an unprepared destructor, it seems to me that the best option is to prevent that in the default case (by disabling cancellation), and requiring any destructor that really WANTS to be cancellable to do something unusual. -- /--------------------[ 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 ]---/ From boo at terekhov.de Tue Jan 13 13:59:11 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Tue, 13 Jan 2004 14:59:11 +0100 Subject: [c++-pthreads] Restating the Jason model References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> <4003E837.7060308@bogo.xs4all.nl> <4003F347.8030203@hp.com> Message-ID: <4003F9AF.35834AEA@terekhov.de> Dave Butenhof wrote: [...] > Absolutely; because the correct behavior of the application (which might > be distributed) could depend on being able to notify the remote partner > that the local thread is shutting down. Nathan's proposal doesn't allow > that, and that's the big weakness. Nathan's proposal does allow that (not that I really like Nathan's proposal). You'd simply have to manually disable cancelation. Think of "traditional" pthread_cleanup_pop(!0) handlers. > > Jason's proposal will cause the cancel to be re-asserted when the > exception object is destroyed, on exit from the catch(), after local > cleanup has been done. This makes the catch() behave (more or less) like > a destructor. And that's "the big" weakness. catch() != destructor. void operation() throw(std::thread_cancel_request); void f() { try { operation(); } catch(...) { pthread_exit("Wow, canceled."); // or something like that } /* ... */ } regards, alexander. From David.Butenhof at hp.com Tue Jan 13 14:03:55 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Tue, 13 Jan 2004 09:03:55 -0500 Subject: [c++-pthreads] Restating the Jason model In-Reply-To: <4003F28D.E6DD6DD6@terekhov.de> References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> <4003E8D5.1030600@hp.com> <4003F28D.E6DD6DD6@terekhov.de> Message-ID: <4003FACB.8080704@hp.com> Alexander Terekhov wrote: >Dave Butenhof wrote: >[...] > > >>>Anything else? >>> >>Yes; but I don't yet know what it is. ;-) >> >> >2-phase EH (with fixed throw spec semantics) and "intelligent" cancel >request delivery. I thought you were in favor of it... > Yes, I am; although I'm far more interested in having a common exception library with the capability available, than in necessarily modifying C++ to fully exploit it. In fact, as I've told you before, my arguments were entirely in regard to a hypothetical "common exception library" (such as the ones to which I'm accustomed on Tru64 and VMS), and I have no interest in involving myself in extending this discussion to C++. While I see advantages in 2-phase EH for C++, this is counterbalanced by the enormous pain that would be involved in trying to switch at this late date. Perhaps if we'd succeeded in convincing Bjarne Stroustrup to do it that way in the first place, things would be different. ;-) In any case, though, we're talking about how to fit threads (and cancel) into C++. The subject here is NOT how or whether to radically re-engineer the C++ exception model, nor is that necessary to deal rationally and usefully with threads and cancellation. So I prefer not to get drawn into an argument about that in this forum. I've already discussed this issue with Dave Abrahams. I know his opinion and he knows mine; and I guess now you've made sure that everyone else knows mine. As far as I'm concerned, that's the end of it unless "they" want to extend this discussion to cover such matters. (And I'd be surprised if anyone did.) I'm not entirely sure I understand what you mean here by '"intelligent" cancel request delivery". If we discussed it before I don't recall that phrase. -- /--------------------[ 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 ]---/ From David.Butenhof at hp.com Tue Jan 13 14:26:18 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Tue, 13 Jan 2004 09:26:18 -0500 Subject: [c++-pthreads] Restating the Jason model In-Reply-To: <4003F9AF.35834AEA@terekhov.de> References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> <4003E837.7060308@bogo.xs4all.nl> <4003F347.8030203@hp.com> <4003F9AF.35834AEA@terekhov.de> Message-ID: <4004000A.3030506@hp.com> Alexander Terekhov wrote: >Dave Butenhof wrote: >[...] > > >>Absolutely; because the correct behavior of the application (which might >>be distributed) could depend on being able to notify the remote partner >>that the local thread is shutting down. Nathan's proposal doesn't allow >>that, and that's the big weakness. >> >> >Nathan's proposal does allow that (not that I really like Nathan's >proposal). You'd simply have to manually disable cancelation. Think of >"traditional" pthread_cleanup_pop(!0) handlers. > > Only if the code was specifically aware of cancellation and the consequences. But it seems that the primary basis for Nathan's proposal (and the only real justification I can see) was the idea that 'if (error) {cleanup(); return error;}' idiom in existing C code would continue to work despite cancellation. And that's simply not true; in general you'd need to re-code cleanup() to disable cancellation. (And at least all such would need to be analyzed carefully to ensure that cleanup() didn't call any cancellation points.) >>Jason's proposal will cause the cancel to be re-asserted when the >>exception object is destroyed, on exit from the catch(), after local >>cleanup has been done. This makes the catch() behave (more or less) like >>a destructor. >> >> >And that's "the big" weakness. catch() != destructor. > > Unfortunately, there's apparently an extremely common (and perhaps even officially recommended) IDIOM in C++ that implements non-object-specific "destructors" using catch(...). It appears that there's general consensus here for a plan that retains this idiom, and I can understand that desire. While I don't really like the idea of "sticky cancel", I don't yet see another practical alternative that addresses the basic problem without redesigning C++. (Of course, it's fine for you to bring this up, though I think you harp on it too much; if the C++ committee IS interested in considering a massive redesign of C++ exceptions, this would be a good time.) >void operation() throw(std::thread_cancel_request); > >void f() { > try { > operation(); > } > catch(...) { > pthread_exit("Wow, canceled."); // or something like that > } > /* ... */ >} > > Given that pthread_exit() is also an exception, there's little difference between this and "throw my_fancy_exception". (Aside from the fact that unlike my_fancy_exception, the thread exit exception is also designated as "thread terminating".) -- /--------------------[ 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 ]---/ From fjh at cs.mu.oz.au Tue Jan 13 14:40:51 2004 From: fjh at cs.mu.oz.au (Fergus Henderson) Date: Wed, 14 Jan 2004 01:40:51 +1100 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <4003D7F3.1D5E3C32@terekhov.de> References: <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <40028D9B.988E8C81@terekhov.de> <20040112164533.GA10141@diablo.name> <4002E53E.8840B94B@terekhov.de> <20040112190651.GA13854@diablo.name> <4003D7F3.1D5E3C32@terekhov.de> Message-ID: <20040113144051.GA27104@jupiter.cs.mu.oz.au> On 13-Jan-2004, Alexander Terekhov wrote: > Ted Baker wrote: > > On Mon, Jan 12, 2004 at 07:19:42PM +0100, Alexander Terekhov wrote: > > > Now, in your model with cancellation ALWAYS disabled while running > > > destructors (not only when acting upon a cancel request delivery... > > > thread_exit aside for a moment), I'd have to add enable/disable RAII > > > > Please translate "RAII" for me? > > http://www.terekhov.de/DESIGN-futex-CV.cpp Alexander Terekhov again fails to communicate, and when pressed substitutes a unrelated URL. Ted, when he says "RAII", he means "Resource Acquision is Initialization". This is the name of a C++ idiom where code which acquires/releases a resource is implemented as the constructor/destructor of a local object. Alex, I suggest that you put more effort into learning to communicate. Most of your recent articles are very difficult to understand and show very little real effort towards actually explaining yourself. If someone explicitly asks what you mean by an acronym, the _least_ that you could do would be to give them the full spelling. Your apparent lack of effort indicates a level of disrespect to your audience that is not conducive to a productive discussion. -- Fergus Henderson | "I have always known that the pursuit The University of Melbourne | of excellence is a lethal habit" WWW: | -- the last words of T. S. Garp. From boo at terekhov.de Tue Jan 13 15:11:09 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Tue, 13 Jan 2004 16:11:09 +0100 Subject: [c++-pthreads] Restating the Jason model References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> <4003E8D5.1030600@hp.com> <4003F28D.E6DD6DD6@terekhov.de> <4003FACB.8080704@hp.com> Message-ID: <40040A8D.B1FE187@terekhov.de> Dave Butenhof wrote: [...] > I'm not entirely sure I understand what you mean here by '"intelligent" > cancel request delivery". If we discussed it before I don't recall that > phrase. I mean "bool std::expected_exception() throw()" thing. "Intelligent" cancel request delivery means that cancellation points and async-cancel regions shall throw (deliver) std::thread_cancel_request only when cancellation is enabled (cancel state is equal to PTHREAD_CANCEL_ENABLE) AND "std::expected_exception()" is true at throw point (i.e. there's reachable catch handler for it in the dynamic context). Throw specs (dtors would have implicit throw() imposed on them) would act like "fences"; they should NOT have catch(...) effect. regards, alexander. From wil at bogo.xs4all.nl Tue Jan 13 15:29:18 2004 From: wil at bogo.xs4all.nl (Wil Evers) Date: Tue, 13 Jan 2004 16:29:18 +0100 Subject: [c++-pthreads] Restating the Jason model References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> <4003E837.7060308@bogo.xs4all.nl> <4003F347.8030203@hp.com> Message-ID: <40040ECE.4090300@bogo.xs4all.nl> Dave Butenhof wrote: > Wil Evers wrote: > >> Jason Merrill wrote: >> >>> * Should cancellation also be disabled in destructors run during normal >>> execution? In catch blocks? >>> >>> IMO, no and no. >> >> This implies a difference in how destructors behave, depending on why >> they are invoked. If such a difference can be avoided, I think it >> should be: we have Ted Baker's model (destructors always disable >> cancellation), and the other obvious choice is to leave it to the user >> to take the necessary precautions when writing destructors. Good C++ >> programmers already know how to do that. > > They may know how, but most haven't previously had any need to worry > about that. It seems to be fairly widely agreed here that propagating an > exception out of a destructor is "bad". Since that's what will happen if > cancellation strikes an unprepared destructor, it seems to me that the > best option is to prevent that in the default case (by disabling > cancellation), and requiring any destructor that really WANTS to be > cancellable to do something unusual. IMO, the problem with Jason's model is that it only protects against *some* cases where exceptions escape from destructors: * Jason wrote that cancellation will not be disabled when a destructor is run during normal (non-unwinding) execution. In that case - likely the most common one - a cancellation exception may be thrown, and will escape. * Many cancellation points are I/O primitives, and I/O operations are sources of runtime errors; many C++ libraries report such errors by throwing exceptions. In other words: if we can expect a cancellation exception, we should probably expect other kinds of exceptions too. Obviously, these exceptions will also escape. So again, if we're determined not to let exceptions escape, we need a catch-and-finalize block. It seems to me that automatically disabling cancellation while unwinding may result in a false sense of security. - Wil From ben.hutchings at businesswebsoftware.com Tue Jan 13 15:40:38 2004 From: ben.hutchings at businesswebsoftware.com (Ben Hutchings) Date: Tue, 13 Jan 2004 15:40:38 -0000 Subject: [c++-pthreads] Restating the Jason model Message-ID: Alexander Terekhov wrote: > Dave Butenhof wrote: > [...] > > I'm not entirely sure I understand what you mean here by > > '"intelligent" cancel request delivery". If we discussed it before > > I don't recall that phrase. > > I mean "bool std::expected_exception() throw()" thing. > "Intelligent" cancel request delivery means that cancellation points > and async-cancel regions shall throw (deliver) > std::thread_cancel_request only when cancellation is enabled (cancel > state is equal to PTHREAD_CANCEL_ENABLE) AND > "std::expected_exception()" is true at > throw point (i.e. there's reachable catch handler for it in the > dynamic context). So this is a way to avoid the overhead of disabling and enabling cancellation in destructors (and elsewhere) - and it requires 2-phase exception-handling, right? > Throw specs (dtors would have implicit throw() imposed on them) > would act like "fences"; they should NOT have catch(...) effect. Am I right in thinking that aside from this wart 2-phase EH is allowed but not required by the current C++ standard? From jason at redhat.com Tue Jan 13 16:41:30 2004 From: jason at redhat.com (Jason Merrill) Date: Tue, 13 Jan 2004 11:41:30 -0500 Subject: [c++-pthreads] Restating the Jason model In-Reply-To: <4003E8D5.1030600@hp.com> (Dave Butenhof's message of "Tue, 13 Jan 2004 07:47:17 -0500") References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> <4003E8D5.1030600@hp.com> Message-ID: On Tue, 13 Jan 2004 07:47:17 -0500, Dave Butenhof wrote: > Jason Merrill wrote: >> * 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". Agreed, but I don't think there is anything else in the C++ standard library that fits that description. >>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. > 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. I think the impact on runtime developers of this change would be minimal; it's a pretty mechanical change since the functions in question already have to deal with exceptions thrown out of their subroutines. It seems to me that the only question is whether or not we *want* the cancel to propagate out of these functions. Presumably the designers of iostreams had a reason for blocking all exceptions in the formatted i/o functions; I'm not familiar with this rationale, so it's hard for me to judge whether or not it applies to cancel as well. >> * 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? Yes. Destructors can be run under two different situations: 1) when the object goes out of scope during normal execution; 2) when unwinding the stack during exception handling. In #1, an exception thrown out of a destructor is propagated normally. In the #2, it causes a call to terminate(). So we need to suppress cancellation for #2, but not (necessarily) #1. >> * 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()"? Yes, that's the main one I was thinking of. > (Could or should this be done automatically be the runtime for > 'catch(cancel)' as opposed to 'catch(...)'?) It could, but that strikes me as excessively clever. :) >> * 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. There's no way to use the same rules for both, since we can't re-assert deferred exit. Jason From boo at terekhov.de Tue Jan 13 17:30:20 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Tue, 13 Jan 2004 18:30:20 +0100 Subject: [c++-pthreads] Restating the Jason model References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> <4003E837.7060308@bogo.xs4all.nl> <4003F347.8030203@hp.com> <40 <4004000A.3030506@hp.com> Message-ID: <40042B2C.9922137A@terekhov.de> Dave Butenhof wrote: [...] > >>Absolutely; because the correct behavior of the application (which might > >>be distributed) could depend on being able to notify the remote partner > >>that the local thread is shutting down. Nathan's proposal doesn't allow > >>that, and that's the big weakness. > >> > >> > >Nathan's proposal does allow that (not that I really like Nathan's > >proposal). You'd simply have to manually disable cancelation. Think of > >"traditional" pthread_cleanup_pop(!0) handlers. > > > > > Only if the code was specifically aware of cancellation and the > consequences. But it seems that the primary basis for Nathan's proposal > (and the only real justification I can see) was the idea that 'if > (error) {cleanup(); return error;}' idiom in existing C code would > continue to work despite cancellation. And that's simply not true; in > general you'd need to re-code cleanup() to disable cancellation. (And at > least all such would need to be analyzed carefully to ensure that > cleanup() didn't call any cancellation points.) Agreed. > > >>Jason's proposal will cause the cancel to be re-asserted when the > >>exception object is destroyed, on exit from the catch(), after local > >>cleanup has been done. This makes the catch() behave (more or less) like > >>a destructor. > >> > >> > >And that's "the big" weakness. catch() != destructor. > > > > > Unfortunately, there's apparently an extremely common (and perhaps even > officially recommended) IDIOM in C++ that implements non-object-specific > "destructors" using catch(...). It appears that there's general > consensus here for a plan that retains this idiom, and I can understand > that desire. That idiom is known as catch/re-throw. It does re-throw. Jason's sticky cancel is meant to "address" the problem of broken iostream stuff which doesn't re-throw. regards, alexander. From boo at terekhov.de Tue Jan 13 17:31:55 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Tue, 13 Jan 2004 18:31:55 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <40028D9B.988E8C81@terekhov.de> <20040112164533.GA10141@diablo.name> <4002E53E.8840B94B@terekhov.de> <20040112190651.GA13854@diablo.name> <4003D7F3.1D5E3C32@terekhov.de> <20040113144051.GA27104@jupiter.cs.mu.oz.au> Message-ID: <40042B8B.C13C7B0F@terekhov.de> Fergus Henderson wrote: > > On 13-Jan-2004, Alexander Terekhov wrote: > > Ted Baker wrote: > > > On Mon, Jan 12, 2004 at 07:19:42PM +0100, Alexander Terekhov wrote: > > > > Now, in your model with cancellation ALWAYS disabled while running > > > > destructors (not only when acting upon a cancel request delivery... > > > > thread_exit aside for a moment), I'd have to add enable/disable RAII > > > > > > Please translate "RAII" for me? > > > > http://www.terekhov.de/DESIGN-futex-CV.cpp > > Alexander Terekhov again fails to communicate, > and when pressed substitutes a unrelated URL. Related URL. > > Ted, when he says "RAII", he means "Resource Acquision is Initialization". To me, this doesn't say much at all in the context of enabling/disabling and/or disabling/enabling cancellation via "RAII guard objects". The link OTOH does illustrate it (e.g. see "class cancel_off_guard"). > This is the name of a C++ idiom where code which acquires/releases > a resource is implemented as the constructor/destructor of a local object. http://www.artima.com/intv/modern3.html regards, alexander. From boo at terekhov.de Tue Jan 13 17:33:25 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Tue, 13 Jan 2004 18:33:25 +0100 Subject: [c++-pthreads] Restating the Jason model References: Message-ID: <40042BE5.19EA0F44@terekhov.de> Ben Hutchings wrote: > > Alexander Terekhov wrote: > > Dave Butenhof wrote: > > [...] > > > I'm not entirely sure I understand what you mean here by > > > '"intelligent" cancel request delivery". If we discussed it before > > > I don't recall that phrase. > > > > I mean "bool std::expected_exception() throw()" thing. > > "Intelligent" cancel request delivery means that cancellation points > > and async-cancel regions shall throw (deliver) > > std::thread_cancel_request only when cancellation is enabled (cancel > > state is equal to PTHREAD_CANCEL_ENABLE) AND > > "std::expected_exception()" is true at > > throw point (i.e. there's reachable catch handler for it in the > > dynamic context). > > So this is a way to avoid the overhead of disabling and enabling > cancellation in destructors (and elsewhere) - and it requires 2-phase > exception-handling, right? Right. And, overhead aside for a moment, it doesn't affect the thread cancel state. > > > Throw specs (dtors would have implicit throw() imposed on them) > > would act like "fences"; they should NOT have catch(...) effect. > > Am I right in thinking that aside from this wart 2-phase EH is > allowed but not required by the current C++ standard? http://groups.google.com/groups?selm=3EE7413D.E520E152%40web.de http://groups.google.com/groups?selm=3EEDD118.5065FAC9%40web.de regards, alexander. From boo at terekhov.de Tue Jan 13 17:34:48 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Tue, 13 Jan 2004 18:34:48 +0100 Subject: [c++-pthreads] Restating the Jason model References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> <4003E8D5.1030600@hp.com> Message-ID: <40042C38.BB781816@terekhov.de> Jason Merrill wrote: [...] > > Well, yes; although it also seems better to make one new rule for the new > > "thread terminating exceptions" rather than two separate new rules. > > There's no way to use the same rules for both, since we can't re-assert > deferred exit. Sure there's a way to use the same rules for both. Get rid of re-assert for cancellation. regards, alexander. From baker at cs.fsu.edu Tue Jan 13 19:31:19 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Tue, 13 Jan 2004 14:31:19 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <66A232D0-453B-11D8-ABB6-00039390D9E0@apple.com> References: <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <20040112191429.GB13854@diablo.name> <17A58ACA-4535-11D8-ABB6-00039390D9E0@apple.com> <20040112193528.GE13854@diablo.name> <66A232D0-453B-11D8-ABB6-00039390D9E0@apple.com> Message-ID: <20040113193119.GA3011@diablo.name> > I still think that uncatchable exceptions aren't the right answer. > Think > about the scope of the modifications that would be required: > 1. To a good first approximation, all code that catches and rethrows > exceptions, and that might be used in a cancellable thread, would > have to be modified. (The basis of my claim: if you have to do > some cleanup whenever an exception passes through your code, > you'll still have to do it for cancellations too.) > 1a. This isn't too different from saying that all code that catches > exceptions will have to be modified. Most code that catches > an exception ends up rethrowing it. > 2. Now think about what kind of modification we're talking about. > Again, to a good first approximation, cleanup is cleanup. So > the example I've give above would be rewritten: > catch(...) { > do_some_partial_cleanup(); > throw; > } > catch (CancellationException) { > do_some_partial_cleanup(); > throw; > } > We don't really want that to become recommended C++ style, > do we? Right, if you have code with catch(...)+throw for general purpose cleanup, if catch(...) does not catch cancellation you will miss the cleanup unless you put in explicit handler for cancellation, and that means (ugly, bad) code changes. On the other hand, if your style for providing cleanup code for exceptions in general uses a local object with destructor to achieve the cleanup (apparently the meaning of "RAII") then you *don't* want catch(...) to catch cancellation. {RAII protect; ... code that may be cancelled or throw other exceptions ... } class RAII { ... RAII () { ... allocate some stuff ... } ~RAII () { try { ... deallocate stuff ... } catch (...) { // want to give up quietly if deallocation causes an exception // but don't want to gobble up cancellation } } ... }; It seems you can't have it both ways. If catch(...) catches cancellation, then destructors that are intended catch everything without rethrowing can gobble up cancellation. On the other hand, if catch(...) does not catch cancellation, then destructors that are intended to catch everything and rethrow will be bypassed. I see better now why the idea of magically "reasserting" cancellation is appealing. That is, you can allow catch(...) to catch cancellation, and if it is gobbled up you get it back again. The only trick is to find exactly the right places to reassert the cancellation. --Ted From baker at cs.fsu.edu Tue Jan 13 19:43:12 2004 From: baker at cs.fsu.edu (Ted Baker) Date: Tue, 13 Jan 2004 14:43:12 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: References: <1073556051.28360.195.camel@felix.inria.fr> <4002A413.4090908@hp.com> <200401130452.19605.r-smith@ihug.co.nz> <20040112191839.GC13854@diablo.name> Message-ID: <20040113194312.GB3011@diablo.name> On Mon, Jan 12, 2004 at 04:56:39PM -0500, Jason Merrill wrote: > On Mon, 12 Jan 2004 14:18:39 -0500, Ted Baker wrote: > > >> ... It sounds as though a lot of people are seriously > >> considering the idea of allowing exceptions to escape from destructors. > >> .... it is _absolutely vital_ > >> that destructors never be allowed to throw under any circumstances. > > > > Right! This is what we decided with Ada. That is why > > cancellation (task abort) is disabled during exception propagation > > and finalization routines of controlled object (execution of > > destructors). > > Can a finalization routine throw a normal exception in Ada? No. If a finalizer tries to propagate an exception it is either ignored totally, or converted to a Program_Error exception at the next safe point for an exception to be propagated. (See detailed rules below.) --Ted [ARM 95]: It is a bounded error for a call on Finalize or Adjust to propagate an exception. The possible consequences depend on what action invoked the Finalize or Adjust operation: For a Finalize invoked as part of an assignment_statement, Program_Error is raised at that point. For an Adjust invoked as part of an assignment operation, any other adjustments due to be performed are performed, and then Program_Error is raised. For a Finalize invoked as part of a call on an instance of Unchecked_Deallocation, any other finalizations due to be performed are performed, and then Program_Error is raised. For a Finalize invoked by the transfer of control of an exit_, return_, goto_, or requeue_statement, Program_Error is raised no earlier than after the finalization of the master being finalized when the exception occurred, and no later than the point where normal execution would have continued. Any other finalizations due to be performed up to that point are performed before raising Program_Error. For a Finalize invoked by a transfer of control that is due to raising an exception, any other finalizations due to be performed for the same master are performed; Program_Error is raised immediately after leaving the master. For a Finalize invoked by a transfer of control due to an abort or selection of a terminate alternative, the exception is ignored; any other finalizations due to be performed are performed. From wil at bogo.xs4all.nl Tue Jan 13 20:19:15 2004 From: wil at bogo.xs4all.nl (Wil Evers) Date: Tue, 13 Jan 2004 21:19:15 +0100 Subject: [c++-pthreads] Restating the Jason model References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> <4003E8D5.1030600@hp.com> Message-ID: <400452C3.7060301@bogo.xs4all.nl> Jason Merrill wrote: > On Tue, 13 Jan 2004 07:47:17 -0500, Dave Butenhof wrote: > >>Cancellation should NOT be disabled in destructors? Did you mean to say >>that? > > Yes. Destructors can be run under two different situations: > > 1) when the object goes out of scope during normal execution; > 2) when unwinding the stack during exception handling. > > In #1, an exception thrown out of a destructor is propagated normally. In > the #2, it causes a call to terminate(). So we need to suppress > cancellation for #2, but not (necessarily) #1. It is true that an exception escaping from a destructor will not trigger immediate program termination when the program/thread in question is not unwinding; however, that doesn't mean there's nothing to worry about. The most likely symptom is a resource leak; in a multi-threaded environment, that could mean a deadlock. - Wil From boo at terekhov.de Tue Jan 13 22:00:17 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Tue, 13 Jan 2004 23:00:17 +0100 Subject: [c++-pthreads] Restating the Jason model References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> <4003E8D5.1030600@hp.com> Message-ID: <40046A71.F4513545@terekhov.de> Wil Evers wrote: > > Jason Merrill wrote: > > > On Tue, 13 Jan 2004 07:47:17 -0500, Dave Butenhof wrote: > > > >>Cancellation should NOT be disabled in destructors? Did you mean to say > >>that? > > > > Yes. Destructors can be run under two different situations: > > > > 1) when the object goes out of scope during normal execution; > > 2) when unwinding the stack during exception handling. This list is incomplete. > > > > In #1, an exception thrown out of a destructor is propagated normally. In > > the #2, it causes a call to terminate(). So we need to suppress > > cancellation for #2, but not (necessarily) #1. http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/cwg_active.html#265 > > It is true that an exception escaping from a destructor will not trigger > immediate program termination when the program/thread in question is not > unwinding; however, that doesn't mean there's nothing to worry about. Clearly, objects with throwing destructors aren't "normal citizens", so to say. Again, why don't YOU (together with Ross) complain that destructors are allowed currently to throw anything by default instead of having implicit throw() ("by default") ES/throw spec. imposed on them? And fixing semantics of throw specs [using them for "fencing" under 2-phase EH] is the next step, BTW. Look, once you'll have that, "intelligent" thread cancellation won't make your life any harder... well, unless you really insist on using blindly-swallowing catch(.../std::exception) in sort of "each and every destructor just in case". regards, alexander. From David.Butenhof at hp.com Wed Jan 14 12:55:49 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Wed, 14 Jan 2004 07:55:49 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <40042B8B.C13C7B0F@terekhov.de> References: <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <40028D9B.988E8C81@terekhov.de> <20040112164533.GA10141@diablo.name> <4002E53E.8840B94B@terekhov.de> <20040112190651.GA13854@diablo.name> <4003D7F3.1D5E3C32@terekhov.de> <20040113144051.GA27104@jupiter.cs.mu.oz.au> <40042B8B.C13C7B0F@terekhov.de> Message-ID: <40053C55.3080206@hp.com> Alexander Terekhov wrote: >Fergus Henderson wrote: > > >>On 13-Jan-2004, Alexander Terekhov wrote: >> >> >>>Ted Baker wrote: >>> >>> >>>>On Mon, Jan 12, 2004 at 07:19:42PM +0100, Alexander Terekhov wrote: >>>> >>>> >>>>>Now, in your model with cancellation ALWAYS disabled while running >>>>>destructors (not only when acting upon a cancel request delivery... >>>>>thread_exit aside for a moment), I'd have to add enable/disable RAII >>>>> >>>>> >>>>Please translate "RAII" for me? >>>> >>>> >>>http://www.terekhov.de/DESIGN-futex-CV.cpp >>> >>> >>Alexander Terekhov again fails to communicate, >>and when pressed substitutes a unrelated URL. >> >> >Related URL. > > Yes, related; but indirectly, and with no context or explanation of the relationship. Only those who already understand the abbreviation could have understood the relevance, and they didn't need the example. That's why Fergus claims (correctly) that you "failed to communicate". Communication is not the trivialized process of throwing "data balls" at someone, Alexander. Information content is required, and it must further be structured in a way that leads to understanding. >>Ted, when he says "RAII", he means "Resource Acquision is Initialization". >> >> >To me, this doesn't say much at all in the context of enabling/disabling >and/or disabling/enabling cancellation via "RAII guard objects". The link >OTOH does illustrate it (e.g. see "class cancel_off_guard"). > > OK, so let's be more specific, Alexander. You would have been fine if you'd explained what the abbreviation stands for, what that phrase means, AND offered the link to some code showing how it's used. But while your link used the abbreviation "RAII" once or twice, it never DEFINED oir EXPLAINED the term and was therefore not particularly useful to anyone not already familiar with it. >>This is the name of a C++ idiom where code which acquires/releases >>a resource is implemented as the constructor/destructor of a local object. >> >> >http://www.artima.com/intv/modern3.html > > Did you notice the interviewer didn't even get the full phrase right in the initial question? ;-) -- /--------------------[ 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 ]---/ From David.Butenhof at hp.com Wed Jan 14 13:04:06 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Wed, 14 Jan 2004 08:04:06 -0500 Subject: [c++-pthreads] Restating the Jason model In-Reply-To: <40042C38.BB781816@terekhov.de> References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> <4003E8D5.1030600@hp.com> <40042C38.BB781816@terekhov.de> Message-ID: <40053E46.6010900@hp.com> Alexander Terekhov wrote: >Jason Merrill wrote: >[...] > > >>>Well, yes; although it also seems better to make one new rule for the new >>>"thread terminating exceptions" rather than two separate new rules. >>> >>> >>There's no way to use the same rules for both, since we can't re-assert >>deferred exit. >> >> >Sure there's a way to use the same rules for both. Get rid of re-assert >for cancellation. > > And it can work the other way, too. It's just as easy to re-assert exit as cancel; instead of calling pthread_cancel() on the current thread when the cancel exception is destroyed, you call pthread_exit() when the exit exception is destroyed. You'd need to retain the original exit status (void*) value, but there's no reason that can't be part of the exit exception object. Now, whether you WANT TO or SHOULD re-assert either condition is clearly a contentious issue and not likely to be resolved any any consensus soon. But either is technically feasible if it's decided to be correct and desirable. Just to restate, though; I DON'T think that re-asserting is "correct", but at this time I'm willing to grant that given common practice within the STL and presumably application code, it may be necessary all the same. -- /--------------------[ 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 ]---/ From boo at terekhov.de Wed Jan 14 15:50:42 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Wed, 14 Jan 2004 16:50:42 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <40028D9B.988E8C81@terekhov.de> <20040112164533.GA10141@diablo.name> <4002E53E.8840B94B@terekhov.de> <20040112190651.GA13854@diablo.name> <4003D7F3.1D5E3C32@terekhov.de> <20040113144051.GA27104@jupiter.cs.mu.oz.au> <40042B8B.C13C7B0F@terekhov.de> <40053C55.3080206@hp.com> Message-ID: <40056552.80B67DE4@terekhov.de> Dave Butenhof wrote: [...] > >>Ted, when he says "RAII", he means "Resource Acquision is Initialization". > >> > >> > >To me, this doesn't say much at all in the context of enabling/disabling > >and/or disabling/enabling cancellation via "RAII guard objects". The link > >OTOH does illustrate it (e.g. see "class cancel_off_guard"). > > > > > OK, so let's be more specific, Alexander. You would have been fine ... Specifically, I meant something along the lines of class cancel_OFF_to_ON_guard { //*** unimplemented since it's non-copyable/non-copy-constructible cancel_OFF_to_ON_guard(const cancel_OFF_to_ON_guard &); cancel_OFF_to_ON_guard & operator=(const cancel_OFF_to_ON_guard &); public: cancel_OFF_to_ON_guard() throw() { int status; status = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &status); assert(!status); } ~cancel_OFF_to_ON_guard() throw() { int status; status = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &status); assert(!status); } }; Okay now? regards, alexander. From boo at terekhov.de Wed Jan 14 15:53:40 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Wed, 14 Jan 2004 16:53:40 +0100 Subject: [c++-pthreads] Restating the Jason model References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> <4003E8D5.1030600@hp.com> Message-ID: <40056604.11380101@terekhov.de> Dave Butenhof wrote: [...] > >Sure there's a way to use the same rules for both. Get rid of re-assert > >for cancellation. > > > And it can work the other way, too. It's just as easy to re-assert exit > as cancel; instead of calling pthread_cancel() on the current thread > when the cancel exception is destroyed, you call pthread_exit() when the > exit exception is destroyed. You'd need to retain the original exit > status (void*) value, but there's no reason that can't be part of the > exit exception object. Well, colliding auto-rethrow (when catch() exits with another throw) aside for a moment, consider: http://aspn.activestate.com/ASPN/Mail/Message/1153458 http://aspn.activestate.com/ASPN/Mail/Message/1156813 > > Now, whether you WANT TO or SHOULD re-assert either condition is clearly > a contentious issue and not likely to be resolved any any consensus > soon. But either is technically feasible if it's decided to be correct > and desirable. > > Just to restate, though; I DON'T think that re-asserting is "correct", Agreed. It may seem tempting on the surface, but is totally incorrect. > but at this time I'm willing to grant that given common practice within > the STL and presumably application code, it may be necessary all the same. Well, one thing is clear: this is the second time "sticky cancel" is proposed. That idea was proposed first at boost about two years ago. http://aspn.activestate.com/ASPN/Mail/Message/1156812 The trend, you know. regards, alexander. From jason at redhat.com Thu Jan 15 21:31:44 2004 From: jason at redhat.com (Jason Merrill) Date: Thu, 15 Jan 2004 16:31:44 -0500 Subject: [c++-pthreads] Restating the Jason model In-Reply-To: <40053E46.6010900@hp.com> (Dave Butenhof's message of "Wed, 14 Jan 2004 08:04:06 -0500") References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> <4003E8D5.1030600@hp.com> <40042C38.BB781816@terekhov.de> <40053E46.6010900@hp.com> Message-ID: On Wed, 14 Jan 2004 08:04:06 -0500, Dave Butenhof wrote: >>Jason Merrill wrote: >>>There's no way to use the same rules for both, since we can't re-assert >>>deferred exit. > It's just as easy to re-assert exit as cancel; instead of calling > pthread_cancel() on the current thread when the cancel exception is > destroyed, you call pthread_exit() when the exit exception is > destroyed. You'd need to retain the original exit status (void*) value, > but there's no reason that can't be part of the exit exception object. Good point. I like that better than terminate(). Jason From jason at redhat.com Thu Jan 15 22:01:57 2004 From: jason at redhat.com (Jason Merrill) Date: Thu, 15 Jan 2004 17:01:57 -0500 Subject: [c++-pthreads] Restating the Jason model In-Reply-To: <400452C3.7060301@bogo.xs4all.nl> (Wil Evers's message of "Tue, 13 Jan 2004 21:19:15 +0100") References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> <4003E8D5.1030600@hp.com> <400452C3.7060301@bogo.xs4all.nl> Message-ID: On Tue, 13 Jan 2004 21:19:15 +0100, Wil Evers wrote: > Jason Merrill wrote: > >> On Tue, 13 Jan 2004 07:47:17 -0500, Dave Butenhof wrote: > > >>>Cancellation should NOT be disabled in destructors? Did you mean to say >>>that? >> Yes. Destructors can be run under two different situations: 1) when the >> object goes out of scope during normal execution; 2) when unwinding the >> stack during exception handling. >> In #1, an exception thrown out of a destructor is propagated normally. In >> the #2, it causes a call to terminate(). So we need to suppress >> cancellation for #2, but not (necessarily) #1. > > It is true that an exception escaping from a destructor will not trigger > immediate program termination when the program/thread in question is not > unwinding; however, that doesn't mean there's nothing to worry about. The > most likely symptom is a resource leak; in a multi-threaded environment, > that could mean a deadlock. True, throwing out of a destructor is more likely to cause trouble than throwing from other places; a destructor like Guard::~Guard() { write (0, "releasing", sizeof ("releasing"); release (resource); } will fail to actually release the resource if a cancel is delivered during the call to write(). It's certainly possible to restructure code to avoid this problem, but I can see this being a source of hard-to-debug problems. This is a somewhat persuasive case for disabling cancel during all destructors, but I'm not sure it outweighs the overhead involved. In the model I proposed, only the EH runtime needs to know about cancellation. Jason From wil at bogo.xs4all.nl Fri Jan 16 15:14:18 2004 From: wil at bogo.xs4all.nl (Wil Evers) Date: Fri, 16 Jan 2004 16:14:18 +0100 Subject: [c++-pthreads] Restating the Jason model References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> <4003E8D5.1030600@hp.com> <400452C3.7060301@bogo.xs4all.nl> Message-ID: <4007FFCA.3000807@bogo.xs4all.nl> Jason Merrill wrote: > On Tue, 13 Jan 2004 21:19:15 +0100, Wil Evers wrote: > >>It is true that an exception escaping from a destructor will not trigger >>immediate program termination when the program/thread in question is not >>unwinding; however, that doesn't mean there's nothing to worry about. The >>most likely symptom is a resource leak; in a multi-threaded environment, >>that could mean a deadlock. > > True, throwing out of a destructor is more likely to cause trouble than > throwing from other places; a destructor like > > Guard::~Guard() > { > write (0, "releasing", sizeof ("releasing"); > release (resource); > } > > will fail to actually release the resource if a cancel is delivered during > the call to write(). It's certainly possible to restructure code to avoid > this problem, but I can see this being a source of hard-to-debug problems. The situation is actually a bit more complicated: many container implementations, including the ones in the C++ standard library, assume that the destructors of their elements do not throw. Most likely, an exception escaping from one element's destructor will cause the remaining elements to be abandoned (and not destroyed at all). > This is a somewhat persuasive case for disabling cancel during all > destructors, but I'm not sure it outweighs the overhead involved. In the > model I proposed, only the EH runtime needs to know about cancellation. As I tried to say before, my main objection to your model is that, depending on the thread's dynamic context, destructors will behave differently when hitting a cancellation point. Sometimes, your model will protect us from cancellation exceptions, but at other times it won't, and I fear the confusion that might result from this. I agree that the overhead of disabling cancellation in all destructors could become a problem. IMO, if we have sticky cancellation, the EH runtime doesn't need to be disable cancellation either. Most destructors only deal with releasing memory anyway, and don't need to call any throwing functions. Some destructors do more complicated things, involving calls to functions that *do* throw exceptions. These destructors must take extra precautions and are harder to implement, but this has always been the case - ever since exceptions were added to C++. The thing that changes because of cancellation is that some functions that didn't throw before will now be licensed to do so. It seems to me that most of the participants on this mailing list accept that this will require changes to existing code. That could include adding a try/catch block to destructor code. - Wil From boo at terekhov.de Fri Jan 16 16:02:57 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Fri, 16 Jan 2004 17:02:57 +0100 Subject: [c++-pthreads] Restating the Jason model References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> <4003E8D5.1030600@hp.com> Message-ID: <40080B31.4F1574E4@terekhov.de> Wil Evers wrote: [...] > The situation is actually a bit more complicated: many container > implementations, including the ones in the C++ standard library, assume > that the destructors of their elements do not throw. Most likely, an > exception escaping from one element's destructor will cause the > remaining elements to be abandoned (and not destroyed at all). Here's sort of solution: template 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); } /* THIS DOESN'T CATCH *UNEXPECTED* EXCEPTIONS */ /* THIS DOES RETHROW EXCEPTIONS AUTOMATICALLY */ /* NOTHING ELSE CAN BE THROWN FROM THIS SCOPE */ action_on_propagation_of(...) { for (; _Next != _First; ++_Next) _Destroy(&*_Next); } } The idea is that if a destructor here attempts to exit with an exception then std::unexpected() will be invoked at throw point. In a way, using hypothetical "no_throw {}" (in addition to hypothetical "action_on_propagation_of()") it is equivalent to template 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); } /* THIS DOESN'T CATCH *UNEXPECTED* EXCEPTIONS */ /* THIS DOES RETHROW EXCEPTIONS AUTOMATICALLY */ /* NOTHING ELSE CAN BE THROWN FROM THIS SCOPE */ action_on_propagation_of(...) { no_throw { for (; _Next != _First; ++_Next) _Destroy(&*_Next); } } } presuming 2-phase (and a bit "extended") EH, of course. > > > This is a somewhat persuasive case for disabling cancel during all > > destructors, but I'm not sure it outweighs the overhead involved. In the > > model I proposed, only the EH runtime needs to know about cancellation. > > As I tried to say before, my main objection to your model is that, > depending on the thread's dynamic context, destructors will behave > differently when hitting a cancellation point. Sometimes, your model > will protect us from cancellation exceptions, but at other times it > won't, and I fear the confusion that might result from this. Not that I really like Jason's model, but "confusion" does already exist in POSIX; I mean pthread_cleanup_pop(!0) "destructors". > > I agree that the overhead of disabling cancellation in all destructors > could become a problem. That's good. > IMO, if we have sticky cancellation, the EH > runtime doesn't need to be disable cancellation either. But it has really nothing to do with stickiness. [...] > The thing that changes because of cancellation is that some functions > that didn't throw before will now be licensed to do so. It seems to me > that most of the participants on this mailing list accept that this will > require changes to existing code. That could include adding a try/catch > block to destructor code. If C++ would impose implicit ("by default") throw() specs on all destructors AND adopt 2-phase EH with "intelligent" cancel delivery, things like object::~object() { fclose(/*...*/); } won't need any changes... and it won't impose any runtime overhead (on any modern PC-mapped EH impl) as long as you "run across" cancellation points disabled via throw specs WITHOUT pending cancel request. regards, alexander. From dave at boost-consulting.com Sat Jan 17 00:26:32 2004 From: dave at boost-consulting.com (David Abrahams) Date: Fri, 16 Jan 2004 19:26:32 -0500 Subject: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> Message-ID: Dave Butenhof writes: >>The problems with catch(...) eating all exceptions are maybe not as >>bad as you think. As a matter of fact, there are vanishingly few >>exceptions that demand special recovery actions that wouldn't work for >>all other exceptions. Systems designed that way tend towards >>fragility. >> >> > I see an immense difference between a pragmatic statement that "in > practice there seem to be few exceptions" and something on which > cross-platform, mixed-language, modular environment programmers can > depend as a law. C++ does not say that "all exceptions can be > finalized and recovered fully by performing these steps". To presume > they can is fragile. It also doesn't say "no destructors will throw exceptions", but we generally rely on them not to, because it makes programs hard to write. There are a whole host of things we leave up to good programming practice, most of which don't have to do with EH. The only way handling an exception with catch(...) can be harmful (other than to debugging; but that's a different issue) is if the throwing code *requires* that the catching code take some special condition-specific cleanup actions in order to ensure program correctness. I don't think I've seen such an arrangement under any error-handling system, though it wouldn't surprise me if an example existed somewhere. IMO it's much more reasonable to say "don't do that" with exceptions than to tell people not to use catch(...). It's easy enough to force that special cleanup action in the exception object's destructor a la JasonMerrillCancellation in the rare case where something of the sort might be needed. But this is all OT for the threads discussion, innit? -- Dave Abrahams Boost Consulting www.boost-consulting.com From dave at boost-consulting.com Sat Jan 17 00:29:06 2004 From: dave at boost-consulting.com (David Abrahams) Date: Fri, 16 Jan 2004 19:29:06 -0500 Subject: Restating the Jason model References: Message-ID: "Ben Hutchings" writes: >> Throw specs (dtors would have implicit throw() imposed on them) >> would act like "fences"; they should NOT have catch(...) effect. > > Am I right in thinking that aside from this wart 2-phase EH is > allowed but not required by the current C++ standard? Yes. -- Dave Abrahams Boost Consulting www.boost-consulting.com From dave at boost-consulting.com Sat Jan 17 00:35:35 2004 From: dave at boost-consulting.com (David Abrahams) Date: Fri, 16 Jan 2004 19:35:35 -0500 Subject: thread-safety definition References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <40028D9B.988E8C81@terekhov.de> <20040112164533.GA10141@diablo.name> Message-ID: Ted Baker writes: > Yes, but it need not be a *huge* amount of cycles. Protection > against cancellation can be done via the equivalent of setting one > bit in a thread descriptor Setting one bit per nontrivial destructor call would be a performance disaster for many programs. -- Dave Abrahams Boost Consulting www.boost-consulting.com From dave at boost-consulting.com Sat Jan 17 00:38:26 2004 From: dave at boost-consulting.com (David Abrahams) Date: Fri, 16 Jan 2004 19:38:26 -0500 Subject: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <30BBBA2F-452D-11D8-ABB6-00039390D9E0@apple.com> Message-ID: Matt Austern writes: > On Jan 12, 2004, at 5:41 AM, Dave Butenhof wrote: > >> I think I disagree, at least philsophically, with the >> characterization of the model as "fragile". But I think I also >> understand what you mean; and the problem isn't with the model, but >> rather with the effect of that model on existing code that >> all-too-casually and agressively eats exceptions it doesn't >> understand. I think there are vanishingly few circumstances where a >> blind catch(...) without an unconditional re-throw should be >> considered "legitimate". If you don't completely understand what an >> exception means, you cannot claim to have completely recovered, and >> therefore cannot reasonably finalize propagation. (And when you >> catch anonymously, you can't possibly understand what they mean >> since you can't even identify them.) On the other hand, regardless >> of whether the semantics are meaningful or reasonable, the syntax is >> legal and apparently (unfortunately) in common use, so I can't >> dispute that you need to consider that. > > One legitimate use for that pattern is in mixed-language programming, > when you write a little adapter to turn the error reporting mechanism > used by the C++ part into an error reporting mechanism that the C part > can understand. I think that's reasonable for most of the things > exceptions are used for. It isn't reasonable for thread cancellation, > because C needs to know about thread cancellation too. Well, how do you propagate the cancellation to 'C'? Surely not by letting the C++ exception leak out into C-land, except on very special systems where the C runtime knows about EH. In that case, you have to finalize the exception and frob some 'C' cancellation reporting mechanism. -- Dave Abrahams Boost Consulting www.boost-consulting.com From dave at boost-consulting.com Sat Jan 17 00:41:19 2004 From: dave at boost-consulting.com (David Abrahams) Date: Fri, 16 Jan 2004 19:41:19 -0500 Subject: thread-safety definition References: <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <40028D9B.988E8C81@terekhov.de> <20040112164533.GA10141@diablo.name> <4002E53E.8840B94B@terekhov.de> <20040112190651.GA13854@diablo.name> Message-ID: Ted Baker writes: > The overhead for enabling/disabling cancellation is not paid on what > you are calling "throw specs" until an exception is thrown > and the code is actually executed to process the exception. > > It would be paid, however, on all (groups of) destructors, > regardless of when they are executed. Hmm. A sufficiently clever C++ implementation could use the same mechanisms used to search for exception handlers to determine whether a destructor is executing at the point where the cancellation occurs, with no runtime overhead for executing destructors. -- Dave Abrahams Boost Consulting www.boost-consulting.com From dave at boost-consulting.com Sat Jan 17 00:32:24 2004 From: dave at boost-consulting.com (David Abrahams) Date: Fri, 16 Jan 2004 19:32:24 -0500 Subject: Restating the Jason model References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <20040112130546.GA29001@jupiter.cs.mu.oz.au> <20040112164855.GB10141@diablo.name> <4003E8D5.1030600@hp.com> <400452C3.7060301@bogo.xs4all.nl> Message-ID: Wil Evers writes: > Jason Merrill wrote: > >> On Tue, 13 Jan 2004 07:47:17 -0500, Dave Butenhof wrote: > > >>>Cancellation should NOT be disabled in destructors? Did you mean to say >>>that? >> Yes. Destructors can be run under two different situations: 1) >> when the object goes out of scope during normal execution; 2) when >> unwinding the stack during exception handling. >> In #1, an exception thrown out of a destructor is propagated >> normally. In >> the #2, it causes a call to terminate(). So we need to suppress >> cancellation for #2, but not (necessarily) #1. > > It is true that an exception escaping from a destructor will not > trigger immediate program termination when the program/thread in > question is not unwinding; however, that doesn't mean there's nothing > to worry about. The most likely symptom is a resource leak; Yes, but it could be arbitrarily bad. This is really the same point I've been making from the beginning: functions which are assumed not to throw can't suddenly start throwing; it's just as hard to manage that as to manage asynchronous cancellations. Everybody expects destructors not to throw. -- Dave Abrahams Boost Consulting www.boost-consulting.com From jason at redhat.com Sat Jan 17 01:17:24 2004 From: jason at redhat.com (Jason Merrill) Date: Fri, 16 Jan 2004 20:17:24 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: (David Abrahams's message of "Fri, 16 Jan 2004 19:41:19 -0500") References: <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <40028D9B.988E8C81@terekhov.de> <20040112164533.GA10141@diablo.name> <4002E53E.8840B94B@terekhov.de> <20040112190651.GA13854@diablo.name> Message-ID: On Fri, 16 Jan 2004 19:41:19 -0500, David Abrahams wrote: > Ted Baker writes: > >> The overhead for enabling/disabling cancellation is not paid on what >> you are calling "throw specs" until an exception is thrown >> and the code is actually executed to process the exception. >> >> It would be paid, however, on all (groups of) destructors, >> regardless of when they are executed. > > Hmm. A sufficiently clever C++ implementation could use the same > mechanisms used to search for exception handlers to determine whether > a destructor is executing at the point where the cancellation occurs, > with no runtime overhead for executing destructors. True. Jason From gdr at integrable-solutions.net Sat Jan 17 01:12:54 2004 From: gdr at integrable-solutions.net (Gabriel Dos Reis) Date: 17 Jan 2004 02:12:54 +0100 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> Message-ID: David Abrahams writes: | Dave Butenhof writes: | | >>The problems with catch(...) eating all exceptions are maybe not as | >>bad as you think. As a matter of fact, there are vanishingly few | >>exceptions that demand special recovery actions that wouldn't work for | >>all other exceptions. Systems designed that way tend towards | >>fragility. | >> | >> | > I see an immense difference between a pragmatic statement that "in | > practice there seem to be few exceptions" and something on which | > cross-platform, mixed-language, modular environment programmers can | > depend as a law. C++ does not say that "all exceptions can be | > finalized and recovered fully by performing these steps". To presume | > they can is fragile. | | It also doesn't say "no destructors will throw exceptions", but we | generally rely on them not to, because it makes programs hard to | write. There are a whole host of things we leave up to good | programming practice, most of which don't have to do with EH. Agreed, but we can't specify things like that. If we assume some working hypothesis to hold, then we have to make that assumption clear in the specification. I think that is the point Dave Butenhof was making. -- Gaby From dave at boost-consulting.com Sat Jan 17 03:26:23 2004 From: dave at boost-consulting.com (David Abrahams) Date: Fri, 16 Jan 2004 22:26:23 -0500 Subject: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> Message-ID: Gabriel Dos Reis writes: > David Abrahams writes: > > | Dave Butenhof writes: > | > | >>The problems with catch(...) eating all exceptions are maybe not as > | >>bad as you think. As a matter of fact, there are vanishingly few > | >>exceptions that demand special recovery actions that wouldn't work for > | >>all other exceptions. Systems designed that way tend towards > | >>fragility. > | >> > | >> > | > I see an immense difference between a pragmatic statement that "in > | > practice there seem to be few exceptions" and something on which > | > cross-platform, mixed-language, modular environment programmers can > | > depend as a law. C++ does not say that "all exceptions can be > | > finalized and recovered fully by performing these steps". To presume > | > they can is fragile. > | > | It also doesn't say "no destructors will throw exceptions", but we > | generally rely on them not to, because it makes programs hard to > | write. There are a whole host of things we leave up to good > | programming practice, most of which don't have to do with EH. > > Agreed, but we can't specify things like that. If we assume some > working hypothesis to hold, then we have to make that assumption clear > in the specification. I think that is the point Dave Butenhof was > making. I didn't think so, at all. I hope Dave will clarify on his own, though, especially if I'm wrong. AFAICT the discussion is about whether it makes sense to support programs which do catch(...) without rethrowing, and if so, how. Right now we're discussing a morality issue: "is it inherently evil to catch(...) without rethrowing?" My point is that one could just as well (and perhaps more justifiably) claim that it's evil to throw any exception that can't be dealt with via a catch(...) block that doesn't rethrow, where that block was designed to handle, say, bad_alloc.** (**) this isn't really about the exception objects, but about the conditions of program state they represent. -- Dave Abrahams Boost Consulting www.boost-consulting.com From dave at boost-consulting.com Sat Jan 17 03:27:54 2004 From: dave at boost-consulting.com (David Abrahams) Date: Fri, 16 Jan 2004 22:27:54 -0500 Subject: thread-safety definition References: <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <20040112010213.GB14371@jupiter.cs.mu.oz.au> <20040112114014.GC5193@diablo.name> <40028D9B.988E8C81@terekhov.de> <20040112164533.GA10141@diablo.name> Message-ID: David Abrahams writes: > Ted Baker writes: > >> Yes, but it need not be a *huge* amount of cycles. Protection >> against cancellation can be done via the equivalent of setting one >> bit in a thread descriptor > > Setting one bit per nontrivial destructor call would be a performance > disaster for many programs. But fortunately we all seem to be coming to the conclusion that it can happen at virtually no cost (see Ted B's "static mapping" remark). -- Dave Abrahams Boost Consulting www.boost-consulting.com From gdr at integrable-solutions.net Sat Jan 17 09:03:43 2004 From: gdr at integrable-solutions.net (Gabriel Dos Reis) Date: 17 Jan 2004 10:03:43 +0100 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> Message-ID: David Abrahams writes: | Gabriel Dos Reis writes: | | > David Abrahams writes: | > | > | Dave Butenhof writes: | > | | > | >>The problems with catch(...) eating all exceptions are maybe not as | > | >>bad as you think. As a matter of fact, there are vanishingly few | > | >>exceptions that demand special recovery actions that wouldn't work for | > | >>all other exceptions. Systems designed that way tend towards | > | >>fragility. | > | >> | > | >> | > | > I see an immense difference between a pragmatic statement that "in | > | > practice there seem to be few exceptions" and something on which | > | > cross-platform, mixed-language, modular environment programmers can | > | > depend as a law. C++ does not say that "all exceptions can be | > | > finalized and recovered fully by performing these steps". To presume | > | > they can is fragile. | > | | > | It also doesn't say "no destructors will throw exceptions", but we | > | generally rely on them not to, because it makes programs hard to | > | write. There are a whole host of things we leave up to good | > | programming practice, most of which don't have to do with EH. | > | > Agreed, but we can't specify things like that. If we assume some | > working hypothesis to hold, then we have to make that assumption clear | > in the specification. I think that is the point Dave Butenhof was | > making. | | I didn't think so, at all. I hope Dave will clarify on his own, | though, especially if I'm wrong. | | AFAICT the discussion is about whether it makes sense to support | programs which do catch(...) without rethrowing, and if so, how. | Right now we're discussing a morality issue: "is it inherently evil to | catch(...) without rethrowing?" I understood the scope of the discussion. | My point is that one could just as well (and perhaps more justifiably) | claim that it's evil to throw any exception that can't be dealt with | via a catch(...) block that doesn't rethrow, where that block was | designed to handle, say, bad_alloc.** Well, let me rephrase what I was saying. >From language point of view, it is permitted to do catch(...) without rethrowing. From practical point of view one may declare such style either utterly nonsense or blessed. Either way, that working assumption should be stated clearly, .e.g. This POSIX binding specification assumes that a catch-all handler that does not rethrow belongs to hell. or In this POSIX binding specification, a catch-all handler that does rethrow has a blessed behaviour. Such clear statement, from portability point of view, has different impact than an unspoken assumption. That is how I understood Dave Butenhof's statement. But, you're right that he might want to clarify on this own. | (**) this isn't really about the exception objects, but about the | conditions of program state they represent. Yes, I understood that too. -- Gaby From dave at boost-consulting.com Sat Jan 17 14:28:58 2004 From: dave at boost-consulting.com (David Abrahams) Date: Sat, 17 Jan 2004 09:28:58 -0500 Subject: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> Message-ID: Gabriel Dos Reis writes: > Well, let me rephrase what I was saying. > > From language point of view, it is permitted to do catch(...) without > rethrowing. From practical point of view one may declare such style > either utterly nonsense or blessed. Either way, that working > assumption should be stated clearly, .e.g. > > This POSIX binding specification assumes that a catch-all handler > that does not rethrow belongs to hell. > > or > > In this POSIX binding specification, a catch-all handler that does > rethrow has a blessed behaviour. ^------"not" ? > > Such clear statement, from portability point of view, has different > impact than an unspoken assumption. I think I heard what you were trying to say. Maybe I'm just blind to the answer, but while I think it's possible (though usually unconstructive) to formally label a particular programming construct evil, I don't think it's possible to formally bless a programming construct. Whether "catch-all without rethrow", or "while(1) loop without break", or "delete this", etc are going to be OK is dependent on program context, even if it is OK "in principle" in the POSIX environment. What can a formal specification say to bless a programming construct that could, depending on how it's used, lead to undefined or otherwise bad behavior? -- Dave Abrahams Boost Consulting www.boost-consulting.com From wil at bogo.xs4all.nl Sat Jan 17 16:25:19 2004 From: wil at bogo.xs4all.nl (Wil Evers) Date: Sat, 17 Jan 2004 17:25:19 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <30BBBA2F-452D-11D8-ABB6-00039390D9E0@apple.com> Message-ID: <400961EF.90309@bogo.xs4all.nl> David Abrahams wrote: > Well, how do you propagate the cancellation to 'C'? Surely not by > letting the C++ exception leak out into C-land, except on very special > systems where the C runtime knows about EH. In that case, you have to > finalize the exception and frob some 'C' cancellation reporting > mechanism. Very special systems? The C binding for POSIX threads *requires* a stack unwinding mechanism. In a pure C environment, it can be implemented by maintaining a runtime stack of registered cleanup routines. In a mixed-language environment, the solution is to implement a common EH framework in the platform's ABI. I was assuming the participants on this list were taking the existence of such a common EH framework for granted. In such an environment, one should expect cancellation exceptions (and, perhaps, other exceptions too) thrown from code written in C++ to leak into C-land, and have compatible behaviour. - Wil From gdr at integrable-solutions.net Sat Jan 17 19:23:40 2004 From: gdr at integrable-solutions.net (Gabriel Dos Reis) Date: 17 Jan 2004 20:23:40 +0100 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> Message-ID: David Abrahams writes: | Gabriel Dos Reis writes: | | > Well, let me rephrase what I was saying. | > | > From language point of view, it is permitted to do catch(...) without | > rethrowing. From practical point of view one may declare such style | > either utterly nonsense or blessed. Either way, that working | > assumption should be stated clearly, .e.g. | > | > This POSIX binding specification assumes that a catch-all handler | > that does not rethrow belongs to hell. | > | > or | > | > In this POSIX binding specification, a catch-all handler that does | > rethrow has a blessed behaviour. | ^------"not" ? | > | > Such clear statement, from portability point of view, has different | > impact than an unspoken assumption. | | I think I heard what you were trying to say. Maybe I'm just blind to | the answer, but while I think it's possible (though usually | unconstructive) to formally label a particular programming construct | evil, I don't think it's possible to formally bless a programming | construct. I think, you're reading "bless" and "hell" too literally :-). "belongs to hell" is another spelling I was using for "has unspecified/undefined behaviour", meaning that the binding specification does not intend to support such program as portable. Similarly, "bless" was used for "specified behaviour". | Whether "catch-all without rethrow", or "while(1) loop | without break", or "delete this", etc are going to be OK is dependent | on program context, even if it is OK "in principle" in the POSIX | environment. Of course, and the keyword here is "in principle". If a binding says that "catch-all without rethrow" has undefined behaviour under conditions foo, the only thing it means for sure is that it is not a *portable* construct if foo do not hold. Even if foo are not met, the construct might be fine for the program depending on the guarantee made by the _particular_ implementation it is using. And you've been doing that all the time. | What can a formal specification say to bless a | programming construct that could, depending on how it's used, lead to | undefined or otherwise bad behavior? It can say under which general circumstances it has defined behaviour. -- Gaby From austern at apple.com Sat Jan 17 20:35:18 2004 From: austern at apple.com (Matt Austern) Date: Sat, 17 Jan 2004 12:35:18 -0800 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> Message-ID: On Jan 16, 2004, at 7:26 PM, David Abrahams wrote: > AFAICT the discussion is about whether it makes sense to support > programs which do catch(...) without rethrowing, and if so, how. > Right now we're discussing a morality issue: "is it inherently evil to > catch(...) without rethrowing?" And my position on the morality question would be that yes, it is inherently evil, but that sometimes programs have to do evil things. I can think of a couple of designs that rely on being able to catch exceptions and not rethrow them, and I'm sure you can too. (Mostly designs where the catch(...) is part of an adapter layer that translates between one kind of error reporting mechanism and another one. The (...) will get translated into something like "unknown error".) And I agree with what you said in an earlier message: the big question is, in designs like that, whether thread cancellation needs to be translated too and whether it needs to be translated in the same way. I'm leaning toward the idea that thread cancellation should be an ordinary C++ exception, that we should name it, that it should be catchable in the ordinary ways, and that people who need to be able to catch everything but a cancellation exception have some perfectly good mechanisms for doing that. Of course, this still doesn't resolve everything. The question still remains: what are the cancellation points, and are there any circumstances (e.g. during the unwinding process itself) under which cancellation is temporarily disabled? --Matt From wil at bogo.xs4all.nl Sun Jan 18 06:38:48 2004 From: wil at bogo.xs4all.nl (Wil Evers) Date: Sun, 18 Jan 2004 07:38:48 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> Message-ID: <400A29F8.7010404@bogo.xs4all.nl> Matt Austern wrote: > On Jan 16, 2004, at 7:26 PM, David Abrahams wrote: > >> AFAICT the discussion is about whether it makes sense to support >> programs which do catch(...) without rethrowing, and if so, how. >> Right now we're discussing a morality issue: "is it inherently evil to >> catch(...) without rethrowing?" > > And my position on the morality question would be that yes, it > is inherently evil, but that sometimes programs have to do evil > things. I can think of a couple of designs that rely on being > able to catch exceptions and not rethrow them, and I'm sure you > can too. (Mostly designs where the catch(...) is part of an > adapter layer that translates between one kind of error reporting > mechanism and another one. The (...) will get translated into > something like "unknown error".) Do you think that designs that rely on catch(...) without rethrow to comply with the 'destructors must not throw' principle are unreasonable? If so, which alternative design do you suggest? - Wil From David.Butenhof at hp.com Tue Jan 20 12:37:24 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Tue, 20 Jan 2004 07:37:24 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> Message-ID: <400D2104.5080605@hp.com> David Abrahams wrote: >Gabriel Dos Reis writes: > > >>David Abrahams writes: >> >>| Dave Butenhof writes: >>| >>| >>The problems with catch(...) eating all exceptions are maybe not as >>| >>bad as you think. As a matter of fact, there are vanishingly few >>| >>exceptions that demand special recovery actions that wouldn't work for >>| >>all other exceptions. Systems designed that way tend towards >>| >>fragility. >>| >> >>| > I see an immense difference between a pragmatic statement that "in >>| > practice there seem to be few exceptions" and something on which >>| > cross-platform, mixed-language, modular environment programmers can >>| > depend as a law. C++ does not say that "all exceptions can be >>| > finalized and recovered fully by performing these steps". To presume >>| > they can is fragile. >>| >>| It also doesn't say "no destructors will throw exceptions", but we >>| generally rely on them not to, because it makes programs hard to >>| write. There are a whole host of things we leave up to good >>| programming practice, most of which don't have to do with EH. >> >>Agreed, but we can't specify things like that. If we assume some >>working hypothesis to hold, then we have to make that assumption clear >>in the specification. I think that is the point Dave Butenhof was >>making. >> >> >I didn't think so, at all. I hope Dave will clarify on his own, >though, especially if I'm wrong. > > Um, well, in a way I guess that is what I'm saying, though I was coming from a different angle and going someplace difference. I was really just casually commenting that, in general, a policy of grabbing all passing exceptions and smashing them indiscriminately into the ground didn't seem like a particular healthy strategy. If the standard doesn't say that "all exceptions can be silently finalized without any special action", then I think any program that tries to do this with arbitrary exceptions is asking for trouble. It's not reliable, it's not portable, it's arguably not "correct". You offer the counter-argument that the same can also be said for code that raises an exception that requires special action -- except, one must allow, where it can also be sure that it will control finalization. But the standard, apparently, neither supports nor refutes either view. Which is very likely as it should be, since the standard applies to all sorts of special circumstances where trying to describe this sort of business just gets confusing and doesn't really help anyway. >AFAICT the discussion is about whether it makes sense to support >programs which do catch(...) without rethrowing, and if so, how. >Right now we're discussing a morality issue: "is it inherently evil to >catch(...) without rethrowing?" > >My point is that one could just as well (and perhaps more justifiably) >claim that it's evil to throw any exception that can't be dealt with >via a catch(...) block that doesn't rethrow, where that block was >designed to handle, say, bad_alloc.** > > The real issue isn't so much catching without a re-throw, but catching and dropping arbitrary exceptions. If you don't claim to know how to finalize a bad_alloc error, what business do you have catching it? (And since catch(...) is anonymous, even if you could handle it you can't identify it.) I'm not convinced there's any rational excuse for finalizing bad_alloc unless you know how to free up some memory to "cure" the problem that caused it. (Nor would there be any particular benefit.) If you can't deal with it, you need to let it propagate to someone who can; or to terminate the process. In the case of cancel, it's possible (though not "inarguably correct") to define the exception's destructor to re-cancel. There's no good analogy for bad_alloc, though; you can't just free arbitrary objects and presume the application can continue. But, sure, this could be argued back and forth forever, and it's not strictly relevant to the "charter" of this group... >(**) this isn't really about the exception objects, but about the >conditions of program state they represent. > > Certainly true, at least to the extent that if the exception doesn't represent a "program state", it may not matter; and if it does, then at least in many cases the situation will be self correcting. (That is, when you drop bad_alloc on the floor, either the situation has been corrected -- whether deliberately or simply by unwinding some set of frames with local objects -- or else it'll reassert itself soon enough.) There seems to be some disagreement over where cancel fits in this spectrum. It is a sort of program state (actually, thread state), and can't simply be thrown away (in normal applications) without breaking program semantics. On the other hand, it's a "one shot" that won't be automagically reasserted. Jason's model resolves this by making it artificially self-reasserting, via the exception object's destructor. Alexander argues against this. I think I'd be happy with an automatic re-cancel on a catch(...), but ideally I'd also like someone to be able to catch(cancel) without needing to do something extra (even a 'cancel.finalize()') to prevent re-cancellation. Given widespread use of catch(...), though, I'm willing to accept that "re-cancel on destruct" might be the least objectionable compromise. -- /--------------------[ 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 ]---/ From David.Butenhof at hp.com Tue Jan 20 13:14:42 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Tue, 20 Jan 2004 08:14:42 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> Message-ID: <400D29C2.1040107@hp.com> David Abrahams wrote: >Gabriel Dos Reis writes: > > >>Such clear statement, from portability point of view, has different >>impact than an unspoken assumption. >> >> >I think I heard what you were trying to say. Maybe I'm just blind to >the answer, but while I think it's possible (though usually >unconstructive) to formally label a particular programming construct >evil, I don't think it's possible to formally bless a programming >construct. Whether "catch-all without rethrow", or "while(1) loop >without break", or "delete this", etc are going to be OK is dependent >on program context, even if it is OK "in principle" in the POSIX >environment. What can a formal specification say to bless a >programming construct that could, depending on how it's used, lead to >undefined or otherwise bad behavior? > > We dealt with this sort of philosophical issue a lot in the POSIX working group. We had two vocal and often diametrically opposed factions (which, in retrospect, was probably good; though it did make for some quite "interesting" meetings): the "academic" faction who wanted to design a clean interface that would make it not only "obvious" how to write a "correct" program but also difficult to write an "incorrect" program; and the realtime ("hard" realtime, as in NASA & Navy) types who frequently HAD to do those things, knew how to, and wanted clean and portable mechanisms. Either extreme is philosophically defensible, perhaps; but neither is practical. Real code needs to do things sometimes that aren't 100% safe, and solutions will be developed. You're not really helping any by forcing those solutions to be non-standard and non-portable. On the other hand, some "unusually" unsafe things may really be too dangerous for a general purpose interface. There's no hard and fast rule that can help in drawing that line; it's a case by case judgement call by the committee and balloters. (With the inevitable result that people complain "why didn't you do this, when you did THAT, which is just as bad or even worse"... well, because the committee that day saw it differently, for whatever reason.) This appears to be the same sort of situation. I'm not even going to pretend to have an answer; and in fact I'd argue that nobody does (or can) have "the" answer... if there even is one. ;-) Getting back into context, we're adding cancellation. It has special semantics. That conflicts with wide and sanctioned usage of catch(...) without rethrow; and somehow we need to deal with this. We can say that all such code is now officially busted even though it was in standard libraries, and perhaps even recommended. We can somehow adjust the model so that it "works" anyway, as by Jason's "re-cancel on exception destruct"; recognizing that it becomes less convenient to catch when you really want to, and that we'll have extra unwinding (and loss of debugging information) when it happens. We could require the language to perform some call stack analysis as part of deciding whether to deliver a cancel in the first place -- which goes along with my earlier "random speculation" that the C++ concept of 'cancel enabled' might be a dynamic property of the call stack rather than explicit API. I was thinking in terms of disabling cancel within a 'throw()' scope... but there might be other conditions. For example, we could disable inside either 'throw()' or any try with a catch(...) that doesn't rethrow... unless there's an INNER scope that allows throwing cancel AND with an explicit catch(cancel). So a nothrow destructor (regardless of whether all destructors were implicitly nothrow or not) could allow "local" cancellation by nesting a try{} catch(cancel) {}. (Sounds too complicated; but it's something to think about. ;-) ) -- /--------------------[ 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 ]---/ From boo at terekhov.de Tue Jan 20 13:42:55 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Tue, 20 Jan 2004 14:42:55 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> Message-ID: <400D305F.7CAFE446@terekhov.de> Dave Butenhof wrote: [...] > than explicit API. I was thinking in terms of disabling cancel within a > 'throw()' scope... but there might be other conditions. For example, we > could disable inside either 'throw()' or any try with a catch(...) that > doesn't rethrow... unless there's an INNER scope that allows throwing > cancel AND with an explicit catch(cancel). So a nothrow destructor > (regardless of whether all destructors were implicitly nothrow or not) > could allow "local" cancellation by nesting a try{} catch(cancel) {}. This will sort of "break" catch(...), I'm afraid. > > (Sounds too complicated; but it's something to think about. ;-) ) The C++ incarnation of pthread_testcancel() will surely have a throw spec "throw(std::thread_cancel_request)" or something like that. Now imagine something along the lines of void oper(/*...*/) throw(std::thread_cancel_request, std::bad_alloc); /*...*/ oper_throw_handlers(/*...*/) throw(); void f1() throw() { /*...*/ try { oper(/*... "X" ...*/); } catch(...) { /*...*/oper_throw_handlers(/*...*/); } /*...*/ } void f2() throw() { /*...*/ try { oper(/*... "Y" ...*/); } catch(...) { /*...*/oper_throw_handlers(/*...*/); } /*...*/ } void f3() throw() { /*...*/ try { oper(/*... "Z" ...*/); } catch(...) { /*...*/oper_throw_handlers(/*...*/); } /*...*/ } /*...*/ oper_throw_handlers() throw() { try { throw; } catch(std::bad_alloc const &) { /*...*/ } catch(std::thread_cancel_request const &) { /*...*/ } /*...*/ } regards, alexander. P.S. http://google.com/groups?selm=4007FC34.D5A1059%40web.de http://google.com/groups?selm=40095EC5.9FDB3513%40web.de http://google.com/groups?selm=400960FF.558DC7DB%40web.de (Subject: Re: is catch(...) safe) From David.Butenhof at hp.com Tue Jan 20 14:11:34 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Tue, 20 Jan 2004 09:11:34 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <400D305F.7CAFE446@terekhov.de> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> <400D305F.7CAFE446@terekhov.de> Message-ID: <400D3716.4020204@hp.com> Alexander Terekhov wrote: >Dave Butenhof wrote: >[...] > > >>than explicit API. I was thinking in terms of disabling cancel within a >>'throw()' scope... but there might be other conditions. For example, we >>could disable inside either 'throw()' or any try with a catch(...) that >>doesn't rethrow... unless there's an INNER scope that allows throwing >>cancel AND with an explicit catch(cancel). So a nothrow destructor >>(regardless of whether all destructors were implicitly nothrow or not) >>could allow "local" cancellation by nesting a try{} catch(cancel) {}. >> >> >This will sort of "break" catch(...), I'm afraid. > > How's that? And why are you afraid? ;-) >>(Sounds too complicated; but it's something to think about. ;-) ) >> >> >The C++ incarnation of pthread_testcancel() will surely have a throw >spec "throw(std::thread_cancel_request)" or something like that. Now >imagine something along the lines of > > I take it you're arguing in favor of my parenthetical comment that such an analysis scheme was "too complicated"? Yes, it'd be easy to leave holes, which is indeed why it's complciated. If that's not what you meant, please explain. You love to dump "data balls" on everyone, Alexander, but there's always a lot of room for alternate interpretations of your point when you refuse to actually make it. (And in fact many opinions regarding whether there is in fact any point, not to mention whether it's worth the effort to try to guess at one.) -- /--------------------[ 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 ]---/ From boo at terekhov.de Tue Jan 20 15:19:19 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Tue, 20 Jan 2004 16:19:19 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> <400D305F.7CAFE446@terekhov.de> <400D3716.4020204@hp.com> Message-ID: <400D46F7.922A5BBC@terekhov.de> Dave Butenhof wrote: [...] > >>than explicit API. I was thinking in terms of disabling cancel within a > >>'throw()' scope... but there might be other conditions. For example, we > >>could disable inside either 'throw()' or any try with a catch(...) that > >>doesn't rethrow... unless there's an INNER scope that allows throwing > >>cancel AND with an explicit catch(cancel). So a nothrow destructor > >>(regardless of whether all destructors were implicitly nothrow or not) > >>could allow "local" cancellation by nesting a try{} catch(cancel) {}. > >> > >> > >This will sort of "break" catch(...), I'm afraid. > > > > > How's that? If a function that I wrap with a try-block has an exception specification then I know that catch(...) can only catch exceptions specified in that throw spec (silly implicit catch(...) aside for a moment). #include void f() throw() { try { std::pthread_testcancel(); // or think of fclose() like stuff } catch(...) { // only thread cancel request can be caught here /* nothrow */ } } shall be equivalent to #include void f() throw() { try { std::pthread_testcancel(); // or think of fclose() like stuff } catch(std::thread_cancel_request const &) { /* nothrow */ } } in my view. Note that C++ library implementations are not allowed to lessen exception-specifications defined in the C++ standard (incidentally, this is the source of conflict with respect to POSIX cancellation points), they can only strengthen them, if they want. > And why are you afraid? ;-) To me, there's nothing wrong with catch(...) as long as it's used "properly" (see "is catch(...) safe" thread at c.l.c++.mod). I don't want any special cases or treatment for thread_cancel_request and/or thread_exit_request/value. People should simply fix the code because it's just impossible to define a model that would add thread cancel and a) not break this or that existing cancel unaware code, plus b) not make writing new cancel aware code sort of "painful" or "inconvenient". regards, alexander. P.S. http://google.com/groups?selm=400BBC6A.4078B8C3%40web.de From dave at boost-consulting.com Wed Jan 21 02:49:50 2004 From: dave at boost-consulting.com (David Abrahams) Date: Tue, 20 Jan 2004 21:49:50 -0500 Subject: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> <400D2104.5080605@hp.com> Message-ID: Dave Butenhof writes: >>My point is that one could just as well (and perhaps more justifiably) >>claim that it's evil to throw any exception that can't be dealt with >>via a catch(...) block that doesn't rethrow, where that block was >>designed to handle, say, bad_alloc.** >> >> > The real issue isn't so much catching without a re-throw, but catching > and dropping arbitrary exceptions. If you don't claim to know how to > finalize a bad_alloc error, what business do you have catching it? > (And since catch(...) is anonymous, even if you could handle it you > can't identify it.) I'm not convinced there's any rational excuse for > finalizing bad_alloc unless you know how to free up some memory to > "cure" the problem that caused it. (Nor would there be any particular > benefit.) If you can't deal with it, you need to let it propagate to > someone who can; or to terminate the process. Let me give you one example. I used to write desktop application software. This software had an undo facility. The undo mechanism would store a copy of the portion of the document's original data that was about to be changed before any change was made. This arrangement also allowed any failing operation to be rolled back. Sometimes the user would try to do something that would consume more than the available memory, and sometimes the user would try to do something that ate up the last bit of disk space, and sometimes an operation would fail for other reasons. I could always bring the user back to the main event loop and fully recover her document's data so she could try to save it. The only reason the type of the exception mattered was that it allowed me to give the user a good error report. If there was an exception I couldn't recognize, it would always be better to say "something I can't identify went wrong; you might want to save under a different name" than to allow the program to terminate suddenly, and *definitely* drop the document's data on the floor. Of course, I never saw an exception I couldn't identify, but termination is not always the right answer. Another real-life example someone once gave me was that of a stage lighting controller. It was always better to plow ahead and hope the issue wasn't serious than to have the stage go suddenly black. There are surely places where finalizing a catch(...) is wrong, but there's no blanket rule that applies to all applications. -- Dave Abrahams Boost Consulting www.boost-consulting.com From dave at boost-consulting.com Wed Jan 21 02:56:54 2004 From: dave at boost-consulting.com (David Abrahams) Date: Tue, 20 Jan 2004 21:56:54 -0500 Subject: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> <400D29C2.1040107@hp.com> Message-ID: Dave Butenhof writes: > We could require the language to perform some call stack analysis as > part of deciding whether to deliver a cancel in the first place -- I think this falls into the same category as 2-phase EH: a nice thing for implementations to provide, but not something we can mandate in a standard without significantly raising the barrier-to-entry for a conforming C++ implementation. Remember that the simplest EH mechanisms are based, essentially, around a very naive setjmp/longjmp-like mechanism. Granted, they suck, but traditionally we on the C++ committee have shied away from legislating quality (no wisecracks from the peanut gallery, please!) -- Dave Abrahams Boost Consulting www.boost-consulting.com From boo at terekhov.de Wed Jan 21 09:42:51 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Wed, 21 Jan 2004 10:42:51 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> <400D29C2.1040107@hp.com> Message-ID: <400E499B.2FC74975@terekhov.de> David Abrahams wrote: [...] > I think this falls into the same category as 2-phase EH: a nice thing > for implementations to provide, but not something we can mandate in a > standard without significantly raising the barrier-to-entry for a Any addition raises the barrier-to-entry. If you don't have any technical objections, you should really stick to "degree of change you're requiring in currently sanctioned and common C++ code might reasonably be judged to be beyond the bounds of proper evolutionary change for an existing language with a moderately long history of fairly wide use" (source: DRB) line of reasoning in opposition to 2-phase EH. Unconvincing and highly subjective, if you ask me. > conforming C++ implementation. Remember that the simplest EH > mechanisms are based, essentially, around a very naive > setjmp/longjmp-like mechanism. 2-phase EH can be based around "not a very naive" setjmp/longjmp- like mechanism, I think. regards, alexander. P.S. Isn't "export" a much more severe barrier-to-entry? ;-) From David.Butenhof at hp.com Wed Jan 21 12:26:36 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Wed, 21 Jan 2004 07:26:36 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> <400D2104.5080605@hp.com> Message-ID: <400E6FFC.3040102@hp.com> David Abrahams wrote: >Dave Butenhof writes: > > >>The real issue isn't so much catching without a re-throw, but catching >>and dropping arbitrary exceptions. If you don't claim to know how to >>finalize a bad_alloc error, what business do you have catching it? >>(And since catch(...) is anonymous, even if you could handle it you >>can't identify it.) I'm not convinced there's any rational excuse for >>finalizing bad_alloc unless you know how to free up some memory to >>"cure" the problem that caused it. (Nor would there be any particular >>benefit.) If you can't deal with it, you need to let it propagate to >>someone who can; or to terminate the process. >> >> >[...] If there >was an exception I couldn't recognize, it would always be better to >say "something I can't identify went wrong; you might want to save >under a different name" than to allow the program to terminate >suddenly, and *definitely* drop the document's data on the floor. Of >course, I never saw an exception I couldn't identify, but termination >is not always the right answer. > >Another real-life example someone once gave me was that of a stage >lighting controller. It was always better to plow ahead and hope the >issue wasn't serious than to have the stage go suddenly black. > >There are surely places where finalizing a catch(...) is wrong, but >there's no blanket rule that applies to all applications. > > Sometimes I'm guilty of writing far too much detail, and I've learned to try to keep it simple and direct, at least "most of the time", and then I always get hit with a complaint that I didn't cover all the bases. You really can't win, can you? ;-) Yes, of course there are exceptions; mostly in outer level control structures, though, not in the inner depths of a library. I've always argued it should be POSSIBLE to finalize any exception, including cancel; and in fact that's one of the major reasons I've always felt that cancel should BE an exception, since the syntax is already there. The issue we've been discussing is not whether there are exceptions, or where they are, but rather how to "warp" our basic theoretically clean model to deal with the reality that C++ libraries (including STL) are rife with arbitrary catch(...) blocks that stop exceptions; often with no really supportable reason except that it seemed convenient at the time. Most of this code should NOT be continuing when an exception occurs they can't directly finalize; they should be passing it on to someone who can. That might be some outer scope that really understands the exception, or it might be some "failsafe catchall" in the main program loop like you're describing. We can keep this inner code from seeing cancel, we can keep it from catching it, we can re-raise cancel after the catch, or we can just let it trash the cancel and force the code to be fixed if the user really wants that thread to be cancellable while in the library. Note that my (hypothetical, simply for the purposes of abstract consideration) proposal for dynamic cancel scoping based (at least partly) on exception constructs, would result in cancel being completely disabled in any thread run by your "recover at all costs" main loop; because the entire call tree would be inside a catch(...) scope. The question is whether that's bad. The thread presumably was never cancellable before, unless it was designed for a POSIX implementation where C++ and POSIX cancel interoperate reasonably well (which doesn't leave many implementations). So it wouldn't be cancellable in the new "c++-pthreads" model, either, without some code changes. That doesn't sound like a big deal. If you WANT it to be cancellable, you could enable it by simply adding an explicit catch for the cancel exception to that try block. That's trivial compared to the work required just to check whether the rest of the threads' call stacks are cancel-safe. -- /--------------------[ 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 ]---/ From David.Butenhof at hp.com Wed Jan 21 12:37:17 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Wed, 21 Jan 2004 07:37:17 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> <400D29C2.1040107@hp.com> Message-ID: <400E727D.5030306@hp.com> David Abrahams wrote: >Dave Butenhof writes: > >>We could require the language to perform some call stack analysis as >>part of deciding whether to deliver a cancel in the first place -- >> >> >I think this falls into the same category as 2-phase EH: a nice thing >for implementations to provide, but not something we can mandate in a >standard without significantly raising the barrier-to-entry for a >conforming C++ implementation. Remember that the simplest EH >mechanisms are based, essentially, around a very naive >setjmp/longjmp-like mechanism. Granted, they suck, but traditionally >we on the C++ committee have shied away from legislating quality (no >wisecracks from the peanut gallery, please!) > > Sure, and POSIX has always had the same philosophy. We've even done some things to avoid entirely precluding braindead trivial implementations (like pure user-mode emulation packages). But we didn't go out of our way to make things EASY for them, either. We don't allow the operations of any thread to prevent any other thread from making forward progress, for example. (E.g., a braindead user-mode library can't let any thread make a direct kernel blocking I/O call; fully implementing that on UNIX is actually impossible, because select() and poll() traditionally "lie" about file-based I/O operations... but even ignoring that complication it isn't easy.) So whether your argument is relevant depends a lot on where this feature sits. If it's something that "might be nice" but isn't really important for portable applications to have, then yeah, maybe it sits above the bar. I know you have that opinion of 2-phase EH, for example, and as much as I like it I don't strongly disagree. But that's not a foregone conclusion for THIS feature, or any other. And if it's decided to be important then the fact that it makes life hard for someone who wants to ship a braindead implementation should be irrelevant. At least in theory. And, in theory, there's no difference between theory and practice. ;-) -- /--------------------[ 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 ]---/ From dave at boost-consulting.com Wed Jan 21 13:04:18 2004 From: dave at boost-consulting.com (David Abrahams) Date: Wed, 21 Jan 2004 08:04:18 -0500 Subject: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> <400D2104.5080605@hp.com> <400E6FFC.3040102@hp.com> Message-ID: Dave Butenhof writes: > Sometimes I'm guilty of writing far too much detail, and I've learned > to try to keep it simple and direct, at least "most of the time", and > then I always get hit with a complaint that I didn't cover all the > bases. You really can't win, can you? ;-) I wasn't complaining. I really thought you were trying to make a blanket rule. Many of the things you have written seemed to indicate that you thought it was always a mistake to finalize a catch(...). > Yes, of course there are exceptions; mostly in outer level control > structures, though, not in the inner depths of a library. I've always > argued it should be POSSIBLE to finalize any exception, including > cancel; and in fact that's one of the major reasons I've always felt > that cancel should BE an exception, since the syntax is already there. Good; we agree. > The issue we've been discussing is not whether there are exceptions, > or where they are, but rather how to "warp" our basic theoretically > clean model to deal with the reality that C++ libraries (including > STL) are rife with arbitrary catch(...) blocks that stop exceptions; > often with no really supportable reason except that it seemed > convenient at the time. Excuse me, but that is just wrong. How many C++ libraries' EH code have you examined? That approach is hardly ever taken by C++ libraries I've seen, and if there are any such instances in a C++ standard library implementation they exist in the iostreams code which is not part of what people call the STL. Unfortunately, iostreams were written before EH was really available and IMO they're a knotty and old-fashioned design that's had some EH capability bolted on. They have a "throwing mode" where exceptions are never stopped, and a "non-throwing" mode where exceptions are turned into "state bits". Even that, though I don't neccessarily agree with it, was done for a reason. > Most of this code should NOT be continuing when an exception occurs > they can't directly finalize; they should be passing it on to > someone who can. And that's what libraries generally do. > That might be some outer scope that really understands the > exception, or it might be some "failsafe catchall" in the main > program loop like you're describing. We can keep this inner code > from seeing cancel, we can keep it from catching it, we can re-raise > cancel after the catch, or we can just let it trash the cancel and > force the code to be fixed if the user really wants that thread to > be cancellable while in the library. > > Note that my (hypothetical, simply for the purposes of abstract > consideration) proposal for dynamic cancel scoping based (at least > partly) on exception constructs, would result in cancel being > completely disabled in any thread run by your "recover at all costs" > main loop; because the entire call tree would be inside a catch(...) > scope. That catch(...) block would only be in the program's main thread, which probably shouldn't be cancelled anyway. -- Dave Abrahams Boost Consulting www.boost-consulting.com From dave at boost-consulting.com Wed Jan 21 13:55:40 2004 From: dave at boost-consulting.com (David Abrahams) Date: Wed, 21 Jan 2004 08:55:40 -0500 Subject: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> <400D29C2.1040107@hp.com> <400E727D.5030306@hp.com> Message-ID: Dave Butenhof writes: > So whether your argument is relevant depends a lot on where this > feature sits. If it's something that "might be nice" but isn't really > important for portable applications to have, then yeah, maybe it sits > above the bar. I know you have that opinion of 2-phase EH, for > example, and as much as I like it I don't strongly disagree. But > that's not a foregone conclusion for THIS feature, or any other. And > if it's decided to be important then the fact that it makes life hard > for someone who wants to ship a braindead implementation should be > irrelevant. > > At least in theory. And, in theory, there's no difference between > theory and practice. ;-) My point is that this feature requires approximately the same resources as 2-phase EH. If you're going to mandate one of them, you might as well mandate both because all the infrastructure will be there. If one of them is demanding to be mandated, then both are. -- Dave Abrahams Boost Consulting www.boost-consulting.com From boo at terekhov.de Wed Jan 21 14:07:10 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Wed, 21 Jan 2004 15:07:10 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> <400D305F.7CAFE446@terekhov.de> <400D3716.4020204@hp.com> <400D46F7.922A5BBC@terekhov.de> <400D4D73.9070103@hp.com> <400E5144.3B00E2DD@terekhov.de> <400E6BBA.80506@hp.com> Message-ID: <400E878E.9C8E7854@terekhov.de> Dave Butenhof wrote: > > Alexander Terekhov wrote: > > >Dave Butenhof wrote: > >[...] > > > > > >>OK, but while I can see several ways in which an implementation of this > >>might not do what you'd like, I still fail to see how you mean it'll > >>"break" catch(...). > >> > >> > >In the current C++, if an exception can be caught by name it can also > >be caught anonymously by catch(...). I want the same program behavior > >(in that "example") if I replace catch-by-name with catch(...). Of > >course, you'll say that catch(...) isn't really broken here and that > >it's the lack of catch-by-name that changed the behavior... that's > >why I wrote "break", not break. ;-) > > > > > No, you're missing the point. I'm not talking about exception handling > as such; I'm talking about dynamic scoping for cancellation, using C++ > exception scoping and throw clauses as (at least part of) the set of > syntactic markers. I was suggesting, hypothetically, for discussion > purposes, that cancellation might never occur within a catch(...) scope > by default (unless something else is done, such as nesting a > catch(cancel)). So it's not that catch(...) wouldn't catch a cancel > exception... it WOULD, if the catch(cancel) re-threw it. I don't like that. I agree that throw clauses shall affect thread cancelability, but I don't like the idea to "empower" catch(...) with the ability to disable cancelability in a similar way. To me, this isn't intuitive. I still think that such catch(...) is sort of "broken". > It's simply > that, by default, the exception would never occur in that scope. > > (And I also realize that I, accidentally, did a "REPLY" rather than a > "REPLY-ALL" yesterday, and this little side discussion is inadvertently > private. Oh well.) Fixed. > > -- > /--------------------[ 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 ]---/ regards, alexander. From David.Butenhof at hp.com Wed Jan 21 14:11:43 2004 From: David.Butenhof at hp.com (Dave Butenhof) Date: Wed, 21 Jan 2004 09:11:43 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> <400D29C2.1040107@hp.com> <400E727D.5030306@hp.com> Message-ID: <400E889F.9020105@hp.com> David Abrahams wrote: >Dave Butenhof writes: > >>So whether your argument is relevant depends a lot on where this >>feature sits. If it's something that "might be nice" but isn't really >>important for portable applications to have, then yeah, maybe it sits >>above the bar. I know you have that opinion of 2-phase EH, for >>example, and as much as I like it I don't strongly disagree. But >>that's not a foregone conclusion for THIS feature, or any other. And >>if it's decided to be important then the fact that it makes life hard >>for someone who wants to ship a braindead implementation should be >>irrelevant. >> >>At least in theory. And, in theory, there's no difference between >>theory and practice. ;-) >> >> >My point is that this feature requires approximately the same >resources as 2-phase EH. If you're going to mandate one of them, you >might as well mandate both because all the infrastructure will be >there. If one of them is demanding to be mandated, then both are. > > OK, fine. It sounded to ME like you were trying to say that neither CAN be mandated because it might inconvenience some simplistic implementations. As Alexander commented, if "raising the bar" is a no-no, then just about everything is off-limits. -- /--------------------[ 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 ]---/ From boo at terekhov.de Wed Jan 21 14:32:29 2004 From: boo at terekhov.de (Alexander Terekhov) Date: Wed, 21 Jan 2004 15:32:29 +0100 Subject: [c++-pthreads] Re: thread-safety definition References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> <400D2104.5080605@hp.com> <400E6FFC.3040102@hp.com> Message-ID: <400E8D7D.5F181187@terekhov.de> David Abrahams wrote: [...] > > The issue we've been discussing is not whether there are exceptions, > > or where they are, but rather how to "warp" our basic theoretically > > clean model to deal with the reality that C++ libraries (including > > STL) are rife with arbitrary catch(...) blocks that stop exceptions; > > often with no really supportable reason except that it seemed > > convenient at the time. > > Excuse me, but that is just wrong. Bah. http://www.boost.org/libs/smart_ptr/sp_techniques.html "One subtle point is that deleters are not allowed to throw exceptions, and the above example as written assumes that p_.reset() doesn't throw. If this is not the case, p_.reset() should be wrapped in a try {} catch(...) {} block that ignores exceptions. [...] Another variation is to move the free list logic to the construction point by using a delayed deleter: struct delayed_deleter { template void operator()(T * p) { try { shared_ptr pv(p); free_list.push_back(pv); } catch(...) { } } };" regards, alexander. From jason at redhat.com Thu Jan 22 03:14:12 2004 From: jason at redhat.com (Jason Merrill) Date: Wed, 21 Jan 2004 22:14:12 -0500 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <400D29C2.1040107@hp.com> (Dave Butenhof's message of "Tue, 20 Jan 2004 08:14:42 -0500") References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> <400D29C2.1040107@hp.com> Message-ID: On Tue, 20 Jan 2004 08:14:42 -0500, Dave Butenhof wrote: > We could require the language to perform some call stack analysis as part > of deciding whether to deliver a cancel in the first place -- which goes > along with my earlier "random speculation" that the C++ concept of 'cancel > enabled' might be a dynamic property of the call stack rather than explicit > API. I was thinking in terms of disabling cancel within a 'throw()' > scope... but there might be other conditions. For example, we could disable > inside either 'throw()' or any try with a catch(...) that doesn't > rethrow... unless there's an INNER scope that allows throwing cancel AND > with an explicit catch(cancel). So a nothrow destructor (regardless of > whether all destructors were implicitly nothrow or not) could allow "local" > cancellation by nesting a try{} catch(cancel) {}. > > (Sounds too complicated; but it's something to think about. ;-) ) I does sound too complicated. In particular, whether or not a catch(...) block rethrows is undecidable; aside from the halting problem, it could call another function which rethrows. However, some call stack analysis might be appropriate to determine whether or not we're executing a destructor. Jason From austern at apple.com Fri Jan 30 22:16:39 2004 From: austern at apple.com (Matt Austern) Date: Fri, 30 Jan 2004 14:16:39 -0800 Subject: [c++-pthreads] Re: thread-safety definition In-Reply-To: <400A29F8.7010404@bogo.xs4all.nl> References: <1073556051.28360.195.camel@felix.inria.fr> <3FFD7487.1000404@hp.com> <3FFD7B71.1010708@hp.com> <3FFE9C82.3060500@hp.com> <4002A413.4090908@hp.com> <4003D9DC.6060705@hp.com> <400A29F8.7010404@bogo.xs4all.nl> Message-ID: On Jan 17, 2004, at 10:38 PM, Wil Evers wrote: > Matt Austern wrote: > >> On Jan 16, 2004, at 7:26 PM, David Abrahams wrote: >>> AFAICT the discussion is about whether it makes sense to support >>> programs which do catch(...) without rethrowing, and if so, how. >>> Right now we're discussing a morality issue: "is it inherently evil >>> to >>> catch(...) without rethrowing?" >> And my position on the morality question would be that yes, it >> is inherently evil, but that sometimes programs have to do evil >> things. I can think of a couple of designs that rely on being >> able to catch exceptions and not rethrow them, and I'm sure you >> can too. (Mostly designs where the catch(...) is part of an >> adapter layer that translates between one kind of error reporting >> mechanism and another one. The (...) will get translated into >> something like "unknown error".) > > Do you think that designs that rely on catch(...) without rethrow to > comply with the 'destructors must not throw' principle are > unreasonable? No. I think designs like that are ugly, but sometimes an ugly solution is the only one that can work. --Matt