I use native or crosscompiler for arm-xscale-be gcc 4.1.1 and glibc 2.5. gcc configuration: Using built-in specs. Target: i686-pc-linux-gnu Configured with: /var/tmp/portage/gcc-4.1.1-r3/work/gcc-4.1.1/configure --prefix=/usr --bindir=/usr/i686-pc-linux-gnu/gcc-bin/4.1.1 --includedir=/usr/lib/gcc/i686-pc-linux-gnu/4.1.1/include --datadir=/usr/share/gcc-data/i686-pc-linux-gnu/4.1.1 --mandir=/usr/share/gcc-data/i686-pc-linux-gnu/4.1.1/man --infodir=/usr/share/gcc-data/i686-pc-linux-gnu/4.1.1/info --with-gxx-include-dir=/usr/lib/gcc/i686-pc-linux-gnu/4.1.1/include/g++-v4 --host=i686-pc-linux-gnu --build=i686-pc-linux-gnu --disable-altivec --enable-nls --without-included-gettext --with-system-zlib --disable-checking --disable-werror --enable-secureplt --disable-libunwind-exceptions --disable-multilib --disable-libmudflap --disable-libssp --disable-libgcj --enable-languages=c,c++,fortran --enable-shared --enable-threads=posix --enable-__cxa_atexit --enable-clocale=gnu Thread model: posix gcc version 4.1.1 (Gentoo 4.1.1-r3) or (the cross compiler) Using built-in specs. Target: armv5teb-softfloat-linux-gnueabi Configured with: ../gcc-4.1.1/configure --prefix=/tmp/clfs/cross-tools --host=i686-cross-linux-gnu --target=armv5teb-softfloat-linux-gnueabi --disable-multilib --with-sysroot=/tmp/clfs --disable-nls --enable-shared --enable-languages=c,c++ --enable-__cxa_atexit --enable-c99 --enable-long-long --enable-threads=posix Thread model: posix gcc version 4.1.1 glibc on the x86 is: GNU C Library stable release version 2.5, by Roland McGrath et al. Copyright (C) 2006 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Compiled by GNU CC version 4.1.1 (Gentoo 4.1.1-r3). Compiled on a Linux 2.6.17 system on 2007-02-09. Available extensions: C stubs add-on version 2.1.2 crypt add-on version 2.1 by Michael Glad and others Gentoo patchset 1.3.1 GNU Libidn by Simon Josefsson GNU libio by Per Bothner NIS(YP)/NIS+ NSS modules 0.19 by Thorsten Kukuk Native POSIX Threads Library by Ulrich Drepper et al Support for some architectures added on, not maintained in glibc core. BIND-8.2.3-T5B Thread-local storage support included. For bug reporting instructions, please see: <http://www.gnu.org/software/libc/bugs.html>. Problem: Pthread cleanup handlers do not get called on thread cancelation if the program is compiled using g++. The same code, if compiled using gcc or using g++ with the -fno-exceptions option, calls cleanup handlers OK. Consequences in C++ application if the cleanup handler is (for example) supposed to unlock a mutex are easy to guess. Reproducible: Always Steps to Reproduce: Compile the code bellow using g++ and using gcc. Run both executables and compare results. Actual Results: The executable compile using gcc prints out this: Starting child thread. Canceling child thread. Cleanup handler called. Child thread terminated. while the executable compiled using g++ prints just this: Starting child thread. Canceling child thread. Child thread terminated. Expected Results: The outputs should be the same regardless of compiler used. -------------- /* * Cleanup handler bug test program. * Build using g++ -o cleanuphandlertest -l pthread cleanuphandlertest.cpp * or using gcc -o cleanuphandlertest -l pthread cleanuphandlertest.c * * If the cleanup handler doesn't get called try the -fno-exceptions option. */ #include <stdio.h> #include <unistd.h> #include <pthread.h> // Assertion which must always be true. // If !(condition) then a fatal error will result. // #define ASSERT( condition, errCode ) ( ( void ) \ ( (condition) ? ( ( void ) 0 ) : \ Assert( __FILE__, __LINE__, errCode ) ) ) void Assert( const char *file, int line, int err ) { fprintf( stderr, "Error in file %s, line %d: error code %d\n", file, line, err ); } void ThreadCleanupHandler( void* arg ) { printf( "Cleanup handler called.\n" ); } void* ThreadBody( void* arg ) { pthread_cleanup_push( ThreadCleanupHandler, arg ); while ( 1 ) { pthread_testcancel(); sleep( 1 ); } pthread_cleanup_pop( 0 ); } int main( int argc, char **argv ) { int status; pthread_attr_t threadAttributes; pthread_t threadId; status = pthread_attr_init( &threadAttributes ); ASSERT( status == 0, status ); // Start the queue scanning thread printf( "Starting child thread.\n" ); status = pthread_create( &threadId, &threadAttributes, ThreadBody, NULL ); ASSERT( status == 0, status ); status = pthread_attr_destroy( &threadAttributes ); ASSERT( status == 0, status ); sleep( 3 ); printf( "Canceling child thread.\n" ); status = pthread_cancel( threadId ); ASSERT( status == 0, status ); // wait until the thread really terminates status = pthread_join( threadId, NULL ); ASSERT( status == 0, status ); printf( "Child thread terminated.\n" ); return 0; } -------------- After a bit more investigation ... If the compiler is stopped after the preprocessor (i.e. g++ -E -o preprocess.cpp cleanuphandlertest.cpp), the code that installs/deinstalls the cleanup hander looks like: void* ThreadBody( void* arg ) { do { __pthread_cleanup_class __clframe (ThreadCleanupHandler, arg); while ( 1 ) { pthread_testcancel(); sleep( 1 ); } __clframe.__setdoit (0); } while (0); } where the __pthread_cleanup_class is defined as: class __pthread_cleanup_class { void (*__cancel_routine) (void *); void *__cancel_arg; int __do_it; int __cancel_type; public: __pthread_cleanup_class (void (*__fct) (void *), void *__arg) : __cancel_routine (__fct), __cancel_arg (__arg), __do_it (1) { } ~__pthread_cleanup_class () { if (__do_it) __cancel_routine (__cancel_arg); } void __setdoit (int __newval) { __do_it = __newval; } void __defer () { pthread_setcanceltype (PTHREAD_CANCEL_DEFERRED, &__cancel_type); } void __restore () const { pthread_setcanceltype (__cancel_type, 0); } }; The actual execution of the cleanup handler when the thread is canceled should happen in the destructor of the __clframe instance. So it looks like if the cleanup handler setup/invocation is implemented using the __pthread_cleanup_class the destructor of an instance created as local variable doesn't get called on thread cancellation.
What is the status of this? We are seeing this also on opensuse 10.2 X86 with gcc 4.1.2 and glibc 2.5-25.
Created attachment 1993 [details] Tar file containing test program to produce the behavior with pthread_exit. In addition, the cleanup handlers are sometimes not called from pthread_exit as well. I tried to reproduce this with the simple test program previously provided but could not. So I added what was a standalone openmotif messagebox app into the program in order to reproduce it. I changed the main function in messagebox.cpp to main_display and called it from the ThreadBody function. Then pthread_exit is called from MessageBox::ButtonCallback which is called upon pressing the OK button. pthread_exit should call the cleanup functions that were put on the cleanup stack in ThreadBody. To produce this behavior build the three source files using: g++ -o cleanuptest2 -lpthread -lXm -lXt cleanuptest2.cpp messagebox.cpp. To produce the correct behavior just add -fno-exceptions as in the original test.
Works fine with glibc 2.12 and gcc 4.4.