/* thr_debug.c - wrapper around the chosen thread wrapper, for debugging. */ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * * Copyright 2005-2006 The OpenLDAP Foundation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted only as authorized by the OpenLDAP * Public License. * * A copy of this license is available in file LICENSE in the * top-level directory of the distribution or, alternatively, at * . */ /* * This package provides three types of thread operation debugging: * * - Print error messages and abort() when thread operations fail: * Operations on threads, mutexes, condition variables, rdwr locks. * Some thread pool operations are also checked, but not those for * which failure can happen in normal slapd operation. * * - Wrap those types except threads and pools in structs that * contain a state variable or a pointer to dummy allocated memory, * and check that on all operations. The dummy memory variant lets * malloc debuggers see some incorrect use as memory leaks, access * to freed memory, etc. * * - Print a count of leaked thread resources after cleanup. * * Compile-time (./configure) setup: Macros defined in CPPFLAGS. * * LDAP_THREAD_DEBUG or LDAP_THREAD_DEBUG=2 * Enables debugging, but value & 2 turns off type wrapping. * * LDAP_UINTPTR_T=integer type to hold pointers, preferably unsigned. * Used by dummy memory option "scramble". Default = unsigned long. * * In addition, you may need to set up an implementation-specific way * to enable whatever error checking your thread library provides. * Currently only implemented for Posix threads (pthreads), where * you may need to define LDAP_INT_THREAD_MUTEXATTR. The default * is PTHREAD_MUTEX_ERRORCHECK, or PTHREAD_MUTEX_ERRORCHECK_NP for * Linux threads. See pthread_mutexattr_settype(3). * * Run-time configuration: Environment variable LDAP_THREAD_DEBUG. * * The variable may contain a comma- or space-separated option list. * Options: * off - Disable this package. * Error checking: * noabort - Do not abort() on errors. * noerror - Do not report errors. Implies noabort. * nocount - Do not report counts of unreleased resources. * State variable/dummy memory, unless type wrapping is disabled: * noalloc - Default. Use a state variable, not dummy memory. * dupinit - Implies noalloc. Check if resources that have * not been destroyed are reinitialized. Tools that * report uninitialized memory access should disable * such warnings about debug_already_initialized(). * alloc - Allocate dummy memory and store pointers as-is. * Malloc debuggers might not notice unreleased * resources in global variables as memory leaks. * scramble - Store bitwise complement of dummy memory pointer. * That never escapes memory leak detectors - * but detection while the program is running will * report active resources as leaks. Do not * use this if a garbage collector is in use:-) * adjptr - Point to end of dummy memory. * Purify reports these as "potential leaks" (PLK). * I have not checked other malloc debuggers. * Tracing: * tracethreads - Report create/join/exit/kill of threads. */ #include "portable.h" #if defined( LDAP_THREAD_DEBUG ) #include #include #include #include #include "ldap_pvt_thread.h" /* Get the thread interface */ #define LDAP_THREAD_IMPLEMENTATION #define LDAP_THREAD_DEBUG_IMPLEMENTATION #define LDAP_THREAD_RDWR_IMPLEMENTATION #define LDAP_THREAD_POOL_IMPLEMENTATION #include "ldap_thr_debug.h" /* Get the underlying implementation */ /* Options from environment variable $LDAP_THREAD_DEBUG */ enum { Count_no = 0, Count_yes, Count_reported, Count_reported_more }; static int nodebug, noabort, noerror, count = Count_yes, options_done; #ifdef LDAP_THREAD_DEBUG_WRAP enum { Wrap_noalloc, Wrap_alloc, Wrap_scramble, Wrap_adjptr }; static int dupinit, wraptype = Wrap_noalloc, wrap_offset, unwrap_offset; #endif static int tracethreads; static int threading_enabled; enum { Idx_unexited_thread, Idx_unjoined_thread, Idx_locked_mutex, Idx_mutex, Idx_cond, Idx_rdwr, Idx_tpool, Idx_max }; static int resource_counts[Idx_max]; static const char *const resource_names[] = { "unexited threads", "unjoined threads", "locked mutexes", "mutexes", "conds", "rdwrs", "thread pools" }; static ldap_int_thread_mutex_t resource_mutexes[Idx_max]; /* * Making ldap_pvt_thread_t a wrapper around ldap_int_thread_t would * slow down ldap_pvt_thread_self(), so keep a list of threads instead. */ typedef struct ldap_debug_thread_s { ldap_pvt_thread_t wrapped; ldap_debug_usage_info_t usage; int detached; int freeme, idx; } ldap_debug_thread_t; static ldap_debug_thread_t **thread_info; static unsigned int thread_info_size, thread_info_used; static ldap_int_thread_mutex_t thread_info_mutex; #define WARN(var, msg) (warn (__FILE__, __LINE__, (msg), #var, (var))) #define WARN_IF(rc, msg) {if (rc) warn (__FILE__, __LINE__, (msg), #rc, (rc));} #define ERROR(var, msg) \ {if (!noerror) error(__FILE__, __LINE__, (msg), #var, (var));} #define ERROR_IF(rc, msg) \ {if (!noerror && (rc)) error(__FILE__, __LINE__, (msg), #rc, (rc));} #if 0 static void warn( const char *file, int line, const char *msg, const char *var, int val ) { fprintf( stderr, "%s:%d: %s warning: %s is %d\n", file, line, msg, var, val ); } #endif static void error( const char *file, int line, const char *msg, const char *var, int val ) { fprintf( stderr, "%s:%d: %s error: %s is %d\n", file, line, msg, var, val ); if( !noabort ) abort(); } static void count_resource_leaks( void ) { int i, j; char errbuf[200], *delim = "Leaked"; if( count == Count_yes ) { count = Count_reported; #if 0 /* Could break if there are still threads after atexit */ for( i = j = 0; i < Idx_max; i++ ) j |= ldap_int_thread_mutex_destroy( &resource_mutexes[i] ); WARN_IF( j, "ldap_debug_thread_destroy:mutexes" ); #endif for( i = j = 0; i < Idx_max; i++ ) { if( resource_counts[i] ) { j += sprintf( errbuf + j, "%s %d %s", delim, resource_counts[i], resource_names[i] ); delim = ","; } } if( j ) fprintf( stderr, "%s:%d: %s.\n", __FILE__, __LINE__, errbuf ); } } static void get_options( void ) { static const struct option_info_s { const char *name; int *var, val; } option_info[] = { { "off", &nodebug, 1 }, { "noabort", &noabort, 1 }, { "noerror", &noerror, 1 }, { "nocount", &count, Count_no }, #ifdef LDAP_THREAD_DEBUG_WRAP { "noalloc", &wraptype, Wrap_noalloc }, { "dupinit", &dupinit, 1 }, { "alloc", &wraptype, Wrap_alloc }, { "adjptr", &wraptype, Wrap_adjptr }, { "scramble", &wraptype, Wrap_scramble }, #endif { "tracethreads", &tracethreads, 1 }, { NULL, NULL, 0 } }; const char *s = getenv( "LDAP_THREAD_DEBUG" ); if( s != NULL ) { while( *(s += strspn( s, ", \t\r\n" )) != '\0' ) { size_t optlen = strcspn( s, ", \t\r\n" ); const struct option_info_s *oi; for( oi = option_info; oi->name; oi++ ) { if( strncasecmp( oi->name, s, optlen ) == 0 ) { if( oi->name && oi->name[optlen] == '\0' ) { *oi->var = oi->val; } else { fprintf( stderr, "Unknown $%s option '%.*s'\n", "LDAP_THREAD_DEBUG", (int) optlen, s ); } break; } } s += optlen; } } if( nodebug ) { noabort = noerror = 1; tracethreads = dupinit = 0; count = Count_no; } #ifdef LDAP_THREAD_DEBUG_WRAP if( nodebug || dupinit ) { wraptype = Wrap_noalloc; } else if( wraptype == Wrap_scramble ) { const unsigned char *dummy = (const unsigned char *)&option_info; if( sizeof(LDAP_UINTPTR_T) < sizeof(void *) || (unsigned char *)~~(LDAP_UINTPTR_T) dummy != dummy || (unsigned char *)~~(LDAP_UINTPTR_T) (unsigned char *)0 ) { fprintf( stderr, "Misconfigured for $%s %s. Using %s.\n", "LDAP_THREAD_DEBUG", "scramble", "adjptr" ); wraptype = Wrap_adjptr; } } unwrap_offset = -(wrap_offset = (wraptype == Wrap_adjptr)); #endif options_done = 1; } static char * thread_name( char *buf, int bufsize, ldap_pvt_thread_t thread ) { int i; --bufsize; if( bufsize > 2*sizeof(thread) ) bufsize = 2*sizeof(thread); for( i = 0; i < bufsize; i += 2 ) snprintf( buf+i, 3, "%02x", ((unsigned char *)&thread)[i/2] ); return buf; } static void exit_thread_message( const ldap_pvt_thread_t thread ) { if( tracethreads ) { char buf[40]; fprintf( stderr, "== Exiting thread %s ==\n", thread_name( buf, sizeof(buf), thread ) ); } } #ifndef LDAP_THREAD_DEBUG_WRAP #define WRAPPED(ptr) (ptr) #define SET_OWNER(ptr) ((void) 0) #define RESET_OWNER(ptr) ((void) 0) #define ASSERT_OWNER(ptr, msg) ((void) 0) #define ASSERT_NO_OWNER(ptr, msg) ((void) 0) #define alloc_usage(ptr, msg) ((void) 0) #define check_usage(ptr, msg) ((void) 0) #define free_usage(ptr, msg) ((void) 0) #define with_threads_lock(statement) { statement; } #define get_new_thread_info(msg) NULL #define update_thread_info(ti, th, det) {} #define remove_thread_info(ti, msg) ((void)0) #define get_thread_info(thread, msg) NULL #define exiting_thread(msg) exit_thread_message(ldap_pvt_thread_self()) #else /* LDAP_THREAD_DEBUG_WRAP */ /* Specialize this if initializer is not appropriate. */ /* The ASSERT_NO_OWNER() definition may also need an override. */ #ifndef LDAP_DEBUG_THREAD_NONE #define LDAP_DEBUG_THREAD_NONE { -1 } /* "no thread" ldap_int_thread_t value */ #endif static const ldap_int_thread_t ldap_debug_thread_none = LDAP_DEBUG_THREAD_NONE; #define THREAD_MUTEX_OWNER(mutex) \ ldap_pvt_thread_equal( (mutex)->owner, ldap_pvt_thread_self() ) void ldap_debug_thread_assert_mutex_owner( const char *file, int line, const char *msg, ldap_pvt_thread_mutex_t *mutex ) { if( !(noerror || THREAD_MUTEX_OWNER( mutex )) ) error( file, line, "ASSERT_MUTEX_OWNER", msg, 0 ); } #define WRAPPED(ptr) (&(ptr)->wrapped) #define SET_OWNER(ptr) ((ptr)->owner = ldap_pvt_thread_self()) #define RESET_OWNER(ptr) ((ptr)->owner = ldap_debug_thread_none) #define ASSERT_OWNER(ptr, msg) ERROR_IF( !THREAD_MUTEX_OWNER( ptr ), msg ) #ifndef ASSERT_NO_OWNER #define ASSERT_NO_OWNER(ptr, msg) ERROR_IF( \ !ldap_int_thread_equal( (ptr)->owner, ldap_debug_thread_none ), msg ) #endif #define INITED_VALUE 0x12345678UL #define INITED_BYTE_VALUE 0xd5 static int debug_already_initialized( const LDAP_UINTPTR_T *num ) { /* Valid programs will access uninitialized memory if dupinit */ return dupinit && *num == INITED_VALUE; } static void alloc_usage( ldap_debug_usage_info_t *usage, const char *msg ) { if( !options_done ) get_options(); if( wraptype == Wrap_noalloc ) { ERROR_IF( debug_already_initialized( &usage->num ), msg ); usage->num = INITED_VALUE; } else { unsigned char *dummy = malloc( 1 ); assert( dummy != NULL ); *dummy = INITED_BYTE_VALUE; if( wraptype == Wrap_scramble ) { usage->num = ~(LDAP_UINTPTR_T) dummy; assert( (unsigned char *)~usage->num == dummy ); } else { usage->ptr = dummy + wrap_offset; } } } static void check_usage( ldap_debug_usage_info_t *usage, const char *msg ) { if( wraptype == Wrap_noalloc ) { ERROR_IF( usage->num != INITED_VALUE, msg ); } else if( wraptype == Wrap_scramble ) { ERROR_IF( !usage->num, msg ); ERROR_IF( *(unsigned char *)~usage->num != INITED_BYTE_VALUE, msg ); } else { ERROR_IF( !usage->ptr, msg ); ERROR_IF( usage->ptr[unwrap_offset] != INITED_BYTE_VALUE, msg ); } } static void free_usage( ldap_debug_usage_info_t *usage, const char *msg ) { if( wraptype == Wrap_noalloc ) { usage->num = ~(LDAP_UINTPTR_T)INITED_VALUE; } else { unsigned char *dummy = (wraptype == Wrap_scramble ? (unsigned char *)~usage->num : usage->ptr + unwrap_offset); *(volatile unsigned char *)dummy = (unsigned char)-1; free( dummy ); } } #define with_threads_lock(statement) { \ if( !nodebug ) { \ int rc_wtl_ = ldap_int_thread_mutex_lock( &thread_info_mutex ); \ assert( rc_wtl_ == 0 ); \ } \ statement; \ if( !nodebug ) { \ int rc_wtl_ = ldap_int_thread_mutex_unlock( &thread_info_mutex ); \ assert( rc_wtl_ == 0 ); \ } \ } static ldap_debug_thread_t * get_new_thread_info( const char *msg ) { if( nodebug ) return NULL; if( thread_info_used >= thread_info_size ) { unsigned int more = thread_info_size + 1; /* debug value. increase. */ unsigned int new_size = thread_info_size + more; ldap_debug_thread_t *t = calloc( more, sizeof(ldap_debug_thread_t) ); assert( t != NULL ); t->freeme = 1; thread_info = realloc( thread_info, new_size * sizeof(*thread_info) ); assert( thread_info != NULL ); while( thread_info_size < new_size ) { t->idx = thread_info_size; thread_info[thread_info_size++] = t++; } } alloc_usage( &thread_info[thread_info_used]->usage, msg ); return thread_info[thread_info_used++]; } static void update_thread_info( ldap_debug_thread_t *t, const ldap_pvt_thread_t *thread, int detached ) { if( !nodebug ) { t->wrapped = *thread; t->detached = detached; } } static void remove_thread_info( ldap_debug_thread_t *t, const char *msg ) { if( !nodebug ) { ldap_debug_thread_t *last; int idx; free_usage( &t->usage, msg ); idx = t->idx; assert( thread_info[idx] == t ); last = thread_info[--thread_info_used]; assert( last->idx == thread_info_used ); (thread_info[idx] = last)->idx = idx; (thread_info[thread_info_used] = t )->idx = thread_info_used; } } ldap_debug_thread_t * get_thread_info( ldap_pvt_thread_t *thread, const char *msg ) { unsigned int i; ldap_debug_thread_t *t; if( nodebug ) return NULL; for( i = 0; i < thread_info_used; i++ ) { if( ldap_pvt_thread_equal( *thread, thread_info[i]->wrapped ) ) break; } ERROR_IF( i == thread_info_used, msg ); t = thread_info[i]; check_usage( &t->usage, msg ); return t; } static void exiting_thread( const char *msg ) { if( !nodebug ) { ldap_pvt_thread_t thread; thread = ldap_pvt_thread_self(); exit_thread_message( thread ); with_threads_lock({ ldap_debug_thread_t *t = get_thread_info( &thread, msg ); if( t->detached ) remove_thread_info( t, msg ); }); } } #endif /* LDAP_THREAD_DEBUG_WRAP */ static void adjust_count( int which, int adjust ) { int rc; switch( count ) { case Count_no: break; case Count_yes: rc = ldap_int_thread_mutex_lock( &resource_mutexes[which] ); assert( rc == 0 ); resource_counts[which] += adjust; rc = ldap_int_thread_mutex_unlock( &resource_mutexes[which] ); assert( rc == 0 ); case Count_reported: fputs( "...more ldap_debug_thread activity after exit...\n", stderr ); count = Count_reported_more; /* FALL THROUGH */ case Count_reported_more: /* Not used, but result might be inspected with debugger */ /* (Hopefully threading is disabled by now...) */ resource_counts[which] += adjust; break; } } /* Wrappers for LDAP_THREAD_IMPLEMENTATION: */ /* Used instead of ldap_int_thread_initialize by ldap_pvt_thread_initialize */ int ldap_debug_thread_initialize( void ) { int i, rc, rc2; if( !options_done ) get_options(); ERROR_IF( threading_enabled, "ldap_debug_thread_initialize" ); threading_enabled = 1; rc = ldap_int_thread_initialize(); if( rc ) { ERROR( rc, "ldap_debug_thread_initialize:threads" ); threading_enabled = 0; } else { rc2 = ldap_int_thread_mutex_init( &thread_info_mutex ); assert( rc2 == 0 ); if( count != Count_no ) { for( i = rc2 = 0; i < Idx_max; i++ ) rc2 |= ldap_int_thread_mutex_init( &resource_mutexes[i] ); assert( rc2 == 0 ); /* FIXME: Only for static libldap_r as in init.c? If so, why? */ atexit( count_resource_leaks ); } } return rc; } /* Used instead of ldap_int_thread_destroy by ldap_pvt_thread_destroy */ int ldap_debug_thread_destroy( void ) { int rc; ERROR_IF( !threading_enabled, "ldap_debug_thread_destroy" ); /* sleep(1) -- need to wait for thread pool to finish? */ rc = ldap_int_thread_destroy(); if( rc ) { ERROR( rc, "ldap_debug_thread_destroy:threads" ); } else { threading_enabled = 0; } return rc; } int ldap_pvt_thread_set_concurrency( int n ) { int rc; ERROR_IF( !threading_enabled, "ldap_pvt_thread_set_concurrency" ); rc = ldap_int_thread_set_concurrency( n ); ERROR_IF( rc, "ldap_pvt_thread_set_concurrency" ); return rc; } int ldap_pvt_thread_get_concurrency( void ) { int rc; ERROR_IF( !threading_enabled, "ldap_pvt_thread_get_concurrency" ); rc = ldap_int_thread_get_concurrency(); ERROR_IF( rc, "ldap_pvt_thread_get_concurrency" ); return rc; } unsigned int ldap_pvt_thread_sleep( unsigned int interval ) { int rc; ERROR_IF( !threading_enabled, "ldap_pvt_thread_sleep" ); rc = ldap_int_thread_sleep( interval ); ERROR_IF( rc, "ldap_pvt_thread_sleep" ); return 0; } int ldap_pvt_thread_create( ldap_pvt_thread_t *thread, int detach, void *(*start_routine)( void * ), void *arg ) { int rc; ERROR_IF( !threading_enabled, "ldap_pvt_thread_create" ); if( !options_done ) get_options(); with_threads_lock({ ldap_debug_thread_t *t; t = get_new_thread_info( "ldap_pvt_thread_create" ); rc = ldap_int_thread_create( thread, detach, start_routine, arg ); if( rc ) { ERROR( rc, "ldap_pvt_thread_create" ); remove_thread_info( t, "ldap_pvt_thread_init" ); } else { update_thread_info( t, thread, detach ); } }); if( rc == 0 ) { if( tracethreads ) { char buf[40]; fprintf( stderr, "== Created thread %s%s ==\n", thread_name( buf, sizeof(buf), *thread ), detach ? " (detached)" : "" ); } adjust_count( Idx_unexited_thread, +1 ); if( !detach ) adjust_count( Idx_unjoined_thread, +1 ); } return rc; } void ldap_pvt_thread_exit( void *retval ) { ERROR_IF( !threading_enabled, "ldap_pvt_thread_exit" ); adjust_count( Idx_unexited_thread, -1 ); exiting_thread( "ldap_pvt_thread_exit" ); ldap_int_thread_exit( retval ); } int ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return ) { int rc; ldap_debug_thread_t *t; ERROR_IF( !threading_enabled, "ldap_pvt_thread_join" ); if( tracethreads ) { char buf[40]; fprintf( stderr, "== Joining thread %s ==\n", thread_name( buf, sizeof(buf), thread ) ); } with_threads_lock( t = get_thread_info( &thread, "ldap_pvt_thread_join" ) ); rc = ldap_int_thread_join( thread, thread_return ); if( rc ) { ERROR( rc, "ldap_pvt_thread_join" ); } else { with_threads_lock( remove_thread_info( t, "ldap_pvt_thread_join" ) ); adjust_count( Idx_unjoined_thread, -1 ); } return rc; } int ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo ) { int rc; ERROR_IF( !threading_enabled, "ldap_pvt_thread_kill" ); if( tracethreads ) { char buf[40]; fprintf( stderr, "== Killing thread %s (sig %i) ==\n", thread_name( buf, sizeof(buf), thread ), signo ); } rc = ldap_int_thread_kill( thread, signo ); ERROR_IF( rc, "ldap_pvt_thread_kill" ); return rc; } int ldap_pvt_thread_yield( void ) { int rc; ERROR_IF( !threading_enabled, "ldap_pvt_thread_yield" ); rc = ldap_int_thread_yield(); ERROR_IF( rc, "ldap_pvt_thread_yield" ); return rc; } ldap_pvt_thread_t ldap_pvt_thread_self( void ) { #if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */ ERROR_IF( !threading_enabled, "ldap_pvt_thread_self" ); #endif return ldap_int_thread_self(); } int ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond ) { int rc; alloc_usage( &cond->usage, "ldap_pvt_thread_cond_init" ); rc = ldap_int_thread_cond_init( WRAPPED( cond ) ); if( rc ) { ERROR( rc, "ldap_pvt_thread_cond_init" ); free_usage( &cond->usage, "ldap_pvt_thread_cond_init" ); } else { adjust_count( Idx_cond, +1 ); } return rc; } int ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cond ) { int rc; check_usage( &cond->usage, "ldap_pvt_thread_cond_destroy" ); rc = ldap_int_thread_cond_destroy( WRAPPED( cond ) ); if( rc ) { ERROR( rc, "ldap_pvt_thread_cond_destroy" ); } else { free_usage( &cond->usage, "ldap_pvt_thread_cond_destroy" ); adjust_count( Idx_cond, -1 ); } return rc; } int ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond ) { int rc; check_usage( &cond->usage, "ldap_pvt_thread_cond_signal" ); rc = ldap_int_thread_cond_signal( WRAPPED( cond ) ); ERROR_IF( rc, "ldap_pvt_thread_cond_signal" ); return rc; } int ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond ) { int rc; check_usage( &cond->usage, "ldap_pvt_thread_cond_broadcast" ); rc = ldap_int_thread_cond_broadcast( WRAPPED( cond ) ); ERROR_IF( rc, "ldap_pvt_thread_cond_broadcast" ); return rc; } int ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond, ldap_pvt_thread_mutex_t *mutex ) { int rc; check_usage( &cond->usage, "ldap_pvt_thread_cond_wait:cond" ); check_usage( &mutex->usage, "ldap_pvt_thread_cond_wait:mutex" ); adjust_count( Idx_locked_mutex, -1 ); ASSERT_OWNER( mutex, "ldap_pvt_thread_cond_wait" ); RESET_OWNER( mutex ); /* Breaks if this thread did not own the mutex */ rc = ldap_int_thread_cond_wait( WRAPPED( cond ), WRAPPED( mutex ) ); ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_cond_wait" ); SET_OWNER( mutex ); adjust_count( Idx_locked_mutex, +1 ); ERROR_IF( rc, "ldap_pvt_thread_cond_wait" ); return rc; } int ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex ) { int rc; alloc_usage( &mutex->usage, "ldap_pvt_thread_mutex_init" ); rc = ldap_int_thread_mutex_init( WRAPPED( mutex ) ); if( rc ) { ERROR( rc, "ldap_pvt_thread_mutex_init" ); free_usage( &mutex->usage, "ldap_pvt_thread_mutex_init" ); } else { RESET_OWNER( mutex ); adjust_count( Idx_mutex, +1 ); } return rc; } int ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex ) { int rc; check_usage( &mutex->usage, "ldap_pvt_thread_mutex_destroy" ); ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_destroy" ); rc = ldap_int_thread_mutex_destroy( WRAPPED( mutex ) ); if( rc ) { ERROR( rc, "ldap_pvt_thread_mutex_destroy" ); /* mutex->owner may now be scrambled, sorry */ } else { free_usage( &mutex->usage, "ldap_pvt_thread_mutex_destroy" ); RESET_OWNER( mutex ); adjust_count( Idx_mutex, -1 ); } return rc; } int ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex ) { int rc; check_usage( &mutex->usage, "ldap_pvt_thread_mutex_lock" ); rc = ldap_int_thread_mutex_lock( WRAPPED( mutex ) ); if( rc ) { ERROR_IF( rc, "ldap_pvt_thread_mutex_lock" ); } else { ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_lock" ); SET_OWNER( mutex ); adjust_count( Idx_locked_mutex, +1 ); } return rc; } int ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex ) { int rc; check_usage( &mutex->usage, "ldap_pvt_thread_mutex_trylock" ); rc = ldap_int_thread_mutex_trylock( WRAPPED( mutex ) ); if( rc == 0 ) { ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_trylock" ); SET_OWNER( mutex ); adjust_count( Idx_locked_mutex, +1 ); } return rc; } int ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex ) { int rc; check_usage( &mutex->usage, "ldap_pvt_thread_mutex_unlock" ); ASSERT_OWNER( mutex, "ldap_pvt_thread_mutex_unlock" ); RESET_OWNER( mutex ); /* Breaks if this thread did not own the mutex */ rc = ldap_int_thread_mutex_unlock( WRAPPED( mutex ) ); if( rc ) { ERROR_IF( rc, "ldap_pvt_thread_mutex_unlock" ); } else { adjust_count( Idx_locked_mutex, -1 ); } return rc; } /* Wrappers for LDAP_THREAD_RDWR_IMPLEMENTATION: */ int ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rwlock ) { int rc; alloc_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_init" ); rc = ldap_int_thread_rdwr_init( WRAPPED( rwlock ) ); if( rc ) { ERROR( rc, "ldap_pvt_thread_rdwr_init" ); free_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_init" ); } else { adjust_count( Idx_rdwr, +1 ); } return rc; } int ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rwlock ) { int rc; check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_destroy" ); rc = ldap_int_thread_rdwr_destroy( WRAPPED( rwlock ) ); if( rc ) { ERROR( rc, "ldap_pvt_thread_rdwr_destroy" ); } else { free_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_destroy" ); adjust_count( Idx_rdwr, -1 ); } return rc; } int ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rwlock ) { int rc; check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_rlock" ); rc = ldap_int_thread_rdwr_rlock( WRAPPED( rwlock ) ); ERROR_IF( rc, "ldap_pvt_thread_rdwr_rlock" ); return rc; } int ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rwlock ) { check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_rtrylock" ); return ldap_int_thread_rdwr_rtrylock( WRAPPED( rwlock ) ); } int ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rwlock ) { int rc; check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_runlock" ); rc = ldap_int_thread_rdwr_runlock( WRAPPED( rwlock ) ); ERROR_IF( rc, "ldap_pvt_thread_rdwr_runlock" ); return rc; } int ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rwlock ) { int rc; check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wlock" ); rc = ldap_int_thread_rdwr_wlock( WRAPPED( rwlock ) ); ERROR_IF( rc, "ldap_pvt_thread_rdwr_wlock" ); return rc; } int ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rwlock ) { check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wtrylock" ); return ldap_int_thread_rdwr_wtrylock( WRAPPED( rwlock ) ); } int ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rwlock ) { int rc; check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wunlock" ); rc = ldap_int_thread_rdwr_wunlock( WRAPPED( rwlock ) ); ERROR_IF( rc, "ldap_pvt_thread_rdwr_wunlock" ); return rc; } #ifdef LDAP_RDWR_DEBUG int ldap_pvt_thread_rdwr_readers( ldap_pvt_thread_rdwr_t *rwlock ) { check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_readers" ); return ldap_int_thread_rdwr_readers( WRAPPED( rwlock ) ); } int ldap_pvt_thread_rdwr_writers( ldap_pvt_thread_rdwr_t *rwlock ) { check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_writers" ); return ldap_int_thread_rdwr_writers( WRAPPED( rwlock ) ); } int ldap_pvt_thread_rdwr_active( ldap_pvt_thread_rdwr_t *rwlock ) { check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_active" ); return ldap_int_thread_rdwr_active( WRAPPED( rwlock ) ); } #endif /* LDAP_RDWR_DEBUG */ /* Some wrappers for LDAP_THREAD_POOL_IMPLEMENTATION: */ #ifdef LDAP_THREAD_POOL_IMPLEMENTATION int ldap_pvt_thread_pool_init( ldap_pvt_thread_pool_t *tpool, int max_threads, int max_pending ) { int rc; ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_init" ); rc = ldap_int_thread_pool_init( tpool, max_threads, max_pending ); if( rc ) { ERROR( rc, "ldap_pvt_thread_pool_init" ); } else { adjust_count( Idx_tpool, +1 ); } return rc; } int ldap_pvt_thread_pool_submit( ldap_pvt_thread_pool_t *tpool, ldap_pvt_thread_start_t *start_routine, void *arg ) { int rc, has_pool; ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_submit" ); has_pool = (tpool != NULL && *tpool != NULL); rc = ldap_int_thread_pool_submit( tpool, start_routine, arg ); if( has_pool ) ERROR_IF( rc, "ldap_pvt_thread_pool_submit" ); return rc; } int ldap_pvt_thread_pool_maxthreads( ldap_pvt_thread_pool_t *tpool, int max_threads ) { ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_maxthreads" ); return ldap_int_thread_pool_maxthreads( tpool, max_threads ); } int ldap_pvt_thread_pool_backload( ldap_pvt_thread_pool_t *tpool ) { ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_backload" ); return ldap_int_thread_pool_backload( tpool ); } int ldap_pvt_thread_pool_destroy( ldap_pvt_thread_pool_t *tpool, int run_pending ) { int rc, has_pool; ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_destroy" ); has_pool = (tpool != NULL && *tpool != NULL); rc = ldap_int_thread_pool_destroy( tpool, run_pending ); if( has_pool ) { if( rc ) { ERROR( rc, "ldap_pvt_thread_pool_destroy" ); } else { adjust_count( Idx_tpool, -1 ); } } return rc; } int ldap_pvt_thread_pool_pause( ldap_pvt_thread_pool_t *tpool ) { ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_pause" ); return ldap_int_thread_pool_pause( tpool ); } int ldap_pvt_thread_pool_resume( ldap_pvt_thread_pool_t *tpool ) { ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_resume" ); return ldap_int_thread_pool_resume( tpool ); } int ldap_pvt_thread_pool_getkey( void *xctx, void *key, void **data, ldap_pvt_thread_pool_keyfree_t **kfree ) { #if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_getkey" ); #endif return ldap_int_thread_pool_getkey( xctx, key, data, kfree ); } int ldap_pvt_thread_pool_setkey( void *xctx, void *key, void *data, ldap_pvt_thread_pool_keyfree_t *kfree ) { int rc; ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_setkey" ); rc = ldap_int_thread_pool_setkey( xctx, key, data, kfree ); ERROR_IF( rc, "ldap_pvt_thread_pool_setkey" ); return rc; } void ldap_pvt_thread_pool_purgekey( void *key ) { ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_purgekey" ); ldap_int_thread_pool_purgekey( key ); } void * ldap_pvt_thread_pool_context( void ) { #if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_context" ); #endif return ldap_int_thread_pool_context(); } void ldap_pvt_thread_pool_context_reset( void *vctx ) { ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_context_reset" ); ldap_int_thread_pool_context_reset( vctx ); } #endif /* LDAP_THREAD_POOL_IMPLEMENTATION */ #endif /* LDAP_THREAD_DEBUG */