Home | History | Annotate | Line # | Download | only in libldap
      1 /*	$NetBSD: thr_debug.c,v 1.3 2025/09/05 21:16:22 christos Exp $	*/
      2 
      3 /* thr_debug.c - wrapper around the chosen thread wrapper, for debugging. */
      4 /* $OpenLDAP$ */
      5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      6  *
      7  * Copyright 2005-2024 The OpenLDAP Foundation.
      8  * All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted only as authorized by the OpenLDAP
     12  * Public License.
     13  *
     14  * A copy of this license is available in file LICENSE in the
     15  * top-level directory of the distribution or, alternatively, at
     16  * <http://www.OpenLDAP.org/license.html>.
     17  */
     18 
     19 /*
     20  * This package provides several types of thread operation debugging:
     21  *
     22  * - Check the results of operations on threads, mutexes, condition
     23  *   variables and read/write locks.  Also check some thread pool
     24  *   operations, but not those for which failure can happen in normal
     25  *   slapd operation.
     26  *
     27  * - Wrap those types except threads and pools in structs with state
     28  *   information, and check that on all operations:
     29  *
     30  *   + Check that the resources are initialized and are only used at
     31  *     their original address (i.e. not realloced or copied).
     32  *
     33  *   + Check the owner (thread ID) on mutex operations.
     34  *
     35  *   + Optionally allocate a reference to a byte of dummy memory.
     36  *     This lets malloc debuggers see some incorrect use as memory
     37  *     leaks, access to freed memory, etc.
     38  *
     39  * - Print an error message and by default abort() upon errors.
     40  *
     41  * - Print a count of leaked thread resources after cleanup.
     42  *
     43  * Compile-time (./configure) setup:  Macros defined in CPPFLAGS.
     44  *
     45  *   LDAP_THREAD_DEBUG or LDAP_THREAD_DEBUG=2
     46  *      Enables debugging, but value & 2 turns off type wrapping.
     47  *
     48  *   LDAP_UINTPTR_T=integer type to hold pointers, preferably unsigned.
     49  *      Used by dummy memory option "scramble". Default = unsigned long.
     50  *
     51  *   LDAP_DEBUG_THREAD_NONE = initializer for a "no thread" thread ID.
     52  *
     53  *   In addition, you may need to set up an implementation-specific way
     54  *      to enable whatever error checking your thread library provides.
     55  *      Currently only implemented for Posix threads (pthreads), where
     56  *      you may need to define LDAP_INT_THREAD_MUTEXATTR.  The default
     57  *      is PTHREAD_MUTEX_ERRORCHECK, or PTHREAD_MUTEX_ERRORCHECK_NP for
     58  *      Linux threads.  See pthread_mutexattr_settype(3).
     59  *
     60  * Run-time configuration:
     61  *
     62  *  Memory debugging tools:
     63  *   Tools that report uninitialized memory accesses should disable
     64  *   such warnings about the function debug_already_initialized().
     65  *   Alternatively, include "noreinit" (below) in $LDAP_THREAD_DEBUG.
     66  *
     67  *  Environment variable $LDAP_THREAD_DEBUG:
     68  *   The variable may contain a comma- or space-separated option list.
     69  *   Options:
     70  *      off      - Disable this package.  (It still slows things down).
     71  *      tracethreads - Report create/join/exit/kill of threads.
     72  *      noabort  - Do not abort() on errors.
     73  *      noerror  - Do not report errors.  Implies noabort.
     74  *      nocount  - Do not report counts of unreleased resources.
     75  *      nosync   - Disable tests that use synchronization and thus
     76  *                 clearly affect thread scheduling:
     77  *                 Implies nocount, and cancels threadID if that is set.
     78  *                 Note that if you turn on tracethreads or malloc
     79  *                 debugging, these also use library calls which may
     80  *                 affect thread scheduling (fprintf and malloc).
     81  *   The following options do not apply if type wrapping is disabled:
     82  *      nomem    - Do not check memory operations.
     83  *                 Implies noreinit,noalloc.
     84  *      noreinit - Do not catch reinitialization of existing resources.
     85  *                 (That test accesses uninitialized memory).
     86  *      threadID - Trace thread IDs.  Currently mostly useless.
     87  *     Malloc debugging -- allocate dummy memory for initialized
     88  *     resources, so malloc debuggers will report them as memory leaks:
     89  *      noalloc  - Default.  Do not allocate dummy memory.
     90  *      alloc    - Store a pointer to dummy memory.   However, leak
     91  *                 detectors might not catch unreleased resources in
     92  *                 global variables.
     93  *      scramble - Store bitwise complement of dummy memory pointer.
     94  *                 That never escapes memory leak detectors -
     95  *                 but detection while the program is running will
     96  *                 report active resources as leaks.  Do not
     97  *                 use this if a garbage collector is in use:-)
     98  *      adjptr   - Point to end of dummy memory.
     99  *                 Purify reports these as "potential leaks" (PLK).
    100  *                 I have not checked other malloc debuggers.
    101  */
    102 
    103 #include <sys/cdefs.h>
    104 __RCSID("$NetBSD: thr_debug.c,v 1.3 2025/09/05 21:16:22 christos Exp $");
    105 
    106 #include "portable.h"
    107 
    108 #if defined( LDAP_THREAD_DEBUG )
    109 
    110 #include <stdio.h>
    111 #include <ac/errno.h>
    112 #include <ac/stdlib.h>
    113 #include <ac/string.h>
    114 
    115 #include "ldap_pvt_thread.h" /* Get the thread interface */
    116 #define LDAP_THREAD_IMPLEMENTATION
    117 #define LDAP_THREAD_DEBUG_IMPLEMENTATION
    118 #define LDAP_THREAD_RDWR_IMPLEMENTATION
    119 #define LDAP_THREAD_POOL_IMPLEMENTATION
    120 #include "ldap_thr_debug.h"  /* Get the underlying implementation */
    121 
    122 #ifndef LDAP_THREAD_DEBUG_WRAP
    123 #undef	LDAP_THREAD_DEBUG_THREAD_ID
    124 #elif !defined LDAP_THREAD_DEBUG_THREAD_ID
    125 #define	LDAP_THREAD_DEBUG_THREAD_ID 1
    126 #endif
    127 
    128 /* Use native malloc - the OpenLDAP wrappers may defeat malloc debuggers */
    129 #undef malloc
    130 #undef calloc
    131 #undef realloc
    132 #undef free
    133 
    134 
    135 /* Options from environment variable $LDAP_THREAD_DEBUG */
    136 enum { Count_no = 0, Count_yes, Count_reported, Count_reported_more };
    137 static int count = Count_yes;
    138 #ifdef LDAP_THREAD_DEBUG_WRAP
    139 enum { Wrap_noalloc, Wrap_alloc, Wrap_scramble, Wrap_adjptr };
    140 static int wraptype = Wrap_noalloc, wrap_offset, unwrap_offset;
    141 static int nomem, noreinit;
    142 #endif
    143 #if LDAP_THREAD_DEBUG_THREAD_ID +0
    144 static int threadID;
    145 #else
    146 enum { threadID = 0 };
    147 #endif
    148 static int nodebug, noabort, noerror, nosync, tracethreads;
    149 static int wrap_threads;
    150 static int options_done;
    151 
    152 
    153 /* ldap_pvt_thread_initialize() called, ldap_pvt_thread_destroy() not called */
    154 static int threading_enabled;
    155 
    156 
    157 /* Resource counts */
    158 enum {
    159 	Idx_unexited_thread, Idx_unjoined_thread, Idx_locked_mutex,
    160 	Idx_mutex, Idx_cond, Idx_rdwr, Idx_tpool, Idx_max
    161 };
    162 static int resource_counts[Idx_max];
    163 static const char *const resource_names[] = {
    164 	"unexited threads", "unjoined threads", "locked mutexes",
    165 	"mutexes", "conds", "rdwrs", "thread pools"
    166 };
    167 static ldap_int_thread_mutex_t resource_mutexes[Idx_max];
    168 
    169 
    170 /* Hide pointers from malloc debuggers. */
    171 #define SCRAMBLE(ptr) (~(LDAP_UINTPTR_T) (ptr))
    172 #define UNSCRAMBLE_usagep(num) ((ldap_debug_usage_info_t *) ~(num))
    173 #define UNSCRAMBLE_dummyp(num) ((unsigned char *) ~(num))
    174 
    175 
    176 #define WARN(var, msg)   (warn (__FILE__, __LINE__, (msg), #var, (var)))
    177 #define WARN_IF(rc, msg) {if (rc) warn (__FILE__, __LINE__, (msg), #rc, (rc));}
    178 
    179 #define ERROR(var, msg) { \
    180 	if (!noerror) { \
    181 		errmsg(__FILE__, __LINE__, (msg), #var, (var)); \
    182 		if( !noabort ) abort(); \
    183 	} \
    184 }
    185 
    186 #define ERROR_IF(rc, msg) { \
    187 	if (!noerror) { \
    188 		int rc_ = (rc); \
    189 		if (rc_) { \
    190 			errmsg(__FILE__, __LINE__, (msg), #rc, rc_); \
    191 			if( !noabort ) abort(); \
    192 		} \
    193 	} \
    194 }
    195 
    196 #ifdef LDAP_THREAD_DEBUG_WRAP
    197 #define MEMERROR_IF(rc, msg, mem_act) { \
    198 	if (!noerror) { \
    199 		int rc_ = (rc); \
    200 		if (rc_) { \
    201 			errmsg(__FILE__, __LINE__, (msg), #rc, rc_); \
    202 			if( wraptype != Wrap_noalloc ) { mem_act; } \
    203 			if( !noabort ) abort(); \
    204 		} \
    205 	} \
    206 }
    207 #endif /* LDAP_THREAD_DEBUG_WRAP */
    208 
    209 #if 0
    210 static void
    211 warn( const char *file, int line, const char *msg, const char *var, int val )
    212 {
    213 	fprintf( stderr,
    214 		(strpbrk( var, "!=" )
    215 		 ? "%s:%d: %s warning: %s\n"
    216 		 : "%s:%d: %s warning: %s is %d\n"),
    217 		file, line, msg, var, val );
    218 }
    219 #endif
    220 
    221 static void
    222 errmsg( const char *file, int line, const char *msg, const char *var, int val )
    223 {
    224 	fprintf( stderr,
    225 		(strpbrk( var, "!=" )
    226 		 ? "%s:%d: %s error: %s\n"
    227 		 : "%s:%d: %s error: %s is %d\n"),
    228 		file, line, msg, var, val );
    229 }
    230 
    231 static void
    232 count_resource_leaks( void )
    233 {
    234 	int i, j;
    235 	char errbuf[200];
    236 	if( count == Count_yes ) {
    237 		count = Count_reported;
    238 #if 0 /* Could break if there are still threads after atexit */
    239 		for( i = j = 0; i < Idx_max; i++ )
    240 			j |= ldap_int_thread_mutex_destroy( &resource_mutexes[i] );
    241 		WARN_IF( j, "ldap_debug_thread_destroy:mutexes" );
    242 #endif
    243 		for( i = j = 0; i < Idx_max; i++ )
    244 			if( resource_counts[i] )
    245 				j += sprintf( errbuf + j, ", %d %s",
    246 					resource_counts[i], resource_names[i] );
    247 		if( j )
    248 			fprintf( stderr, "== thr_debug: Leaked%s. ==\n", errbuf + 1 );
    249 	}
    250 }
    251 
    252 static void
    253 get_options( void )
    254 {
    255 	static const struct option_info_s {
    256 		const char	*name;
    257 		int       	*var, val;
    258 	} option_info[] = {
    259 		{ "off",        &nodebug,  1 },
    260 		{ "noabort",    &noabort,  1 },
    261 		{ "noerror",    &noerror,  1 },
    262 		{ "nocount",    &count,    Count_no },
    263 		{ "nosync",     &nosync,   1 },
    264 #if LDAP_THREAD_DEBUG_THREAD_ID +0
    265 		{ "threadID",   &threadID, 1 },
    266 #endif
    267 #ifdef LDAP_THREAD_DEBUG_WRAP
    268 		{ "nomem",      &nomem,    1 },
    269 		{ "noreinit",   &noreinit, 1 },
    270 		{ "noalloc",    &wraptype, Wrap_noalloc },
    271 		{ "alloc",      &wraptype, Wrap_alloc },
    272 		{ "adjptr",     &wraptype, Wrap_adjptr },
    273 		{ "scramble",	&wraptype, Wrap_scramble },
    274 #endif
    275 		{ "tracethreads", &tracethreads, 1 },
    276 		{ NULL, NULL, 0 }
    277 	};
    278 	const char *s = getenv( "LDAP_THREAD_DEBUG" );
    279 	if( s != NULL ) {
    280 		while( *(s += strspn( s, ", \t\r\n" )) != '\0' ) {
    281 			size_t optlen = strcspn( s, ", \t\r\n" );
    282 			const struct option_info_s *oi = option_info;
    283 			while( oi->name &&
    284 				   (strncasecmp( oi->name, s, optlen ) || oi->name[optlen]) )
    285 				oi++;
    286 			if( oi->name )
    287 				*oi->var = oi->val;
    288 			else
    289 				fprintf( stderr,
    290 					"== thr_debug: Unknown $%s option '%.*s' ==\n",
    291 					"LDAP_THREAD_DEBUG", (int) optlen, s );
    292 			s += optlen;
    293 		}
    294 	}
    295 	if( nodebug ) {
    296 		tracethreads = 0;
    297 		nosync = noerror = 1;
    298 	}
    299 	if( nosync )
    300 		count = Count_no;
    301 	if( noerror )
    302 		noabort = 1;
    303 #if LDAP_THREAD_DEBUG_THREAD_ID +0
    304 	if( nosync )
    305 		threadID = 0;
    306 #endif
    307 #ifdef LDAP_THREAD_DEBUG_WRAP
    308 	if( noerror )
    309 		nomem = 1;
    310 	if( !nomem ) {
    311 		static const ldap_debug_usage_info_t usage;
    312 		if( sizeof(LDAP_UINTPTR_T) < sizeof(unsigned char *)
    313 			|| sizeof(LDAP_UINTPTR_T) < sizeof(ldap_debug_usage_info_t *)
    314 			|| UNSCRAMBLE_usagep( SCRAMBLE( &usage ) ) != &usage
    315 			|| UNSCRAMBLE_dummyp( SCRAMBLE( (unsigned char *) 0 ) ) )
    316 		{
    317 			fputs( "== thr_debug: Memory checks unsupported, "
    318 				"adding nomem to $LDAP_THREAD_DEBUG ==\n", stderr );
    319 			nomem = 1;
    320 		}
    321 	}
    322 	if( nomem ) {
    323 		noreinit = 1;
    324 		wraptype = Wrap_noalloc;
    325 	}
    326 	unwrap_offset = -(wrap_offset = (wraptype == Wrap_adjptr));
    327 #endif
    328 	wrap_threads = (tracethreads || threadID || count);
    329 	options_done = 1;
    330 }
    331 
    332 
    333 #ifndef LDAP_THREAD_DEBUG_WRAP
    334 
    335 #define	WRAPPED(ptr)			(ptr)
    336 #define	GET_OWNER(ptr)			0
    337 #define	SET_OWNER(ptr, thread)	((void) 0)
    338 #define	RESET_OWNER(ptr)		((void) 0)
    339 #define	ASSERT_OWNER(ptr, msg)	((void) 0)
    340 #define	ASSERT_NO_OWNER(ptr, msg) ((void) 0)
    341 
    342 #define init_usage(ptr, msg)	((void) 0)
    343 #define check_usage(ptr, msg)	((void) 0)
    344 #define destroy_usage(ptr)		((void) 0)
    345 
    346 #else /* LDAP_THREAD_DEBUG_WRAP */
    347 
    348 /* Specialize this if the initializer is not appropriate. */
    349 /* The ASSERT_NO_OWNER() definition may also need an override. */
    350 #ifndef LDAP_DEBUG_THREAD_NONE
    351 #define	LDAP_DEBUG_THREAD_NONE { -1 } /* "no thread" ldap_int_thread_t value */
    352 #endif
    353 
    354 static const ldap_int_thread_t ldap_debug_thread_none = LDAP_DEBUG_THREAD_NONE;
    355 
    356 #define THREAD_MUTEX_OWNER(mutex) \
    357 	ldap_int_thread_equal( (mutex)->owner, ldap_int_thread_self() )
    358 
    359 void
    360 ldap_debug_thread_assert_mutex_owner(
    361 	const char *file,
    362 	int line,
    363 	const char *msg,
    364 	ldap_pvt_thread_mutex_t *mutex )
    365 {
    366 	if( !(noerror || THREAD_MUTEX_OWNER( mutex )) ) {
    367 		errmsg( file, line, msg, "ASSERT_MUTEX_OWNER", 0 );
    368 		if( !noabort ) abort();
    369 	}
    370 }
    371 
    372 #define	WRAPPED(ptr)			(&(ptr)->wrapped)
    373 #define	GET_OWNER(ptr)			((ptr)->owner)
    374 #define	SET_OWNER(ptr, thread)	((ptr)->owner = (thread))
    375 #define	RESET_OWNER(ptr)		((ptr)->owner = ldap_debug_thread_none)
    376 #define	ASSERT_OWNER(ptr, msg)	ERROR_IF( !THREAD_MUTEX_OWNER( ptr ), msg )
    377 #ifndef	ASSERT_NO_OWNER
    378 #define	ASSERT_NO_OWNER(ptr, msg) ERROR_IF( \
    379 	!ldap_int_thread_equal( (ptr)->owner, ldap_debug_thread_none ), msg )
    380 #endif
    381 
    382 /* Try to provoke memory access error (for malloc debuggers) */
    383 #define PEEK(mem) {if (-*(volatile const unsigned char *)(mem)) debug_noop();}
    384 
    385 static void debug_noop( void );
    386 static int debug_already_initialized( const ldap_debug_usage_info_t *usage );
    387 
    388 /* Name used for clearer error message */
    389 #define IS_COPY_OR_MOVED(usage) ((usage)->self != SCRAMBLE( usage ))
    390 
    391 #define DUMMY_ADDR(usage) \
    392 	(wraptype == Wrap_scramble \
    393 	 ? UNSCRAMBLE_dummyp( (usage)->mem.num ) \
    394 	 : (usage)->mem.ptr + unwrap_offset)
    395 
    396 /* Mark resource as initialized */
    397 static void
    398 init_usage( ldap_debug_usage_info_t *usage, const char *msg )
    399 {
    400 	if( !options_done )
    401 		get_options();
    402 	if( !nomem ) {
    403 		if( !noreinit ) {
    404 			MEMERROR_IF( debug_already_initialized( usage ), msg, {
    405 				/* Provoke malloc debuggers */
    406 				unsigned char *dummy = DUMMY_ADDR( usage );
    407 				PEEK( dummy );
    408 				free( dummy );
    409 				free( dummy );
    410 			} );
    411 		}
    412 		if( wraptype != Wrap_noalloc ) {
    413 			unsigned char *dummy = malloc( 1 );
    414 			assert( dummy != NULL );
    415 			if( wraptype == Wrap_scramble ) {
    416 				usage->mem.num = SCRAMBLE( dummy );
    417 				/* Verify that ptr<->integer casts work on this host */
    418 				assert( UNSCRAMBLE_dummyp( usage->mem.num ) == dummy );
    419 			} else {
    420 				usage->mem.ptr = dummy + wrap_offset;
    421 			}
    422 		}
    423 	} else {
    424 		/* Unused, but set for readability in debugger */
    425 		usage->mem.ptr = NULL;
    426 	}
    427 	usage->self = SCRAMBLE( usage );	/* If nomem, only for debugger */
    428 	usage->magic = ldap_debug_magic;
    429 	usage->state = ldap_debug_state_inited;
    430 }
    431 
    432 /* Check that resource is initialized and not copied/realloced */
    433 static void
    434 check_usage( const ldap_debug_usage_info_t *usage, const char *msg )
    435 {
    436 	enum { Is_destroyed = 1 };	/* Name used for clearer error message */
    437 
    438 	if( usage->magic != ldap_debug_magic ) {
    439 		ERROR( usage->magic, msg );
    440 		return;
    441 	}
    442 	switch( usage->state ) {
    443 	case ldap_debug_state_destroyed:
    444 		MEMERROR_IF( Is_destroyed, msg, {
    445 			PEEK( DUMMY_ADDR( usage ) );
    446 		} );
    447 		break;
    448 	default:
    449 		ERROR( usage->state, msg );
    450 		break;
    451 	case ldap_debug_state_inited:
    452 		if( !nomem ) {
    453 			MEMERROR_IF( IS_COPY_OR_MOVED( usage ), msg, {
    454 				PEEK( DUMMY_ADDR( usage ) );
    455 				PEEK( UNSCRAMBLE_usagep( usage->self ) );
    456 			} );
    457 		}
    458 		break;
    459 	}
    460 }
    461 
    462 /* Mark resource as destroyed. */
    463 /* Does not check for errors, call check_usage()/init_usage() first. */
    464 static void
    465 destroy_usage( ldap_debug_usage_info_t *usage )
    466 {
    467 	if( usage->state == ldap_debug_state_inited ) {
    468 		if( wraptype != Wrap_noalloc ) {
    469 			free( DUMMY_ADDR( usage ) );
    470 			/* Do not reset the DUMMY_ADDR, leave it for malloc debuggers
    471 			 * in case the resource is used after it is freed. */
    472 		}
    473 		usage->state = ldap_debug_state_destroyed;
    474 	}
    475 }
    476 
    477 /* Define these after they are used, so they are hopefully not inlined */
    478 
    479 static void
    480 debug_noop( void )
    481 {
    482 }
    483 
    484 /*
    485  * Valid programs access uninitialized memory here unless "noreinit".
    486  *
    487  * Returns true if the resource is initialized and not copied/realloced.
    488  */
    489 LDAP_GCCATTR((noinline))
    490 static int
    491 debug_already_initialized( const ldap_debug_usage_info_t *usage )
    492 {
    493 	/*
    494 	 * 'ret' keeps the Valgrind warning "Conditional jump or move
    495 	 * depends on uninitialised value(s)" _inside_ this function.
    496 	 */
    497 	volatile int ret = 0;
    498 	if( usage->state == ldap_debug_state_inited )
    499 		if( !IS_COPY_OR_MOVED( usage ) )
    500 	        if( usage->magic == ldap_debug_magic )
    501 				ret = 1;
    502 	return ret;
    503 }
    504 
    505 #endif /* LDAP_THREAD_DEBUG_WRAP */
    506 
    507 
    508 #if !(LDAP_THREAD_DEBUG_THREAD_ID +0)
    509 
    510 typedef void ldap_debug_thread_t;
    511 #define init_thread_info()	{}
    512 #define with_thread_info_lock(statements) { statements; }
    513 #define thread_info_detached(t)	0
    514 #define add_thread_info(msg, thr, det)	((void) 0)
    515 #define remove_thread_info(tinfo, msg)	((void) 0)
    516 #define get_thread_info(thread, msg)	NULL
    517 
    518 #else /* LDAP_THREAD_DEBUG_THREAD_ID */
    519 
    520 /*
    521  * Thread ID tracking.  Currently achieves little.
    522  * Should be either expanded or deleted.
    523  */
    524 
    525 /*
    526  * Array of threads.  Used instead of making ldap_pvt_thread_t a wrapper
    527  * around ldap_int_thread_t, which would slow down ldap_pvt_thread_self().
    528  */
    529 typedef struct {
    530 	ldap_pvt_thread_t           wrapped;
    531 	ldap_debug_usage_info_t     usage;
    532 	int                         detached;
    533 	int                         idx;
    534 } ldap_debug_thread_t;
    535 
    536 static ldap_debug_thread_t      **thread_info;
    537 static unsigned int             thread_info_size, thread_info_used;
    538 static ldap_int_thread_mutex_t  thread_info_mutex;
    539 
    540 #define init_thread_info() { \
    541 	if( threadID ) { \
    542 		int mutex_init_rc = ldap_int_thread_mutex_init( &thread_info_mutex ); \
    543 		assert( mutex_init_rc == 0 ); \
    544 	} \
    545 }
    546 
    547 #define with_thread_info_lock(statements) { \
    548 	int rc_wtl_ = ldap_int_thread_mutex_lock( &thread_info_mutex ); \
    549 	assert( rc_wtl_ == 0 ); \
    550 	{ statements; } \
    551 	rc_wtl_ = ldap_int_thread_mutex_unlock( &thread_info_mutex ); \
    552 	assert( rc_wtl_ == 0 ); \
    553 }
    554 
    555 #define thread_info_detached(t) ((t)->detached)
    556 
    557 static void
    558 add_thread_info(
    559 	const char *msg,
    560 	const ldap_pvt_thread_t *thread,
    561 	int detached )
    562 {
    563 	ldap_debug_thread_t *t;
    564 
    565 	if( thread_info_used >= thread_info_size ) {
    566 		unsigned int more = thread_info_size + 8;
    567 		unsigned int new_size = thread_info_size + more;
    568 
    569 		t = calloc( more, sizeof(ldap_debug_thread_t) );
    570 		assert( t != NULL );
    571 		thread_info = realloc( thread_info, new_size * sizeof(*thread_info) );
    572 		assert( thread_info != NULL );
    573 		do {
    574 			t->idx = thread_info_size;
    575 			thread_info[thread_info_size++] = t++;
    576 		} while( thread_info_size < new_size );
    577 	}
    578 
    579 	t = thread_info[thread_info_used];
    580 	init_usage( &t->usage, msg );
    581 	t->wrapped = *thread;
    582 	t->detached = detached;
    583 	thread_info_used++;
    584 }
    585 
    586 static void
    587 remove_thread_info( ldap_debug_thread_t *t, const char *msg )
    588 {
    589 		ldap_debug_thread_t *last;
    590 		int idx;
    591 		check_usage( &t->usage, msg );
    592 		destroy_usage( &t->usage );
    593 		idx = t->idx;
    594 		assert( thread_info[idx] == t );
    595 		last = thread_info[--thread_info_used];
    596 		assert( last->idx == thread_info_used );
    597 		(thread_info[idx]              = last)->idx = idx;
    598 		(thread_info[thread_info_used] = t   )->idx = thread_info_used;
    599 }
    600 
    601 static ldap_debug_thread_t *
    602 get_thread_info( ldap_pvt_thread_t thread, const char *msg )
    603 {
    604 	unsigned int i;
    605 	ldap_debug_thread_t *t;
    606 	for( i = 0; i < thread_info_used; i++ ) {
    607 		if( ldap_pvt_thread_equal( thread, thread_info[i]->wrapped ) )
    608 			break;
    609 	}
    610 	ERROR_IF( i == thread_info_used, msg );
    611 	t = thread_info[i];
    612 	check_usage( &t->usage, msg );
    613 	return t;
    614 }
    615 
    616 #endif /* LDAP_THREAD_DEBUG_THREAD_ID */
    617 
    618 
    619 static char *
    620 thread_name( char *buf, int bufsize, ldap_pvt_thread_t thread )
    621 {
    622 	int i;
    623 	--bufsize;
    624 	if( bufsize > 2*sizeof(thread) )
    625 		bufsize = 2*sizeof(thread);
    626 	for( i = 0; i < bufsize; i += 2 )
    627 		snprintf( buf+i, 3, "%02x", ((unsigned char *)&thread)[i/2] );
    628 	return buf;
    629 }
    630 
    631 
    632 /* Add <adjust> (+/-1) to resource count <which> unless "nocount". */
    633 static void
    634 adjust_count( int which, int adjust )
    635 {
    636 	int rc;
    637 	switch( count ) {
    638 	case Count_no:
    639 		break;
    640 	case Count_yes:
    641 		rc = ldap_int_thread_mutex_lock( &resource_mutexes[which] );
    642 		assert( rc == 0 );
    643 		resource_counts[which] += adjust;
    644 		rc = ldap_int_thread_mutex_unlock( &resource_mutexes[which] );
    645 		assert( rc == 0 );
    646 		break;
    647 	case Count_reported:
    648 		fputs( "== thr_debug: More thread activity after exit ==\n", stderr );
    649 		count = Count_reported_more;
    650 		/* FALL THROUGH */
    651 	case Count_reported_more:
    652 		/* Not used, but result might be inspected with debugger */
    653 		/* (Hopefully threading is disabled by now...) */
    654 		resource_counts[which] += adjust;
    655 		break;
    656 	}
    657 }
    658 
    659 
    660 /* Wrappers for LDAP_THREAD_IMPLEMENTATION: */
    661 
    662 /* Used instead of ldap_int_thread_initialize by ldap_pvt_thread_initialize */
    663 int
    664 ldap_debug_thread_initialize( void )
    665 {
    666 	int i, rc, rc2;
    667 	if( !options_done )
    668 		get_options();
    669 	ERROR_IF( threading_enabled, "ldap_debug_thread_initialize" );
    670 	threading_enabled = 1;
    671 	rc = ldap_int_thread_initialize();
    672 	if( rc ) {
    673 		ERROR( rc, "ldap_debug_thread_initialize:threads" );
    674 		threading_enabled = 0;
    675 	} else {
    676 		init_thread_info();
    677 		if( count != Count_no ) {
    678 			for( i = rc2 = 0; i < Idx_max; i++ )
    679 				rc2 |= ldap_int_thread_mutex_init( &resource_mutexes[i] );
    680 			assert( rc2 == 0 );
    681 			/* FIXME: Only for static libldap as in init.c? If so, why? */
    682 			atexit( count_resource_leaks );
    683 		}
    684 	}
    685 	return rc;
    686 }
    687 
    688 /* Used instead of ldap_int_thread_destroy by ldap_pvt_thread_destroy */
    689 int
    690 ldap_debug_thread_destroy( void )
    691 {
    692 	int rc;
    693 	ERROR_IF( !threading_enabled, "ldap_debug_thread_destroy" );
    694 	/* sleep(1) -- need to wait for thread pool to finish? */
    695 	rc = ldap_int_thread_destroy();
    696 	if( rc ) {
    697 		ERROR( rc, "ldap_debug_thread_destroy:threads" );
    698 	} else {
    699 		threading_enabled = 0;
    700 	}
    701 	return rc;
    702 }
    703 
    704 int
    705 ldap_pvt_thread_set_concurrency( int n )
    706 {
    707 	int rc;
    708 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_set_concurrency" );
    709 	rc = ldap_int_thread_set_concurrency( n );
    710 	ERROR_IF( rc, "ldap_pvt_thread_set_concurrency" );
    711 	return rc;
    712 }
    713 
    714 int
    715 ldap_pvt_thread_get_concurrency( void )
    716 {
    717 	int rc;
    718 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_get_concurrency" );
    719 	rc = ldap_int_thread_get_concurrency();
    720 	ERROR_IF( rc, "ldap_pvt_thread_get_concurrency" );
    721 	return rc;
    722 }
    723 
    724 unsigned int
    725 ldap_pvt_thread_sleep( unsigned int interval )
    726 {
    727 	int rc;
    728 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_sleep" );
    729 	rc = ldap_int_thread_sleep( interval );
    730 	ERROR_IF( rc, "ldap_pvt_thread_sleep" );
    731 	return 0;
    732 }
    733 
    734 static void
    735 thread_exiting( const char *how, const char *msg )
    736 {
    737 	ldap_pvt_thread_t thread;
    738 #if 0 /* Detached threads may exit after ldap_debug_thread_destroy(). */
    739 	ERROR_IF( !threading_enabled, msg );
    740 #endif
    741 	thread = ldap_pvt_thread_self();
    742 	if( tracethreads ) {
    743 		char buf[40];
    744 		fprintf( stderr, "== thr_debug: %s thread %s ==\n",
    745 			how, thread_name( buf, sizeof(buf), thread ) );
    746 	}
    747 	if( threadID ) {
    748 		with_thread_info_lock({
    749 			ldap_debug_thread_t *t = get_thread_info( thread, msg );
    750 			if( thread_info_detached( t ) )
    751 				remove_thread_info( t, msg );
    752 		});
    753 	}
    754 	adjust_count( Idx_unexited_thread, -1 );
    755 }
    756 
    757 void
    758 ldap_pvt_thread_exit( void *retval )
    759 {
    760 	thread_exiting( "Exiting", "ldap_pvt_thread_exit" );
    761 	ldap_int_thread_exit( retval );
    762 }
    763 
    764 typedef struct {
    765 	void *(*start_routine)( void * );
    766 	void *arg;
    767 } ldap_debug_thread_call_t;
    768 
    769 static void *
    770 ldap_debug_thread_wrapper( void *arg )
    771 {
    772 	void *ret;
    773 	ldap_debug_thread_call_t call = *(ldap_debug_thread_call_t *)arg;
    774 	free( arg );
    775 	ret = call.start_routine( call.arg );
    776 	thread_exiting( "Returning from", "ldap_debug_thread_wrapper" );
    777 	return ret;
    778 }
    779 
    780 int
    781 ldap_pvt_thread_create(
    782 	ldap_pvt_thread_t *thread,
    783 	int detach,
    784 	void *(*start_routine)( void * ),
    785 	void *arg )
    786 {
    787 	int rc;
    788 	if( !options_done )
    789 		get_options();
    790 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_create" );
    791 
    792 	if( wrap_threads ) {
    793 		ldap_debug_thread_call_t *call = malloc(
    794 			sizeof( ldap_debug_thread_call_t ) );
    795 		assert( call != NULL );
    796 		call->start_routine = start_routine;
    797 		call->arg = arg;
    798 		start_routine = ldap_debug_thread_wrapper;
    799 		arg = call;
    800 	}
    801 	if( threadID ) {
    802 		with_thread_info_lock({
    803 			rc = ldap_int_thread_create( thread, detach, start_routine, arg );
    804 			if( rc == 0 )
    805 				add_thread_info( "ldap_pvt_thread_create", thread, detach );
    806 		});
    807 	} else {
    808 		rc = ldap_int_thread_create( thread, detach, start_routine, arg );
    809 	}
    810 	if( rc ) {
    811 		ERROR( rc, "ldap_pvt_thread_create" );
    812 		if( wrap_threads )
    813 			free( arg );
    814 	} else {
    815 		if( tracethreads ) {
    816 			char buf[40], buf2[40];
    817 			fprintf( stderr,
    818 				"== thr_debug: Created thread %s%s from thread %s ==\n",
    819 				thread_name( buf, sizeof(buf), *thread ),
    820 				detach ? " (detached)" : "",
    821 				thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) );
    822 		}
    823 		adjust_count( Idx_unexited_thread, +1 );
    824 		if( !detach )
    825 			adjust_count( Idx_unjoined_thread, +1 );
    826 	}
    827 	return rc;
    828 }
    829 
    830 int
    831 ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
    832 {
    833 	int rc;
    834 	ldap_debug_thread_t *t = NULL;
    835 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_join" );
    836 	if( tracethreads ) {
    837 		char buf[40], buf2[40];
    838 		fprintf( stderr, "== thr_debug: Joining thread %s in thread %s ==\n",
    839 			thread_name( buf, sizeof(buf), thread ),
    840 			thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) );
    841 	}
    842 	if( threadID )
    843 		with_thread_info_lock( {
    844 			t = get_thread_info( thread, "ldap_pvt_thread_join" );
    845 			ERROR_IF( thread_info_detached( t ), "ldap_pvt_thread_join" );
    846 		} );
    847 	rc = ldap_int_thread_join( thread, thread_return );
    848 	if( rc ) {
    849 		ERROR( rc, "ldap_pvt_thread_join" );
    850 	} else {
    851 		if( threadID )
    852 			with_thread_info_lock(
    853 				remove_thread_info( t, "ldap_pvt_thread_join" ) );
    854 		adjust_count( Idx_unjoined_thread, -1 );
    855 	}
    856 
    857 	return rc;
    858 }
    859 
    860 int
    861 ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
    862 {
    863 	int rc;
    864 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_kill" );
    865 	if( tracethreads ) {
    866 		char buf[40], buf2[40];
    867 		fprintf( stderr,
    868 			"== thr_debug: Killing thread %s (sig %i) from thread %s ==\n",
    869 			thread_name( buf, sizeof(buf), thread ), signo,
    870 			thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) );
    871 	}
    872 	rc = ldap_int_thread_kill( thread, signo );
    873 	ERROR_IF( rc, "ldap_pvt_thread_kill" );
    874 	return rc;
    875 }
    876 
    877 int
    878 ldap_pvt_thread_yield( void )
    879 {
    880 	int rc;
    881 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_yield" );
    882 	rc = ldap_int_thread_yield();
    883 	ERROR_IF( rc, "ldap_pvt_thread_yield" );
    884 	return rc;
    885 }
    886 
    887 ldap_pvt_thread_t
    888 ldap_pvt_thread_self( void )
    889 {
    890 #if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */
    891 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_self" );
    892 #endif
    893 	return ldap_int_thread_self();
    894 }
    895 
    896 int
    897 ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
    898 {
    899 	int rc;
    900 	init_usage( &cond->usage, "ldap_pvt_thread_cond_init" );
    901 	rc = ldap_int_thread_cond_init( WRAPPED( cond ) );
    902 	if( rc ) {
    903 		ERROR( rc, "ldap_pvt_thread_cond_init" );
    904 		destroy_usage( &cond->usage );
    905 	} else {
    906 		adjust_count( Idx_cond, +1 );
    907 	}
    908 	return rc;
    909 }
    910 
    911 int
    912 ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cond )
    913 {
    914 	int rc;
    915 	check_usage( &cond->usage, "ldap_pvt_thread_cond_destroy" );
    916 	rc = ldap_int_thread_cond_destroy( WRAPPED( cond ) );
    917 	if( rc ) {
    918 		ERROR( rc, "ldap_pvt_thread_cond_destroy" );
    919 	} else {
    920 		destroy_usage( &cond->usage );
    921 		adjust_count( Idx_cond, -1 );
    922 	}
    923 	return rc;
    924 }
    925 
    926 int
    927 ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
    928 {
    929 	int rc;
    930 	check_usage( &cond->usage, "ldap_pvt_thread_cond_signal" );
    931 	rc = ldap_int_thread_cond_signal( WRAPPED( cond ) );
    932 	ERROR_IF( rc, "ldap_pvt_thread_cond_signal" );
    933 	return rc;
    934 }
    935 
    936 int
    937 ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond )
    938 {
    939 	int rc;
    940 	check_usage( &cond->usage, "ldap_pvt_thread_cond_broadcast" );
    941 	rc = ldap_int_thread_cond_broadcast( WRAPPED( cond ) );
    942 	ERROR_IF( rc, "ldap_pvt_thread_cond_broadcast" );
    943 	return rc;
    944 }
    945 
    946 int
    947 ldap_pvt_thread_cond_wait(
    948 	ldap_pvt_thread_cond_t *cond,
    949 	ldap_pvt_thread_mutex_t *mutex )
    950 {
    951 	int rc;
    952 	ldap_int_thread_t owner;
    953 	check_usage( &cond->usage, "ldap_pvt_thread_cond_wait:cond" );
    954 	check_usage( &mutex->usage, "ldap_pvt_thread_cond_wait:mutex" );
    955 	adjust_count( Idx_locked_mutex, -1 );
    956 	owner = GET_OWNER( mutex );
    957 	ASSERT_OWNER( mutex, "ldap_pvt_thread_cond_wait" );
    958 	RESET_OWNER( mutex );
    959 	rc = ldap_int_thread_cond_wait( WRAPPED( cond ), WRAPPED( mutex ) );
    960 	ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_cond_wait" );
    961 	SET_OWNER( mutex, rc ? owner : ldap_int_thread_self() );
    962 	adjust_count( Idx_locked_mutex, +1 );
    963 	ERROR_IF( rc, "ldap_pvt_thread_cond_wait" );
    964 	return rc;
    965 }
    966 
    967 int
    968 ldap_pvt_thread_mutex_recursive_init( ldap_pvt_thread_mutex_t *mutex )
    969 {
    970 	int rc;
    971 	init_usage( &mutex->usage, "ldap_pvt_thread_mutex_recursive_init" );
    972 	rc = ldap_int_thread_mutex_recursive_init( WRAPPED( mutex ) );
    973 	if( rc ) {
    974 		ERROR( rc, "ldap_pvt_thread_mutex_recursive_init" );
    975 		destroy_usage( &mutex->usage );
    976 	} else {
    977 		RESET_OWNER( mutex );
    978 		adjust_count( Idx_mutex, +1 );
    979 	}
    980 	return rc;
    981 }
    982 
    983 int
    984 ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
    985 {
    986 	int rc;
    987 	init_usage( &mutex->usage, "ldap_pvt_thread_mutex_init" );
    988 	rc = ldap_int_thread_mutex_init( WRAPPED( mutex ) );
    989 	if( rc ) {
    990 		ERROR( rc, "ldap_pvt_thread_mutex_init" );
    991 		destroy_usage( &mutex->usage );
    992 	} else {
    993 		RESET_OWNER( mutex );
    994 		adjust_count( Idx_mutex, +1 );
    995 	}
    996 	return rc;
    997 }
    998 
    999 int
   1000 ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
   1001 {
   1002 	int rc;
   1003 	check_usage( &mutex->usage, "ldap_pvt_thread_mutex_destroy" );
   1004 	ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_destroy" );
   1005 	rc = ldap_int_thread_mutex_destroy( WRAPPED( mutex ) );
   1006 	if( rc ) {
   1007 		ERROR( rc, "ldap_pvt_thread_mutex_destroy" );
   1008 	} else {
   1009 		destroy_usage( &mutex->usage );
   1010 		RESET_OWNER( mutex );
   1011 		adjust_count( Idx_mutex, -1 );
   1012 	}
   1013 	return rc;
   1014 }
   1015 
   1016 int
   1017 ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
   1018 {
   1019 	int rc;
   1020 	check_usage( &mutex->usage, "ldap_pvt_thread_mutex_lock" );
   1021 	rc = ldap_int_thread_mutex_lock( WRAPPED( mutex ) );
   1022 	if( rc ) {
   1023 		ERROR_IF( rc, "ldap_pvt_thread_mutex_lock" );
   1024 	} else {
   1025 		ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_lock" );
   1026 		SET_OWNER( mutex, ldap_int_thread_self() );
   1027 		adjust_count( Idx_locked_mutex, +1 );
   1028 	}
   1029 	return rc;
   1030 }
   1031 
   1032 int
   1033 ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex )
   1034 {
   1035 	int rc;
   1036 	check_usage( &mutex->usage, "ldap_pvt_thread_mutex_trylock" );
   1037 	rc = ldap_int_thread_mutex_trylock( WRAPPED( mutex ) );
   1038 	if( rc == 0 ) {
   1039 		ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_trylock" );
   1040 		SET_OWNER( mutex, ldap_int_thread_self() );
   1041 		adjust_count( Idx_locked_mutex, +1 );
   1042 	}
   1043 	return rc;
   1044 }
   1045 
   1046 int
   1047 ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
   1048 {
   1049 	int rc;
   1050 	check_usage( &mutex->usage, "ldap_pvt_thread_mutex_unlock" );
   1051 	ASSERT_OWNER( mutex, "ldap_pvt_thread_mutex_unlock" );
   1052 	RESET_OWNER( mutex ); /* Breaks if this thread did not own the mutex */
   1053 	rc = ldap_int_thread_mutex_unlock( WRAPPED( mutex ) );
   1054 	if( rc ) {
   1055 		ERROR_IF( rc, "ldap_pvt_thread_mutex_unlock" );
   1056 	} else {
   1057 		adjust_count( Idx_locked_mutex, -1 );
   1058 	}
   1059 	return rc;
   1060 }
   1061 
   1062 
   1063 /* Wrappers for LDAP_THREAD_RDWR_IMPLEMENTATION: */
   1064 
   1065 int
   1066 ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rwlock )
   1067 {
   1068 	int rc;
   1069 	init_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_init" );
   1070 	rc = ldap_int_thread_rdwr_init( WRAPPED( rwlock ) );
   1071 	if( rc ) {
   1072 		ERROR( rc, "ldap_pvt_thread_rdwr_init" );
   1073 		destroy_usage( &rwlock->usage );
   1074 	} else {
   1075 		adjust_count( Idx_rdwr, +1 );
   1076 	}
   1077 	return rc;
   1078 }
   1079 
   1080 int
   1081 ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rwlock )
   1082 {
   1083 	int rc;
   1084 	check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_destroy" );
   1085 	rc = ldap_int_thread_rdwr_destroy( WRAPPED( rwlock ) );
   1086 	if( rc ) {
   1087 		ERROR( rc, "ldap_pvt_thread_rdwr_destroy" );
   1088 	} else {
   1089 		destroy_usage( &rwlock->usage );
   1090 		adjust_count( Idx_rdwr, -1 );
   1091 	}
   1092 	return rc;
   1093 }
   1094 
   1095 int
   1096 ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rwlock )
   1097 {
   1098 	int rc;
   1099 	check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_rlock" );
   1100 	rc = ldap_int_thread_rdwr_rlock( WRAPPED( rwlock ) );
   1101 	ERROR_IF( rc, "ldap_pvt_thread_rdwr_rlock" );
   1102 	return rc;
   1103 }
   1104 
   1105 int
   1106 ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rwlock )
   1107 {
   1108 	check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_rtrylock" );
   1109 	return ldap_int_thread_rdwr_rtrylock( WRAPPED( rwlock ) );
   1110 }
   1111 
   1112 int
   1113 ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rwlock )
   1114 {
   1115 	int rc;
   1116 	check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_runlock" );
   1117 	rc = ldap_int_thread_rdwr_runlock( WRAPPED( rwlock ) );
   1118 	ERROR_IF( rc, "ldap_pvt_thread_rdwr_runlock" );
   1119 	return rc;
   1120 }
   1121 
   1122 int
   1123 ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rwlock )
   1124 {
   1125 	int rc;
   1126 	check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wlock" );
   1127 	rc = ldap_int_thread_rdwr_wlock( WRAPPED( rwlock ) );
   1128 	ERROR_IF( rc, "ldap_pvt_thread_rdwr_wlock" );
   1129 	return rc;
   1130 }
   1131 
   1132 int
   1133 ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rwlock )
   1134 {
   1135 	check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wtrylock" );
   1136 	return ldap_int_thread_rdwr_wtrylock( WRAPPED( rwlock ) );
   1137 }
   1138 
   1139 int
   1140 ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rwlock )
   1141 {
   1142 	int rc;
   1143 	check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wunlock" );
   1144 	rc = ldap_int_thread_rdwr_wunlock( WRAPPED( rwlock ) );
   1145 	ERROR_IF( rc, "ldap_pvt_thread_rdwr_wunlock" );
   1146 	return rc;
   1147 }
   1148 
   1149 #if defined(LDAP_RDWR_DEBUG) && !defined(LDAP_THREAD_HAVE_RDWR)
   1150 
   1151 int
   1152 ldap_pvt_thread_rdwr_readers( ldap_pvt_thread_rdwr_t *rwlock )
   1153 {
   1154 	check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_readers" );
   1155 	return ldap_int_thread_rdwr_readers( WRAPPED( rwlock ) );
   1156 }
   1157 
   1158 int
   1159 ldap_pvt_thread_rdwr_writers( ldap_pvt_thread_rdwr_t *rwlock )
   1160 {
   1161 	check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_writers" );
   1162 	return ldap_int_thread_rdwr_writers( WRAPPED( rwlock ) );
   1163 }
   1164 
   1165 int
   1166 ldap_pvt_thread_rdwr_active( ldap_pvt_thread_rdwr_t *rwlock )
   1167 {
   1168 	check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_active" );
   1169 	return ldap_int_thread_rdwr_active( WRAPPED( rwlock ) );
   1170 }
   1171 
   1172 #endif /* LDAP_RDWR_DEBUG && !LDAP_THREAD_HAVE_RDWR */
   1173 
   1174 
   1175 /* Some wrappers for LDAP_THREAD_POOL_IMPLEMENTATION: */
   1176 #ifdef LDAP_THREAD_POOL_IMPLEMENTATION
   1177 
   1178 int
   1179 ldap_pvt_thread_pool_init(
   1180 	ldap_pvt_thread_pool_t *tpool,
   1181 	int max_threads,
   1182 	int max_pending )
   1183 {
   1184 	int rc;
   1185 	if( !options_done )
   1186 		get_options();
   1187 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_init" );
   1188 	rc = ldap_int_thread_pool_init( tpool, max_threads, max_pending );
   1189 	if( rc ) {
   1190 		ERROR( rc, "ldap_pvt_thread_pool_init" );
   1191 	} else {
   1192 		adjust_count( Idx_tpool, +1 );
   1193 	}
   1194 	return rc;
   1195 }
   1196 
   1197 int
   1198 ldap_pvt_thread_pool_submit(
   1199 	ldap_pvt_thread_pool_t *tpool,
   1200 	ldap_pvt_thread_start_t *start_routine, void *arg )
   1201 {
   1202 	int rc, has_pool;
   1203 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_submit" );
   1204 	has_pool = (tpool && *tpool);
   1205 	rc = ldap_int_thread_pool_submit( tpool, start_routine, arg );
   1206 	if( has_pool )
   1207 		ERROR_IF( rc, "ldap_pvt_thread_pool_submit" );
   1208 	return rc;
   1209 }
   1210 
   1211 int
   1212 ldap_pvt_thread_pool_maxthreads(
   1213 	ldap_pvt_thread_pool_t *tpool,
   1214 	int max_threads )
   1215 {
   1216 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_maxthreads" );
   1217 	return ldap_int_thread_pool_maxthreads(	tpool, max_threads );
   1218 }
   1219 
   1220 int
   1221 ldap_pvt_thread_pool_backload( ldap_pvt_thread_pool_t *tpool )
   1222 {
   1223 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_backload" );
   1224 	return ldap_int_thread_pool_backload( tpool );
   1225 }
   1226 
   1227 int
   1228 ldap_pvt_thread_pool_destroy( ldap_pvt_thread_pool_t *tpool, int run_pending )
   1229 {
   1230 	int rc, has_pool;
   1231 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_destroy" );
   1232 	has_pool = (tpool && *tpool);
   1233 	rc = ldap_int_thread_pool_destroy( tpool, run_pending );
   1234 	if( has_pool ) {
   1235 		if( rc ) {
   1236 			ERROR( rc, "ldap_pvt_thread_pool_destroy" );
   1237 		} else {
   1238 			adjust_count( Idx_tpool, -1 );
   1239 		}
   1240 	}
   1241 	return rc;
   1242 }
   1243 
   1244 int
   1245 ldap_pvt_thread_pool_close( ldap_pvt_thread_pool_t *tpool, int run_pending )
   1246 {
   1247 	int rc, has_pool;
   1248 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_close" );
   1249 	has_pool = (tpool && *tpool);
   1250 	rc = ldap_int_thread_pool_close( tpool, run_pending );
   1251 	if( has_pool && rc ) {
   1252 		ERROR( rc, "ldap_pvt_thread_pool_close" );
   1253 	}
   1254 	return rc;
   1255 }
   1256 
   1257 int
   1258 ldap_pvt_thread_pool_free( ldap_pvt_thread_pool_t *tpool )
   1259 {
   1260 	int rc, has_pool;
   1261 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_free" );
   1262 	has_pool = (tpool && *tpool);
   1263 	rc = ldap_int_thread_pool_free( tpool );
   1264 	if( has_pool ) {
   1265 		if( rc ) {
   1266 			ERROR( rc, "ldap_pvt_thread_pool_free" );
   1267 		} else {
   1268 			adjust_count( Idx_tpool, -1 );
   1269 		}
   1270 	}
   1271 	return rc;
   1272 }
   1273 
   1274 int
   1275 ldap_pvt_thread_pool_pause( ldap_pvt_thread_pool_t *tpool )
   1276 {
   1277 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_pause" );
   1278 	return ldap_int_thread_pool_pause( tpool );
   1279 }
   1280 
   1281 int
   1282 ldap_pvt_thread_pool_resume( ldap_pvt_thread_pool_t *tpool )
   1283 {
   1284 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_resume" );
   1285 	return ldap_int_thread_pool_resume( tpool );
   1286 }
   1287 
   1288 int
   1289 ldap_pvt_thread_pool_getkey(
   1290 	void *xctx,
   1291 	void *key,
   1292 	void **data,
   1293 	ldap_pvt_thread_pool_keyfree_t **kfree )
   1294 {
   1295 #if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */
   1296 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_getkey" );
   1297 #endif
   1298 	return ldap_int_thread_pool_getkey( xctx, key, data, kfree );
   1299 }
   1300 
   1301 int
   1302 ldap_pvt_thread_pool_setkey(
   1303 	void *xctx,
   1304 	void *key,
   1305 	void *data,
   1306 	ldap_pvt_thread_pool_keyfree_t *kfree,
   1307 	void **olddatap,
   1308 	ldap_pvt_thread_pool_keyfree_t **oldkfreep )
   1309 {
   1310 	int rc;
   1311 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_setkey" );
   1312 	rc = ldap_int_thread_pool_setkey(
   1313 		xctx, key, data, kfree, olddatap, oldkfreep );
   1314 	ERROR_IF( rc, "ldap_pvt_thread_pool_setkey" );
   1315 	return rc;
   1316 }
   1317 
   1318 void
   1319 ldap_pvt_thread_pool_purgekey( void *key )
   1320 {
   1321 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_purgekey" );
   1322 	ldap_int_thread_pool_purgekey( key );
   1323 }
   1324 
   1325 void *
   1326 ldap_pvt_thread_pool_context( void )
   1327 {
   1328 #if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */
   1329 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_context" );
   1330 #endif
   1331 	return ldap_int_thread_pool_context();
   1332 }
   1333 
   1334 void
   1335 ldap_pvt_thread_pool_context_reset( void *vctx )
   1336 {
   1337 	ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_context_reset" );
   1338 	ldap_int_thread_pool_context_reset( vctx );
   1339 }
   1340 
   1341 #endif /* LDAP_THREAD_POOL_IMPLEMENTATION */
   1342 
   1343 #endif /* LDAP_THREAD_DEBUG */
   1344