This is the mail archive of the glibc-bugs@sourceware.org mailing list for the glibc 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]

[Bug nptl/4123] New: pthread cleanup handler not called


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.

-- 
           Summary: pthread cleanup handler not called
           Product: glibc
           Version: unspecified
            Status: NEW
          Severity: critical
          Priority: P2
         Component: nptl
        AssignedTo: drepper at redhat dot com
        ReportedBy: mkes at ra dot rockwell dot com
                CC: glibc-bugs at sources dot redhat dot com
 GCC build triplet: i686-pc-linux-gnu
  GCC host triplet: i686-pc-linux-gnu
GCC target triplet: i686-pc-linux-gnu, arm-xscale-be


http://sourceware.org/bugzilla/show_bug.cgi?id=4123

------- You are receiving this mail because: -------
You are on the CC list for the bug, or are watching someone who is.


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