From mboxrd@z Thu Jan 1 00:00:00 1970 From: "Bossom, John" To: 'Jason Nye' , Ross Johnson Cc: 'Pthreads-win32' Subject: RE: asynchronous cancellation Date: Tue, 16 Nov 1999 14:40:00 -0000 Message-id: <450FB96A8F51D31186CD00805F95C7DC9C3514@SOTTEXCH62A> X-SW-Source: 1999/msg00134.html Hi Jason, No flames intended... (and there are none contained within this email either) I'm not trying to say "ours is better than yours" either. (Better grab a coffee... I got kind of long winded here!) The situation I am trying to describe has nothing to do with the pthreads-win32 implementation or your implementation, or any other implementation for that matter. It is a general comment I have that the C++ features - automatic C++ object destructors - C++ exception handling are in direct conflict with the "philosophy" of the PThreads Cancellation programming paradigm (specifically cleanup) regardless of platform/implementation. Summary: ======= As a C++ programmer, I wouldn't use the pthreads cancellation mechanism simply because it does prevent cleaning up resources held by automatic (stack) based C++ objects (or, conversely, it prevents me from using these C++ features). The cleanup code one would put in their C++ 'catch' would have to be duplicated in their pthread_cleanup_push routines... so a C++ programmer might as well choose one methodology or the other: a) pthread cancellation mechanism, with push/pop for cleanup: - never using automatic objects that retain resources, - never using/relying on the C++ catch code being executed for cancellation b) C++ features: - automatic C++ object destructors - C++ Exception Handling - your own method of cancelling a thread (flag/event/condition/etc. and unwind the stack by throwing an exception) Attempting to mix the two such that you have no resource leaks wouldn't be pretty. Details ------- There is no standard that specifies the behaviour between C++ and PThreads. As a result, the behaviour of a PThreads library and these C++ features is implementation specific and should not be relied upon. My point is, you cannot rely on the two mechanisms working together from machine to machine as there is no standard enforcing this. As you have stated, your implementation bypasses stack-unwinding... there is nothing wrong with this... as far as I know, the native PThreads implementations on all UNIX platforms also bypass C++ stack unwinding - this is their defined behaviour - PThreads knows nothing about C++. I am not touting the pthreads-win32 mechanism either... I personally wouldn't use the pthreads cancellation mechanism on any platform (including pthreads-win32) if I was writing code in C++ because it limits/prevents the use of these C++ features. Most PThreads books I have read state that you should avoid using the PThreads cancellation mechanism since it can result in complex code and bypasses all C++ stack unwinding resulting in lost resources (see my little C++ example below. These books typically recommend that C++ programmers implement their own cancellation mechanism (i.e. setting a variable and checking it periodically within the thread, or by some other means - then throw a C++ exception out to the thread's main routine, and exit the thread there by simply returning from the main routine.) This is the philosophy taken by Threads++ library by RogueWave(?). The programmer defines cancellation points within their code. The cancelation check simply returns whether you should carry on or cancel. It is up to the programmer to then shutdown the thread however they choose. This model works for both C and C++ without having to sacrifice language features. More Detail: i.e.) C++ guarantees that objects declared on the stack have their destructors invoked no matter how you leave the scope in which they are defined. There are two valid methods of leaving the scope, as far as C++ is concerned: 1) simply executing to the end of the scope in which the object is declared, or 2) jumping out of the scope through the use of the C++ exception handler. In both cases, the C++ compiler places sufficient implicit hooks into the generated code to ensure that your automatic destructors for these stack based objects are called. The programming paradigm for the PThreads cancellation mechanism is that only those destructors explicitly registered with pthread_cleanup_push are called; then the thread exists in place (i.e. doesn't return through the call stack)... this step bypasses the cleanup of all stack based C++ objects ... any resources being held by these objects are forever lost). Here is an example snippet of code (sorry if it isn't accurate C++, but you'll get the idea... class MyClass { public: MyClass() { acquireResource(); } ~MyClass() { releaseResource(); } }; someRoutine() { { MyClass classInstance; doSomeStuff(); // compiler generated implicit code to call // classInstance.~MyClass() here... } By stack unwinding I mean that, whether or not you use C++ exception handling, when you leave the scope in which an automatic (i.e. stack based variable) is created, the destructors are implicitly called (i.e. the C++ compiler generates an implicit call to classInstance::~MyClass() in the above example). Now, if someRoutine was called within a thread, and we are currently running in doSomeStuff() when the cancellation is recognised, the object classInstance would not have it's destructor called and any resources held by it will be leaked/lost... A C++ programmer wishing to use the pthread cancellation mechanism must take special care in order to get these features to work together... They must either only use deferred cancellation and only enable it in safe situations, or re-write the above to look something like someRoutine() { { MyClass * classInstance = New MyClass(); pthread_cleanup_push( classInstance, MyClass::~MyClass ); doSomeStuff(); pthread_cleanup_pop( classInstance, 1 ); } } I hope I have clarified myself and I do thank you for showing me how to implement an asynchronous cancel by basically telling the CPU to start executing code at a specific address... -----Original Message----- From: Jason Nye [ mailto:jnye@nbnet.nb.ca ] Sent: Tuesday, November 16, 1999 3:57 PM To: Ross Johnson Cc: 'Pthreads-win32' Subject: Re: asynchronous cancellation John Bossom wrote: > > Interesting solution... > > > > To work with the pthreads-win32 library, your AsyncCancelPoint > > needs to get pSelf from dynamically declared TLS... (i.e. pthread_self, > > or it's internal implementation). > > > > Just a note to C++ users: one shouldn't use the built-in pthreads > > cancellation if they expect C++ stack unwinding to work.... > > I'm a C++ developer, not necessarily a guru in the ways of pthreads -- I do know some its specifications, however -- please, no flames! Why does the stack have to be unwound when a thread is cancelled? Is this a requirement of pthreads (I'm pretty sure this is not the case) or is it just the way that pthreads-win32 has always implemented it and you are sticking to this design? My ObjectThread library does not unwind the stack when a thread is cancelled and the net result is that it has no weird implementation-specific side effects from using exception handling (whether SEH or C++) and there is no unnecessary performance overhead when a thread gets cancelled -- no unwinding the stack, just executing cleanup handlers/destructors and calling _exitthreadex(). IMHO using SEH and C++ exceptions ties pthreads-win32 to specific compilers/languages and introduces unnecessary implementation-specific side-effects that will catch some developers by surprise (it caught me by surprise -- and note John's last point). Cancellation can be easily implemented without using such constructs and the result is a compiler-independent library in which cancellation acts as one would expect. Note that I'm not trying to say "Mine is better than yours", I'm just saying that there is an another way to achieve cancellation that may be more reliable and more compliant to the standard. Cheerio, Jason