I use the latest version of gcc (4.1.1) and glibc (2.5 nptlonly). Pthread cleanup handlers do not get called on thread cancelation if the program is compiled using g++. The same code, if compiled using gcc, 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 */ #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; }
works fine for me: Starting child thread. Canceling child thread. Cleanup handler called. Child thread terminated. post `emerge info` and such
mkes@euczprgmkes1 ~ $ emerge --info Portage 2.1.1-r2 (default-linux/x86/2006.1, gcc-4.1.1, glibc-2.5-r0, 2.6.19-gentoo-r5 i686) ================================================================= System uname: 2.6.19-gentoo-r5 i686 Intel(R) Xeon(TM) CPU 2.40GHz Gentoo Base System version 1.12.6 Last Sync: Thu, 08 Feb 2007 01:47:01 +0000 app-admin/eselect-compiler: [Not Present] dev-java/java-config: 1.3.7, 2.0.31 dev-lang/python: 2.4.3-r4 dev-python/pycrypto: 2.0.1-r5 dev-util/ccache: [Not Present] dev-util/confcache: [Not Present] sys-apps/sandbox: 1.2.17 sys-devel/autoconf: 2.13, 2.61 sys-devel/automake: 1.4_p6, 1.5, 1.6.3, 1.7.9-r1, 1.8.5-r3, 1.9.6-r2, 1.10 sys-devel/binutils: 2.16.1-r3 sys-devel/gcc-config: 1.3.14 sys-devel/libtool: 1.5.22 virtual/os-headers: 2.6.17-r2 ACCEPT_KEYWORDS="x86" AUTOCLEAN="yes" CBUILD="i686-pc-linux-gnu" CFLAGS="-O2 -march=i686 -pipe" CHOST="i686-pc-linux-gnu" CONFIG_PROTECT="/etc /usr/kde/3.5/env /usr/kde/3.5/share/config /usr/kde/3.5/shutdown /usr/share/X11/xkb /usr/share/config" CONFIG_PROTECT_MASK="/etc/env.d /etc/env.d/java/ /etc/gconf /etc/java-config/vms/ /etc/revdep-rebuild /etc/terminfo" CXXFLAGS="-O2 -march=i686 -pipe" DISTDIR="/usr/portage/distfiles" FEATURES="autoconfig distlocks metadata-transfer sandbox sfperms strict" GENTOO_MIRRORS="http://ftp.ucsb.edu/pub/mirrors/linux/gentoo ftp://distro.ibiblio.org/pub/linux/distributions/gentoo/ http://distro.ibiblio.org/pub/linux/distributions/gentoo/ ftp://ftp.gtlib.gatech.edu/pub/gentoo http://www.gtlib.gatech.edu/pub/gentoo http://gentoo.chem.wisc.edu/gentoo/ ftp://gentoo.chem.wisc.edu/gentoo/ " PKGDIR="/usr/portage/packages" PORTAGE_RSYNC_OPTS="--recursive --links --safe-links --perms --times --compress --force --whole-file --delete --delete-after --stats --timeout=180 --exclude='/distfiles' --exclude='/local' --exclude='/packages'" PORTAGE_TMPDIR="/var/tmp" PORTDIR="/usr/portage" SYNC="rsync://rsync.gentoo.org/gentoo-portage" USE="x86 X alsa alsa_cards_ali5451 alsa_cards_als4000 alsa_cards_atiixp alsa_cards_atiixp-modem alsa_cards_bt87x alsa_cards_ca0106 alsa_cards_cmipci alsa_cards_emu10k1 alsa_cards_emu10k1x alsa_cards_ens1370 alsa_cards_ens1371 alsa_cards_es1938 alsa_cards_es1968 alsa_cards_fm801 alsa_cards_hda-intel alsa_cards_intel8x0 alsa_cards_intel8x0m alsa_cards_maestro3 alsa_cards_trident alsa_cards_usb-audio alsa_cards_via82xx alsa_cards_via82xx-modem alsa_cards_ymfpci alsa_pcm_plugins_adpcm alsa_pcm_plugins_alaw alsa_pcm_plugins_asym alsa_pcm_plugins_copy alsa_pcm_plugins_dmix alsa_pcm_plugins_dshare alsa_pcm_plugins_dsnoop alsa_pcm_plugins_empty alsa_pcm_plugins_extplug alsa_pcm_plugins_file alsa_pcm_plugins_hooks alsa_pcm_plugins_iec958 alsa_pcm_plugins_ioplug alsa_pcm_plugins_ladspa alsa_pcm_plugins_lfloat alsa_pcm_plugins_linear alsa_pcm_plugins_meter alsa_pcm_plugins_mulaw alsa_pcm_plugins_multi alsa_pcm_plugins_null alsa_pcm_plugins_plug alsa_pcm_plugins_rate alsa_pcm_plugins_route alsa_pcm_plugins_share alsa_pcm_plugins_shm alsa_pcm_plugins_softvol apache2 berkdb bitmap-fonts cdr cli cracklib crypt cups dlloader dri elibc_glibc fortran gpm gtk2 iconv input_devices_keyboard input_devices_mouse ipv6 isdnlog java jpeg kde kernel_linux lcd_devices_bayrad lcd_devices_cfontz lcd_devices_cfontz633 lcd_devices_glk lcd_devices_hd44780 lcd_devices_lb216 lcd_devices_lcdm001 lcd_devices_mtxorb lcd_devices_ncurses lcd_devices_text ldap libg++ maildir mailwrapper midi mmap ncurses nls nptl nptlonly nsplugin opengl pam pcre perl png ppds pppd python qt readline reflection samba session spl ssl svg tcpd tiff truetype-fonts type1-fonts udev unicode userland_GNU video_cards_radeon video_cards_vesa wmf xorg zlib" Unset: CTARGET, EMERGE_DEFAULT_OPTS, INSTALL_MASK, LANG, LC_ALL, LDFLAGS, LINGUAS, MAKEOPTS, PORTAGE_RSYNC_EXTRA_OPTS, PORTDIR_OVERLAY
I have put the output of emerge --info but I didn't notice I had to reopen the bug ... so I'm trying to reopen it now.
I made a bit more investigation. If the compiler is stopped after the preprocessor 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. On the other hand, if the same code is compiled using the no-exceptions option i.e. g++ -fno-exceptions -o cleanuphandlertest -l pthread cleanuphandlertest.cpp the cleanup handler is called OK. In this case the preprocessor substitution of pthread_cleanup_push and pthread_cleanup_pop looks like: void* ThreadBody( void* arg ) { do { __pthread_unwind_buf_t __cancel_buf; void (*__cancel_routine) (void *) = (ThreadCleanupHandler); void *__cancel_arg = (arg); int not_first_call = __sigsetjmp ((struct __jmp_buf_tag *) __cancel_buf.__cancel_jmp_buf, 0); if (__builtin_expect (not_first_call, 0)) { __cancel_routine (__cancel_arg); __pthread_unwind_next (&__cancel_buf); } __pthread_register_cancel (&__cancel_buf); do {; while ( 1 ) { sleep( 1 ); } } while (0); __pthread_unregister_cancel (&__cancel_buf); if (0) __cancel_routine (__cancel_arg); } while (0); } where __sigsetjmp, __pthread_unwind_next, __pthread_register_cancel, and __pthread_unregister_cancel are external functions. 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.
This works fine for me on both: gcc-4.1.2 / glibc-2.6.1-r0 gcc-4.2.2 / glibc-2.7-r0 Please reopen if you are still experiencing any problems.