public inbox for pthreads-win32@sourceware.org
 help / color / mirror / Atom feed
From: "Bossom, John" <John.Bossom@Cognos.COM>
To: 'Jason Nye' <jnye@nbnet.nb.ca>, Ross Johnson <rpj@ise.canberra.edu.au>
Cc: 'Pthreads-win32' <pthreads-win32@sourceware.cygnus.com>
Subject: RE: asynchronous cancellation
Date: Tue, 16 Nov 1999 14:40:00 -0000	[thread overview]
Message-ID: <450FB96A8F51D31186CD00805F95C7DC9C3514@SOTTEXCH62A> (raw)

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

             reply	other threads:[~1999-11-16 14:40 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
1999-11-16 14:40 Bossom, John [this message]
  -- strict thread matches above, loose matches on Subject: below --
1999-11-15  7:45 Bossom, John
1999-11-16  1:30 ` Ross Johnson
1999-11-16 13:11   ` Jason Nye
1999-11-12 18:08 Jason Nye
1999-11-16 19:17 ` Ross Johnson
1999-11-17  4:46   ` Jason Nye

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=450FB96A8F51D31186CD00805F95C7DC9C3514@SOTTEXCH62A \
    --to=john.bossom@cognos.com \
    --cc=jnye@nbnet.nb.ca \
    --cc=pthreads-win32@sourceware.cygnus.com \
    --cc=rpj@ise.canberra.edu.au \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).