Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
Bug 166631 - pthread cleanup handler not called if compiling using g++
Summary: pthread cleanup handler not called if compiling using g++
Status: RESOLVED WORKSFORME
Alias: None
Product: Gentoo Linux
Classification: Unclassified
Component: [OLD] Core system (show other bugs)
Hardware: x86 Linux
: High critical (vote)
Assignee: Gentoo Toolchain Maintainers
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2007-02-13 09:58 UTC by Miroslav Kes
Modified: 2007-11-29 01:41 UTC (History)
0 users

See Also:
Package list:
Runtime testing required: ---


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Miroslav Kes 2007-02-13 09:58:21 UTC
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;
}
Comment 1 SpanKY gentoo-dev 2007-02-13 11:25:30 UTC
works fine for me:

Starting child thread.
Canceling child thread.
Cleanup handler called.
Child thread terminated.

post `emerge info` and such
Comment 2 Miroslav Kes 2007-02-13 11:33:08 UTC
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
Comment 3 Miroslav Kes 2007-02-15 08:00:14 UTC
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.
Comment 4 Miroslav Kes 2007-03-02 09:16:25 UTC
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.
Comment 5 Mark Loeser (RETIRED) gentoo-dev 2007-11-29 01:41:32 UTC
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.