This is the mail archive of the
pthreads-win32@sources.redhat.com
mailing list for the pthreas-win32 project.
Re: pthreads VCE: problem with destructor
- From: Ross Johnson <rpj at ise dot canberra dot edu dot au>
- To: "Bossom, John" <John dot Bossom at Cognos dot COM>
- Cc: "'Gardian, Milan'" <Milan dot Gardian at LEIBINGER dot com>, "Pthreads-Win32 at Sources dot Redhat dot Com" <pthreads-win32 at sources dot redhat dot com>
- Date: Wed, 19 Dec 2001 22:37:59 +1100
- Subject: Re: pthreads VCE: problem with destructor
- Organization: University of Canberra, DMT, xISE
- References: <430F887D415DD1118C2700805F31ECF106B58985@sota0005.cognos.com>
- Reply-to: rpj at ise dot canberra dot edu dot au
I sense a rising and ruthless desire to deal with the problem of
the exception-based versions of the library. It would certainly
be a lot easier if they weren't there, and there are some
hacks in pthread.h supporting them that really are nasty.
So ... what to do about them?
I will firstly put John's warning in the README file
and the README.NONPORTABLE file, and on the Web page.
Secondly, there is a standard C version of the library courtesy
of Thomas Pfaff's contributions. It uses setjmp/longjmp.
Does this need to be built differently to work with C++
applications (assuming they are written as John suggests they
should be)? I can try putting it through the VCE run of the
test suite as soon as I reinstall my corrupted Windows 98 machine.
Thirdly, if possible, consider phasing out all but the VC and GC
versions of the library (currently the only standard C versions).
That is, phase out the VCE, VSE, and GCE versions.
Does anyone wan't to jump up and shout - NO!!
Ross
"Bossom, John" wrote:
>
> <Yes - I'm not going to lurk over this one... Hi Ross - I'm still here)
>
> You are entering dark territory here. You are assuming that
> Operating System primitives actually cooperate with C++ primitives (i.e
> exception handling, implicit
> call of local destructors)
> This is not the case. At the time of implementation of pthread_win32
> (original version) pthread_exit on UNIX did NOT call the C++ destructors
> and perform graceful stack unwinding. pthread_exit basically did an end run
> exit and locally called all registered pthread cleanup methods.
>
> Recommended approach when using C++ in conjunction with pthreads is to
> NEVER use pthread_exit... you should have your code return to your
> thread mainline (use exceptions if you choose) and simply "return"
> from the thread mainline.
>
> If you (Ross) implement this marrying of C++ stack-unwinding with
> pthread_exit, I suggest you
> document that it is not typical behavior and you can expect that if you port
> your code back
> to various unix platforms that actually support pthreads natively, you
> will encounter exactly the same problem.
>
> -----Original Message-----
> From: Gardian, Milan [mailto:Milan.Gardian@LEIBINGER.com]
> Sent: December 18, 2001 9:17 AM
> To: Pthreads-Win32@Sources.Redhat.Com
> Cc: Ross Johnson (E-mail)
> Subject: pthreads VCE: problem with destructor
>
> Hi,
>
> I came across a problem with pthreads VCE: destructors for objects in local
> scope (automatic variables on stack) of the thread-function are not called
> when using 'pthread_exit' to exit the thread (or when using any function
> that eventually throws a C++ exception (because of VCE build) using
> ptw32_throw internal function).
>
> To illustrate this point I created a new test case, 'exit4.c'. It uses a C++
> guard object to increment a shared reference count in constructor and
> decrement it in destructor. This object is then created on stack in both the
> main thread (before creating the new thread) and in the thread-function
> (i.e. reference count is increased by 2).
>
> If we finish the thread-function by returning from it (falling off the end),
> destructor for both objects is called as expected (first the thread-function
> local object and then the main thread local object). This case is
> illustrated by commenting out the macro USE_PTHREAD_EXIT in exit4.c, line 11
> -> the test case passes.
>
> On the other hand if we finish the thread-function by using 'pthread_exit'
> instead of falling off the end, the destructor for the thread-function local
> object is NOT called (although the destructor for main thread local object
> is called). This case is illustrated by compiling with the macro
> USE_PTHREAD_EXIT in exit4.c, line 11 -> the test case fails.
>
> C++ resource idiom (acquire in constructor, release in destructor) is used
> throughout our threaded program. The described behaviour of pthreads is
> introducing possibility of a resource leak and potential deadlock. Does
> anybody have any ideas what is happening and how to resolve the problem?
>
> Thank you very much for your replies and help,
>
> Cheers,
>
> Milan
>
> PS0: My config -> SMP 2 x PIII/500, W2k SP1, VC++ 6 SP4, Platform SDK
> June/2001, pthreads-latest-2001_12_17 (snaphost of the CVS taken on
> 17-DEC-2001 by Ross)
>
> PS1: Note that the reference counting need not be protected by a mutex as it
> will never be accessed simultaneously by both threads due to synchronization
> using pthread_join.
>
> PS2: The exit4.c file
> ===================================
> /*
> * Test for pthread_exit().
> *
> * Depends on API functions: pthread_create().
> */
>
> #include "test.h"
>
> #ifdef __cplusplus
>
> #define USE_PTHREAD_EXIT
> static const int init_counter_value = 3;
> static void *ret_value = reinterpret_cast<void *>(1);
> static int counter = init_counter_value;
>
> class Guard
> {
> const char * const _str;
> int &_ref;
> Guard &operator=(const Guard&);
> Guard(const Guard&);
> public:
> Guard(const char * const str, int &ref) : _str(str), _ref(ref) {
> printf("Construct %s [%d->%d]\n", _str, _ref, _ref+++1);
> };
> ~Guard() {
> printf("~Destruct %s [%d->%d]\n", _str, _ref, _ref---1);
> };
> };
>
> void *
> func(void * arg)
> {
> Guard g("func", counter);
> #ifdef USE_PTHREAD_EXIT
> pthread_exit(arg);
> assert(0); //Never reached with pthread_exit
> #endif //USE_PTHREAD_EXIT
> return ret_value;
> }
>
> #endif /*__cplusplus */
>
> int main(int, char **)
> {
> #ifndef __cplusplus
> printf("Test requires C++. Skipped.\n");
> #else
> {
> void *ret = 0;
> Guard g("main", counter);
> pthread_t id;
> assert(0 == pthread_create(&id, 0, func, ret_value));
> assert(0 == pthread_join(id, &ret));
> assert(ret == ret_value);
> }
> assert(counter == init_counter_value);
> #endif /*__cplusplus */
> return 0;
> }
> ===================================
>
> PS3: Changes to the 'tests/Makefile' (diff file, use patch):
> ===================================
> --- ..\..\pthreads\tests\Makefile Fri Oct 26 12:12:48 2001
> +++ Makefile Tue Dec 18 14:16:00 2001
> @@ -41,7 +41,7 @@
> mutex1.pass mutex1n.pass mutex1e.pass mutex1r.pass mutex2.pass
> mutex3.pass \
> condvar1.pass condvar2.pass condvar2_1.pass \
> exit1.pass create1.pass equal1.pass \
> - exit2.pass exit3.pass \
> + exit2.pass exit3.pass exit4.pass \
> join0.pass join1.pass join2.pass \
> mutex4.pass mutex6.pass mutex6n.pass mutex6e.pass mutex6r.pass
> \
> count1.pass once1.pass tsd1.pass \
> @@ -186,6 +186,7 @@
> exit1.pass:
> exit2.pass: create1.pass
> exit3.pass: create1.pass
> +exit4.pass: create1.pass
> eyal1.pass: tsd1.pass
> inherit1.pass: join1.pass
> join0.pass: create1.pass
> ===================================
>
> This message may contain privileged and/or confidential information. If you
> have received this e-mail in error or are not the intended recipient, you
> may not use, copy, disseminate, or distribute it; do not open any
> attachments, delete it immediately from your system and notify the sender by
> e-mail promptly that you have done so. Thank You.