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

Yeah, I have encountered similar problems. I presume you are using MS VC++
6.0 and that these disappearing destructors affect your release build but
not your debug build?

The cause is apparently a bug in the VC++ 6.0 optimiser. With default
optimisation in release mode, the compiler just convinces itself that it
needn't bother generating those pesky destructors in exception-handling Turn
off the release mode optimisation and you get the destructors back.

This solution is a bit of pain in the arse however, because the affected
PThreadCleanup class is fully exposed in pthread.h, so to make pthread
client code safe from this bug you are going have to forego optimisation for
__all__ of it.

I found that a preferable solution was to hide the implementation of the
cleanup class off in a source module to be built with the pthreads library.
Then you lose the inlining of the cleanup class, which is regretable, and
you need to build the library without optimisation, but at least you can
build you own release code with the usual optimisation.

Mike Kinghan, Systems Union Ltd  

-----Original Message-----
From: Gardian, Milan []
Sent: Tuesday, December 18, 2001 2:17 PM
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

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