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