From: Hallvard Furuseth Date: Sat, 17 Sep 2005 23:28:08 +0000 (+0000) Subject: Add thread debugging wrapper thr_debug.c and ldap_thr_debug.h in libldap_r/, X-Git-Tag: OPENLDAP_REL_ENG_2_2_MP~409 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=123948bd5ee67881ba59fd2604581c75524d8770;p=openldap Add thread debugging wrapper thr_debug.c and ldap_thr_debug.h in libldap_r/, enabled with LDAP_THREAD_DEBUG (cpp macro and environment variable): Move any ldap_pvt_* definitions from ldap_int_thread.h to ldap_pvt_thread.h. #define ldap_int_thread_equal/ldap_pvt_thread_equal instead of tpool.c:TID_EQ. Define some ldap_debug_*_t types, and LDAP_UINTPTR_T, in ldap_int_thread.h. ldap_int_thread.h/ldap_pvt_thread.h can now be included multiple times, giving different results depending on whether libldap_r/ldap_thr_debug.h was included. Add some cleanup and some preprocessor hacks. #define LDAP_THREAD*_IMPLEMENTATION in libldap_r/*.c, used by ldap_thr_debug.h. Add PTHREAD_MUTEX_ERRORCHECK/PTHREAD_MUTEX_ERRORCHECK_NP in thr_posix.c. --- diff --git a/include/ldap_int_thread.h b/include/ldap_int_thread.h index 385fef52f6..307d85c86e 100644 --- a/include/ldap_int_thread.h +++ b/include/ldap_int_thread.h @@ -14,6 +14,16 @@ * . */ + +LDAP_BEGIN_DECL + +/* Can be done twice in libldap_r. See libldap_r/ldap_thr_debug.h. */ +LDAP_F(int) ldap_int_thread_initialize LDAP_P(( void )); +LDAP_F(int) ldap_int_thread_destroy LDAP_P(( void )); + +LDAP_END_DECL + + #ifndef _LDAP_INT_THREAD_H #define _LDAP_INT_THREAD_H @@ -35,6 +45,8 @@ typedef pthread_t ldap_int_thread_t; typedef pthread_mutex_t ldap_int_thread_mutex_t; typedef pthread_cond_t ldap_int_thread_cond_t; +#define ldap_int_thread_equal(a, b) pthread_equal((a), (b)) + #if defined( _POSIX_REENTRANT_FUNCTIONS ) || \ defined( _POSIX_THREAD_SAFE_FUNCTIONS ) || \ defined( _POSIX_THREADSAFE_FUNCTIONS ) @@ -53,7 +65,7 @@ typedef pthread_cond_t ldap_int_thread_cond_t; #if 0 && defined( HAVE_PTHREAD_RWLOCK_DESTROY ) #define LDAP_THREAD_HAVE_RDWR 1 -typedef pthread_rwlock_t ldap_pvt_thread_rdwr_t; +typedef pthread_rwlock_t ldap_int_thread_rdwr_t; #endif LDAP_END_DECL @@ -97,7 +109,7 @@ typedef pth_cond_t ldap_int_thread_cond_t; #if 0 #define LDAP_THREAD_HAVE_RDWR 1 -typedef pth_rwlock_t ldap_pvt_thread_rdwr_t; +typedef pth_rwlock_t ldap_int_thread_rdwr_t; #endif LDAP_END_DECL @@ -155,6 +167,11 @@ typedef struct ldap_int_thread_lwp_cv ldap_int_thread_cond_t; LDAP_END_DECL #elif defined(HAVE_NT_THREADS) +/************************************* + * * + * thread definitions for NT threads * + * * + *************************************/ #include #include @@ -168,7 +185,6 @@ typedef HANDLE ldap_int_thread_cond_t; LDAP_END_DECL #else - /*********************************** * * * thread definitions for no * @@ -193,10 +209,17 @@ LDAP_END_DECL #endif /* no threads support */ + LDAP_BEGIN_DECL -LDAP_F(int) ldap_int_thread_initialize LDAP_P(( void )); -LDAP_F(int) ldap_int_thread_destroy LDAP_P(( void )); +#ifndef ldap_int_thread_equal +#define ldap_int_thread_equal(a, b) ((a) == (b)) +#endif + +#ifndef LDAP_THREAD_HAVE_RDWR +typedef struct ldap_int_thread_rdwr_s * ldap_int_thread_rdwr_t; +#endif + LDAP_F(int) ldap_int_thread_pool_startup ( void ); LDAP_F(int) ldap_int_thread_pool_shutdown ( void ); @@ -206,4 +229,46 @@ typedef struct ldap_int_thread_pool_s * ldap_int_thread_pool_t; LDAP_END_DECL + +#if defined(LDAP_THREAD_DEBUG) && !((LDAP_THREAD_DEBUG +0) & 2U) +#define LDAP_THREAD_DEBUG_WRAP 1 +#endif + +#ifdef LDAP_THREAD_DEBUG_WRAP +/************************************** + * * + * definitions for type-wrapped debug * + * * + **************************************/ + +LDAP_BEGIN_DECL + +#ifndef LDAP_UINTPTR_T /* May be configured in CPPFLAGS */ +#define LDAP_UINTPTR_T unsigned long +#endif + +typedef union { + unsigned char *ptr; + LDAP_UINTPTR_T num; +} ldap_debug_usage_info_t; + +typedef struct { + ldap_int_thread_mutex_t wrapped; + ldap_debug_usage_info_t usage; +} ldap_debug_thread_mutex_t; + +typedef struct { + ldap_int_thread_cond_t wrapped; + ldap_debug_usage_info_t usage; +} ldap_debug_thread_cond_t; + +typedef struct { + ldap_int_thread_rdwr_t wrapped; + ldap_debug_usage_info_t usage; +} ldap_debug_thread_rdwr_t; + +LDAP_END_DECL + +#endif /* LDAP_THREAD_DEBUG_WRAP */ + #endif /* _LDAP_INT_THREAD_H */ diff --git a/include/ldap_pvt_thread.h b/include/ldap_pvt_thread.h index 79c821dd65..47bcf129bb 100644 --- a/include/ldap_pvt_thread.h +++ b/include/ldap_pvt_thread.h @@ -15,16 +15,27 @@ */ #ifndef _LDAP_PVT_THREAD_H -#define _LDAP_PVT_THREAD_H +#define _LDAP_PVT_THREAD_H /* libldap_r/ldap_thr_debug.h #undefines this */ #include "ldap_cdefs.h" #include "ldap_int_thread.h" LDAP_BEGIN_DECL -typedef ldap_int_thread_t ldap_pvt_thread_t; -typedef ldap_int_thread_mutex_t ldap_pvt_thread_mutex_t; -typedef ldap_int_thread_cond_t ldap_pvt_thread_cond_t; +#ifndef LDAP_PVT_THREAD_H_DONE +typedef ldap_int_thread_t ldap_pvt_thread_t; +#ifdef LDAP_THREAD_DEBUG_WRAP +typedef ldap_debug_thread_mutex_t ldap_pvt_thread_mutex_t; +typedef ldap_debug_thread_cond_t ldap_pvt_thread_cond_t; +typedef ldap_debug_thread_rdwr_t ldap_pvt_thread_rdwr_t; +#else +typedef ldap_int_thread_mutex_t ldap_pvt_thread_mutex_t; +typedef ldap_int_thread_cond_t ldap_pvt_thread_cond_t; +typedef ldap_int_thread_rdwr_t ldap_pvt_thread_rdwr_t; +#endif +#endif /* !LDAP_PVT_THREAD_H_DONE */ + +#define ldap_pvt_thread_equal ldap_int_thread_equal LDAP_F( int ) ldap_pvt_thread_initialize LDAP_P(( void )); @@ -44,6 +55,7 @@ ldap_pvt_thread_set_concurrency LDAP_P(( int )); #define LDAP_PVT_THREAD_CREATE_JOINABLE 0 #define LDAP_PVT_THREAD_CREATE_DETACHED 1 +#ifndef LDAP_PVT_THREAD_H_DONE #define LDAP_PVT_THREAD_SET_STACK_SIZE #ifndef LDAP_PVT_THREAD_STACK_SIZE /* LARGE stack. Will be twice as large on 64 bit machine. */ @@ -52,6 +64,7 @@ ldap_pvt_thread_set_concurrency LDAP_P(( int )); #elif LDAP_PVT_THREAD_STACK_SIZE == 0 #undef LDAP_PVT_THREAD_SET_STACK_SIZE #endif +#endif /* !LDAP_PVT_THREAD_H_DONE */ LDAP_F( int ) ldap_pvt_thread_create LDAP_P(( @@ -107,10 +120,6 @@ ldap_pvt_thread_mutex_unlock LDAP_P(( ldap_pvt_thread_mutex_t *mutex )); LDAP_F( ldap_pvt_thread_t ) ldap_pvt_thread_self LDAP_P(( void )); -#ifndef LDAP_THREAD_HAVE_RDWR -typedef struct ldap_int_thread_rdwr_s * ldap_pvt_thread_rdwr_t; -#endif - LDAP_F( int ) ldap_pvt_thread_rdwr_init LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp)); @@ -149,10 +158,12 @@ ldap_pvt_thread_rdwr_active LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp)); #define LDAP_PVT_THREAD_EINVAL EINVAL #define LDAP_PVT_THREAD_EBUSY EINVAL +#ifndef LDAP_PVT_THREAD_H_DONE typedef ldap_int_thread_pool_t ldap_pvt_thread_pool_t; typedef void * (ldap_pvt_thread_start_t) LDAP_P((void *ctx, void *arg)); typedef void (ldap_pvt_thread_pool_keyfree_t) LDAP_P((void *key, void *data)); +#endif /* !LDAP_PVT_THREAD_H_DONE */ LDAP_F( int ) ldap_pvt_thread_pool_init LDAP_P(( @@ -213,4 +224,5 @@ ldap_pvt_thread_pool_context_reset LDAP_P(( void *key )); LDAP_END_DECL +#define LDAP_PVT_THREAD_H_DONE #endif /* _LDAP_THREAD_H */ diff --git a/libraries/libldap_r/Makefile.in b/libraries/libldap_r/Makefile.in index 09554ea9ad..bc658b84f3 100644 --- a/libraries/libldap_r/Makefile.in +++ b/libraries/libldap_r/Makefile.in @@ -31,10 +31,10 @@ XXSRCS = apitest.c test.c \ turn.c groupings.c txn.c ppolicy.c SRCS = threads.c rdwr.c tpool.c rq.c \ thr_posix.c thr_cthreads.c thr_thr.c thr_lwp.c thr_nt.c \ - thr_pth.c thr_stub.c + thr_pth.c thr_stub.c thr_debug.c OBJS = threads.lo rdwr.lo tpool.lo rq.lo \ thr_posix.lo thr_cthreads.lo thr_thr.lo thr_lwp.lo thr_nt.lo \ - thr_pth.lo thr_stub.lo \ + thr_pth.lo thr_stub.lo thr_debug.lo \ bind.lo open.lo result.lo error.lo compare.lo search.lo \ controls.lo messages.lo references.lo extended.lo cyrus.lo \ modify.lo add.lo modrdn.lo delete.lo abandon.lo \ diff --git a/libraries/libldap_r/ldap_thr_debug.h b/libraries/libldap_r/ldap_thr_debug.h new file mode 100644 index 0000000000..9c15bb1c79 --- /dev/null +++ b/libraries/libldap_r/ldap_thr_debug.h @@ -0,0 +1,175 @@ +/* ldap_thr_debug.h - preprocessor magic for LDAP_THREAD_DEBUG */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * Copyright 2005 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 the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * . + */ + +#ifdef LDAP_THREAD_DEBUG + +/* + * libldap_r .c files should include this file after ldap_pvt_thread.h, + * with the appropriate LDAP_THREAD*_IMPLEMENTATION macro defined. + */ + +#ifndef _LDAP_PVT_THREAD_H +#error "ldap_pvt_thread.h" must be included before "ldap_thr_debug.h" +#endif + +/* + * Support for thr_debug.c: + * + * thr_debug.c defines the ldap_pvt_*() as wrappers around + * ldap_int_*(), and ldap_debug_*() around ldap_int_*(). + * + * Renames ldap_pvt_thread_* names to ldap_int_thread_*, and a few + * ldap_int_*() names to ldap_debug_*(). Includes "ldap_pvt_thread.h" + * to declare these renamed functions, and undefines the macros + * afterwards when included from thr_debug.c. So, + * + * libldap_r/ define ldap_int_* instead of ldap_pvt_*. + * In thread.c, ldap_pvt_thread_() will call + * ldap_debug_*() instead of ldap_int_*(). + * In tpool.c, ldap_int_thread_pool_shutdown() has thr_debug support + * which treats ldap_pvt_thread_pool_destroy() the same way. + */ + +#ifndef LDAP_THREAD_IMPLEMENTATION /* for first part of threads.c */ +#define ldap_int_thread_initialize ldap_debug_thread_initialize +#define ldap_int_thread_destroy ldap_debug_thread_destroy +#else /* LDAP_THREAD_IMPLEMENTATION -- for thr_*.c and end of threads.c */ +#undef ldap_int_thread_initialize +#undef ldap_int_thread_destroy +#ifdef LDAP_THREAD_DEBUG_WRAP /* see ldap_pvt_thread.h */ +#define ldap_pvt_thread_mutex_t ldap_int_thread_mutex_t +#define ldap_pvt_thread_cond_t ldap_int_thread_cond_t +#endif +#define ldap_pvt_thread_sleep ldap_int_thread_sleep +#define ldap_pvt_thread_get_concurrency ldap_int_thread_get_concurrency +#define ldap_pvt_thread_set_concurrency ldap_int_thread_set_concurrency +#define ldap_pvt_thread_create ldap_int_thread_create +#define ldap_pvt_thread_exit ldap_int_thread_exit +#define ldap_pvt_thread_join ldap_int_thread_join +#define ldap_pvt_thread_kill ldap_int_thread_kill +#define ldap_pvt_thread_yield ldap_int_thread_yield +#define ldap_pvt_thread_cond_init ldap_int_thread_cond_init +#define ldap_pvt_thread_cond_destroy ldap_int_thread_cond_destroy +#define ldap_pvt_thread_cond_signal ldap_int_thread_cond_signal +#define ldap_pvt_thread_cond_broadcast ldap_int_thread_cond_broadcast +#define ldap_pvt_thread_cond_wait ldap_int_thread_cond_wait +#define ldap_pvt_thread_mutex_init ldap_int_thread_mutex_init +#define ldap_pvt_thread_mutex_destroy ldap_int_thread_mutex_destroy +#define ldap_pvt_thread_mutex_lock ldap_int_thread_mutex_lock +#define ldap_pvt_thread_mutex_trylock ldap_int_thread_mutex_trylock +#define ldap_pvt_thread_mutex_unlock ldap_int_thread_mutex_unlock +#define ldap_pvt_thread_self ldap_int_thread_self +#endif /* LDAP_THREAD_IMPLEMENTATION */ + +#ifdef LDAP_THREAD_RDWR_IMPLEMENTATION /* rdwr.c, thr_debug.c */ +#ifdef LDAP_THREAD_DEBUG_WRAP /* see ldap_pvt_thread.h */ +#define ldap_pvt_thread_rdwr_t ldap_int_thread_rdwr_t +#endif +#define ldap_pvt_thread_rdwr_init ldap_int_thread_rdwr_init +#define ldap_pvt_thread_rdwr_destroy ldap_int_thread_rdwr_destroy +#define ldap_pvt_thread_rdwr_rlock ldap_int_thread_rdwr_rlock +#define ldap_pvt_thread_rdwr_rtrylock ldap_int_thread_rdwr_rtrylock +#define ldap_pvt_thread_rdwr_runlock ldap_int_thread_rdwr_runlock +#define ldap_pvt_thread_rdwr_wlock ldap_int_thread_rdwr_wlock +#define ldap_pvt_thread_rdwr_wtrylock ldap_int_thread_rdwr_wtrylock +#define ldap_pvt_thread_rdwr_wunlock ldap_int_thread_rdwr_wunlock +#define ldap_pvt_thread_rdwr_readers ldap_int_thread_rdwr_readers +#define ldap_pvt_thread_rdwr_writers ldap_int_thread_rdwr_writers +#define ldap_pvt_thread_rdwr_active ldap_int_thread_rdwr_active +#endif /* LDAP_THREAD_RDWR_IMPLEMENTATION */ + +#ifdef LDAP_THREAD_POOL_IMPLEMENTATION /* tpool.c, thr_stub.c, thr_debug.c */ +#ifdef LDAP_THREAD_DEBUG_WRAP /* see ldap_pvt_thread.h */ +#define ldap_pvt_thread_pool_t ldap_int_thread_pool_t +#endif +#define ldap_pvt_thread_pool_init ldap_int_thread_pool_init +#define ldap_pvt_thread_pool_submit ldap_int_thread_pool_submit +#define ldap_pvt_thread_pool_maxthreads ldap_int_thread_pool_maxthreads +#define ldap_pvt_thread_pool_backload ldap_int_thread_pool_backload +#define ldap_pvt_thread_pool_pause ldap_int_thread_pool_pause +#define ldap_pvt_thread_pool_resume ldap_int_thread_pool_resume +#define ldap_pvt_thread_pool_destroy ldap_int_thread_pool_destroy +#define ldap_pvt_thread_pool_getkey ldap_int_thread_pool_getkey +#define ldap_pvt_thread_pool_setkey ldap_int_thread_pool_setkey +#define ldap_pvt_thread_pool_purgekey ldap_int_thread_pool_purgekey +#define ldap_pvt_thread_pool_context ldap_int_thread_pool_context +#define ldap_pvt_thread_pool_context_reset ldap_int_thread_pool_context_reset +#endif /* LDAP_THREAD_POOL_IMPLEMENTATION */ + +#undef _LDAP_PVT_THREAD_H +#include "ldap_pvt_thread.h" + +#ifdef LDAP_THREAD_POOL_IMPLEMENTATION /* tpool.c */ +/* + * tpool.c:ldap_int_thread_pool_shutdown() needs this. Could not + * use it for ldap_pvt_thread.h above because of its use of LDAP_P(). + */ +#undef ldap_pvt_thread_pool_destroy +#define ldap_pvt_thread_pool_destroy(p,r) ldap_int_thread_pool_destroy(p,r) +#endif + +#ifdef LDAP_THREAD_DEBUG_IMPLEMENTATION /* thr_debug.c */ +#undef ldap_pvt_thread_mutex_t +#undef ldap_pvt_thread_cond_t +#undef ldap_pvt_thread_sleep +#undef ldap_pvt_thread_get_concurrency +#undef ldap_pvt_thread_set_concurrency +#undef ldap_pvt_thread_create +#undef ldap_pvt_thread_exit +#undef ldap_pvt_thread_join +#undef ldap_pvt_thread_kill +#undef ldap_pvt_thread_yield +#undef ldap_pvt_thread_cond_init +#undef ldap_pvt_thread_cond_destroy +#undef ldap_pvt_thread_cond_signal +#undef ldap_pvt_thread_cond_broadcast +#undef ldap_pvt_thread_cond_wait +#undef ldap_pvt_thread_mutex_init +#undef ldap_pvt_thread_mutex_destroy +#undef ldap_pvt_thread_mutex_lock +#undef ldap_pvt_thread_mutex_trylock +#undef ldap_pvt_thread_mutex_unlock +#undef ldap_pvt_thread_self +/* LDAP_THREAD_RDWR_IMPLEMENTATION: */ +#undef ldap_pvt_thread_rdwr_t +#undef ldap_pvt_thread_rdwr_init +#undef ldap_pvt_thread_rdwr_destroy +#undef ldap_pvt_thread_rdwr_rlock +#undef ldap_pvt_thread_rdwr_rtrylock +#undef ldap_pvt_thread_rdwr_runlock +#undef ldap_pvt_thread_rdwr_wlock +#undef ldap_pvt_thread_rdwr_wtrylock +#undef ldap_pvt_thread_rdwr_wunlock +#undef ldap_pvt_thread_rdwr_readers +#undef ldap_pvt_thread_rdwr_writers +#undef ldap_pvt_thread_rdwr_active +/* LDAP_THREAD_POOL_IMPLEMENTATION: */ +#undef ldap_pvt_thread_pool_t +#undef ldap_pvt_thread_pool_init +#undef ldap_pvt_thread_pool_submit +#undef ldap_pvt_thread_pool_maxthreads +#undef ldap_pvt_thread_pool_backload +#undef ldap_pvt_thread_pool_pause +#undef ldap_pvt_thread_pool_resume +#undef ldap_pvt_thread_pool_destroy +#undef ldap_pvt_thread_pool_getkey +#undef ldap_pvt_thread_pool_setkey +#undef ldap_pvt_thread_pool_purgekey +#undef ldap_pvt_thread_pool_context +#undef ldap_pvt_thread_pool_context_reset +#endif /* LDAP_THREAD_DEBUG_IMPLEMENTATION */ + +#endif /* LDAP_THREAD_DEBUG */ diff --git a/libraries/libldap_r/rdwr.c b/libraries/libldap_r/rdwr.c index 5acf713e11..bbae423bc6 100644 --- a/libraries/libldap_r/rdwr.c +++ b/libraries/libldap_r/rdwr.c @@ -40,7 +40,9 @@ #include #include "ldap-int.h" -#include "ldap_pvt_thread.h" +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_RDWR_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ /* * implementations that provide their own compatible @@ -439,6 +441,6 @@ int ldap_pvt_thread_rdwr_active(ldap_pvt_thread_rdwr_t *rwlock) ldap_pvt_thread_rdwr_writers(rwlock)); } -#endif /* LDAP_DEBUG */ +#endif /* LDAP_RDWR_DEBUG */ #endif /* LDAP_THREAD_HAVE_RDWR */ diff --git a/libraries/libldap_r/thr_cthreads.c b/libraries/libldap_r/thr_cthreads.c index d81e0101b5..83ba5aeb50 100644 --- a/libraries/libldap_r/thr_cthreads.c +++ b/libraries/libldap_r/thr_cthreads.c @@ -20,7 +20,9 @@ #include "portable.h" #if defined( HAVE_MACH_CTHREADS ) -#include "ldap_pvt_thread.h" +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ int ldap_int_thread_initialize( void ) diff --git a/libraries/libldap_r/thr_debug.c b/libraries/libldap_r/thr_debug.c new file mode 100644 index 0000000000..cc69b067d9 --- /dev/null +++ b/libraries/libldap_r/thr_debug.c @@ -0,0 +1,1029 @@ +/* thr_debug.c - wrapper around the chosen thread wrapper, for debugging. */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * Copyright 2005 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 ERROR(var,msg) (error(__FILE__, __LINE__, (msg), #var, (var))) +#define WARN_IF(rc, msg) {if (rc) warn (__FILE__, __LINE__, (msg), #rc, (rc));} +#define ERROR_IF(rc,msg) {if (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 ) +{ + if( !noerror ) { + 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 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 */ + +#define WRAPPED(ptr) (&(ptr)->wrapped) + +#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 ); + rc = ldap_int_thread_cond_wait( WRAPPED( cond ), WRAPPED( 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 { + 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" ); + rc = ldap_int_thread_mutex_destroy( WRAPPED( mutex ) ); + if( rc ) { + ERROR( rc, "ldap_pvt_thread_mutex_destroy" ); + } else { + free_usage( &mutex->usage, "ldap_pvt_thread_mutex_destroy" ); + 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 { + 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 ) + 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" ); + 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 */ diff --git a/libraries/libldap_r/thr_lwp.c b/libraries/libldap_r/thr_lwp.c index 6d66e39136..be72e98976 100644 --- a/libraries/libldap_r/thr_lwp.c +++ b/libraries/libldap_r/thr_lwp.c @@ -38,7 +38,9 @@ #include "ldap-int.h" -#include "ldap_pvt_thread.h" +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ #include #include @@ -67,7 +69,7 @@ ldap_int_thread_initialize( void ) int ldap_int_thread_destroy( void ) { - /* need to destory lwp_scheduler thread and clean up private + /* need to destroy lwp_scheduler thread and clean up private variables */ return 0; } @@ -311,7 +313,7 @@ ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond ) int ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond, - ldap_int_thread_mutex_t *mutex ) + ldap_pvt_thread_mutex_t *mutex ) { if ( ! cond->lcv_created ) { cv_create( &cond->lcv_cv, *mutex ); diff --git a/libraries/libldap_r/thr_nt.c b/libraries/libldap_r/thr_nt.c index 24a7a8d4ae..8953f4bdaf 100644 --- a/libraries/libldap_r/thr_nt.c +++ b/libraries/libldap_r/thr_nt.c @@ -18,7 +18,9 @@ #if defined( HAVE_NT_THREADS ) -#include "ldap_pvt_thread.h" +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ typedef struct ldap_int_thread_s { long tid; diff --git a/libraries/libldap_r/thr_posix.c b/libraries/libldap_r/thr_posix.c index 0d353206fa..cb964d982d 100644 --- a/libraries/libldap_r/thr_posix.c +++ b/libraries/libldap_r/thr_posix.c @@ -20,8 +20,10 @@ #include -#include "ldap_pvt_thread.h" - +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_IMPLEMENTATION +#define LDAP_THREAD_RDWR_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ #if HAVE_PTHREADS < 6 # define LDAP_INT_THREAD_ATTR_DEFAULT pthread_attr_default @@ -30,13 +32,29 @@ #else # define LDAP_INT_THREAD_ATTR_DEFAULT NULL # define LDAP_INT_THREAD_CONDATTR_DEFAULT NULL -# define LDAP_INT_THREAD_MUTEXATTR_DEFAULT NULL +# define LDAP_INT_THREAD_MUTEXATTR_DEFAULT NULL #endif +#ifdef LDAP_THREAD_DEBUG +# if defined LDAP_INT_THREAD_MUTEXATTR /* May be defined in CPPFLAGS */ +# elif defined HAVE_PTHREAD_KILL_OTHER_THREADS_NP + /* LinuxThreads hack */ +# define LDAP_INT_THREAD_MUTEXATTR PTHREAD_MUTEX_ERRORCHECK_NP +# else +# define LDAP_INT_THREAD_MUTEXATTR PTHREAD_MUTEX_ERRORCHECK +# endif +static pthread_mutexattr_t mutex_attr; +# undef LDAP_INT_THREAD_MUTEXATTR_DEFAULT +# define LDAP_INT_THREAD_MUTEXATTR_DEFAULT &mutex_attr +#endif int ldap_int_thread_initialize( void ) { +#ifdef LDAP_INT_THREAD_MUTEXATTR + pthread_mutexattr_init( &mutex_attr ); + pthread_mutexattr_settype( &mutex_attr, LDAP_INT_THREAD_MUTEXATTR ); +#endif return 0; } @@ -46,6 +64,9 @@ ldap_int_thread_destroy( void ) #ifdef HAVE_PTHREAD_KILL_OTHER_THREADS_NP /* LinuxThreads: kill clones */ pthread_kill_other_threads_np(); +#endif +#ifdef LDAP_INT_THREAD_MUTEXATTR + pthread_mutexattr_destroy( &mutex_attr ); #endif return 0; } @@ -407,7 +428,7 @@ int ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rw ) #endif } -#endif /* HAVE_PTHREAD_RDLOCK_DESTROY */ +#endif /* HAVE_PTHREAD_RWLOCK_DESTROY */ #endif /* LDAP_THREAD_HAVE_RDWR */ #endif /* HAVE_PTHREADS */ diff --git a/libraries/libldap_r/thr_pth.c b/libraries/libldap_r/thr_pth.c index 9b413ebd15..2d6012ba96 100644 --- a/libraries/libldap_r/thr_pth.c +++ b/libraries/libldap_r/thr_pth.c @@ -18,7 +18,11 @@ #if defined( HAVE_GNU_PTH ) -#include "ldap_pvt_thread.h" +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_IMPLEMENTATION +#define LDAP_THREAD_RDWR_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ + #include /******************* diff --git a/libraries/libldap_r/thr_stub.c b/libraries/libldap_r/thr_stub.c index 03c3b1be60..c4db69659a 100644 --- a/libraries/libldap_r/thr_stub.c +++ b/libraries/libldap_r/thr_stub.c @@ -18,7 +18,10 @@ #if defined( NO_THREADS ) -#include "ldap_pvt_thread.h" +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_IMPLEMENTATION +#define LDAP_THREAD_POOL_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ /*********************************************************************** * * diff --git a/libraries/libldap_r/thr_thr.c b/libraries/libldap_r/thr_thr.c index e609365447..edbd78a349 100644 --- a/libraries/libldap_r/thr_thr.c +++ b/libraries/libldap_r/thr_thr.c @@ -18,7 +18,9 @@ #if defined( HAVE_THR ) -#include "ldap_pvt_thread.h" +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ /******************* * * diff --git a/libraries/libldap_r/threads.c b/libraries/libldap_r/threads.c index d19400b971..e2ae4e5211 100644 --- a/libraries/libldap_r/threads.c +++ b/libraries/libldap_r/threads.c @@ -22,7 +22,8 @@ #include #include -#include "ldap_pvt_thread.h" +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#include "ldap_thr_debug.h" /* May redirect thread initialize/destroy calls */ /* @@ -60,6 +61,15 @@ int ldap_pvt_thread_destroy( void ) return ldap_int_thread_destroy(); } + +/* + * Default implementations of some LDAP thread routines + */ + +#define LDAP_THREAD_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ + + #ifndef LDAP_THREAD_HAVE_GETCONCURRENCY int ldap_pvt_thread_get_concurrency ( void ) diff --git a/libraries/libldap_r/tpool.c b/libraries/libldap_r/tpool.c index d65d9c40b6..ba87eaa989 100644 --- a/libraries/libldap_r/tpool.c +++ b/libraries/libldap_r/tpool.c @@ -24,8 +24,10 @@ #include #include "ldap-int.h" -#include "ldap_pvt_thread.h" +#include "ldap_pvt_thread.h" /* Get the thread interface */ #include "ldap_queue.h" +#define LDAP_THREAD_POOL_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename symbols defined below */ #ifndef LDAP_THREAD_HAVE_TPOOL @@ -50,11 +52,6 @@ typedef struct ldap_int_thread_key_s { static ldap_pvt_thread_t tid_zero; -#ifdef HAVE_PTHREADS -#define TID_EQ(a,b) pthread_equal((a),(b)) -#else -#define TID_EQ(a,b) ((a) == (b)) -#endif static struct { ldap_pvt_thread_t id; ldap_int_thread_key_t *ctx; @@ -115,7 +112,7 @@ ldap_int_thread_pool_shutdown ( void ) while ((pool = LDAP_STAILQ_FIRST(&ldap_int_thread_pool_list)) != NULL) { LDAP_STAILQ_REMOVE_HEAD(&ldap_int_thread_pool_list, ltp_next); - ldap_pvt_thread_pool_destroy( &pool, 0); + (ldap_pvt_thread_pool_destroy)(&pool, 0); /* ignore thr_debug macro */ } ldap_pvt_thread_mutex_destroy(&ldap_pvt_thread_pool_mutex); return(0); @@ -277,7 +274,7 @@ ldap_pvt_thread_pool_submit ( */ TID_HASH(thr, hash); for (rc = hash & (LDAP_MAXTHR-1); - !TID_EQ(thread_keys[rc].id, tid_zero); + !ldap_pvt_thread_equal(thread_keys[rc].id, tid_zero); rc = (rc+1) & (LDAP_MAXTHR-1)); thread_keys[rc].id = thr; } else { @@ -437,7 +434,8 @@ ldap_int_thread_pool_wrapper ( /* store pointer to our keys */ TID_HASH(tid, hash); - for (i = hash & (LDAP_MAXTHR-1); !TID_EQ(thread_keys[i].id, tid); + for (i = hash & (LDAP_MAXTHR-1); + !ldap_pvt_thread_equal(thread_keys[i].id, tid); i = (i+1) & (LDAP_MAXTHR-1)); thread_keys[i].ctx = ltc_key; keyslot = i; @@ -661,12 +659,14 @@ void *ldap_pvt_thread_pool_context( ) int i, hash; tid = ldap_pvt_thread_self(); - if ( TID_EQ( tid, ldap_int_main_tid )) + if ( ldap_pvt_thread_equal( tid, ldap_int_main_tid )) return ldap_int_main_thrctx; TID_HASH( tid, hash ); - for (i = hash & (LDAP_MAXTHR-1); !TID_EQ(thread_keys[i].id, tid_zero) && - !TID_EQ(thread_keys[i].id, tid); i = (i+1) & (LDAP_MAXTHR-1)); + for (i = hash & (LDAP_MAXTHR-1); + !ldap_pvt_thread_equal(thread_keys[i].id, tid_zero) && + !ldap_pvt_thread_equal(thread_keys[i].id, tid); + i = (i+1) & (LDAP_MAXTHR-1)); return thread_keys[i].ctx; }