This is the mail archive of the mailing list for the pthreas-win32 project.

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

RE: pthreads VCE: problem with destructor

<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 []
Sent: December 18, 2001 9:17 AM
To: Pthreads-Win32@Sources.Redhat.Com
Cc: Ross Johnson (E-mail)
Subject: pthreads VCE: problem with destructor


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,



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

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&);
    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);
    assert(0); //Never reached with pthread_exit
    return ret_value;

#endif /*__cplusplus */

int main(int, char **)
#ifndef __cplusplus
    printf("Test requires C++. Skipped.\n");
        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 @@
 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.

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]