]> git.sur5r.net Git - openldap/blob - libraries/libldap_r/thr_debug.c
LDAP_THREAD_DEBUG revamping, cleanup, bug fixes.
[openldap] / libraries / libldap_r / thr_debug.c
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-2006 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  *  Setup of 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 */
131 enum { Count_no = 0, Count_yes, Count_reported, Count_reported_more };
132 static int count = Count_yes;
133 #ifdef LDAP_THREAD_DEBUG_WRAP
134 enum { Wrap_noalloc, Wrap_alloc, Wrap_scramble, Wrap_adjptr };
135 static int wraptype = Wrap_noalloc, wrap_offset, unwrap_offset;
136 static int nomem, noreinit;
137 #endif
138 #if LDAP_THREAD_DEBUG_THREAD_ID +0
139 static int threadID;
140 #else
141 enum { threadID = 0 };
142 #endif
143 static int nodebug, noabort, noerror, nosync, tracethreads;
144 static int options_done;
145
146
147 /* ldap_pvt_thread_initialize() called, ldap_pvt_thread_destroy() not called */
148 static int threading_enabled;
149
150
151 /* Resource counts */
152 enum {
153         Idx_unexited_thread, Idx_unjoined_thread, Idx_locked_mutex,
154         Idx_mutex, Idx_cond, Idx_rdwr, Idx_tpool, Idx_max
155 };
156 static int resource_counts[Idx_max];
157 static const char *const resource_names[] = {
158         "unexited threads", "unjoined threads", "locked mutexes",
159         "mutexes", "conds", "rdwrs", "thread pools"
160 };
161 static ldap_int_thread_mutex_t resource_mutexes[Idx_max];
162
163
164 /* Hide pointers from malloc debuggers. */
165 #define SCRAMBLE(ptr) (~(LDAP_UINTPTR_T) (ptr))
166 #define UNSCRAMBLE_usagep(num) ((ldap_debug_usage_info_t *) ~(num))
167 #define UNSCRAMBLE_dummyp(num) ((unsigned char *) ~(num))
168
169
170 #define WARN(var, msg)   (warn (__FILE__, __LINE__, (msg), #var, (var)))
171 #define WARN_IF(rc, msg) {if (rc) warn (__FILE__, __LINE__, (msg), #rc, (rc));}
172
173 #define ERROR(var, msg) { \
174         if (!noerror) { \
175                 errmsg(__FILE__, __LINE__, (msg), #var, (var)); \
176                 if( !noabort ) abort(); \
177         } \
178 }
179
180 #define ERROR_IF(rc, msg) { \
181         if (!noerror) { \
182                 int rc_ = (rc); \
183                 if (rc_) { \
184                         errmsg(__FILE__, __LINE__, (msg), #rc, rc_); \
185                         if( !noabort ) abort(); \
186                 } \
187         } \
188 }
189
190 #ifdef LDAP_THREAD_DEBUG_WRAP
191 #define MEMERROR_IF(rc, msg, mem_act) { \
192         if (!noerror) { \
193                 int rc_ = (rc); \
194                 if (rc_) { \
195                         errmsg(__FILE__, __LINE__, (msg), #rc, rc_); \
196                         if( wraptype != Wrap_noalloc ) { mem_act; } \
197                         if( !noabort ) abort(); \
198                 } \
199         } \
200 }
201 #endif /* LDAP_THREAD_DEBUG_WRAP */
202
203 #if 0
204 static void
205 warn( const char *file, int line, const char *msg, const char *var, int val )
206 {
207         fprintf( stderr,
208                 (strpbrk( var, "!=" )
209                  ? "%s:%d: %s warning: %s\n"
210                  : "%s:%d: %s warning: %s is %d\n"),
211                 file, line, msg, var, val );
212 }
213 #endif
214
215 static void
216 errmsg( const char *file, int line, const char *msg, const char *var, int val )
217 {
218         fprintf( stderr,
219                 (strpbrk( var, "!=" )
220                  ? "%s:%d: %s error: %s\n"
221                  : "%s:%d: %s error: %s is %d\n"),
222                 file, line, msg, var, val );
223 }
224
225 static void
226 count_resource_leaks( void )
227 {
228         int i, j;
229         char errbuf[200];
230         if( count == Count_yes ) {
231                 count = Count_reported;
232 #if 0 /* Could break if there are still threads after atexit */
233                 for( i = j = 0; i < Idx_max; i++ )
234                         j |= ldap_int_thread_mutex_destroy( &resource_mutexes[i] );
235                 WARN_IF( j, "ldap_debug_thread_destroy:mutexes" );
236 #endif
237                 for( i = j = 0; i < Idx_max; i++ )
238                         if( resource_counts[i] )
239                                 j += sprintf( errbuf + j, ", %d %s",
240                                         resource_counts[i], resource_names[i] );
241                 if( j )
242                         fprintf( stderr, "== thr_debug: Leaked%s. ==\n", errbuf + 1 );
243         }
244 }
245
246 static void
247 get_options( void )
248 {
249         static const struct option_info_s {
250                 const char      *name;
251                 int             *var, val;
252         } option_info[] = {
253                 { "off",        &nodebug,  1 },
254                 { "noabort",    &noabort,  1 },
255                 { "noerror",    &noerror,  1 },
256                 { "nocount",    &count,    Count_no },
257                 { "nosync",     &nosync,   1 },
258 #if LDAP_THREAD_DEBUG_THREAD_ID +0
259                 { "threadID",   &threadID, 1 },
260 #endif
261 #ifdef LDAP_THREAD_DEBUG_WRAP
262                 { "nomem",      &nomem,    1 },
263                 { "noreinit",   &noreinit, 1 },
264                 { "noalloc",    &wraptype, Wrap_noalloc },
265                 { "alloc",      &wraptype, Wrap_alloc },
266                 { "adjptr",     &wraptype, Wrap_adjptr },
267                 { "scramble",   &wraptype, Wrap_scramble },
268 #endif
269                 { "tracethreads", &tracethreads, 1 },
270                 { NULL, NULL, 0 }
271         };
272         const char *s = getenv( "LDAP_THREAD_DEBUG" );
273         if( s != NULL ) {
274                 while( *(s += strspn( s, ", \t\r\n" )) != '\0' ) {
275                         size_t optlen = strcspn( s, ", \t\r\n" );
276                         const struct option_info_s *oi;
277                         for( oi = option_info; oi->name; oi++ ) {
278                                 if( strncasecmp( oi->name, s, optlen ) == 0 ) {
279                                         if( oi->name && oi->name[optlen] == '\0' ) {
280                                                 *oi->var = oi->val;
281                                         } else {
282                                                 fprintf( stderr,
283                                                         "== thr_debug: Unknown $%s option '%.*s' ==\n",
284                                                         "LDAP_THREAD_DEBUG", (int) optlen, s );
285                                         }
286                                         break;
287                                 }
288                         }
289                         s += optlen;
290                 }
291         }
292         if( nodebug ) {
293                 tracethreads = 0;
294                 nosync = noerror = 1;
295         }
296         if( nosync )
297                 count = Count_no;
298         if( noerror )
299                 noabort = 1;
300 #if LDAP_THREAD_DEBUG_THREAD_ID +0
301         if( nosync )
302                 threadID = 0;
303 #endif
304 #ifdef LDAP_THREAD_DEBUG_WRAP
305         if( noerror )
306                 nomem = 1;
307         if( !nomem ) {
308                 static const ldap_debug_usage_info_t usage;
309                 if( sizeof(LDAP_UINTPTR_T) < sizeof(unsigned char *)
310                         || sizeof(LDAP_UINTPTR_T) < sizeof(ldap_debug_usage_info_t *)
311                         || UNSCRAMBLE_usagep( SCRAMBLE( &usage ) ) != &usage
312                         || UNSCRAMBLE_dummyp( SCRAMBLE( (unsigned char *) 0 ) ) )
313                 {
314                         fputs( "== thr_debug: Memory checks unsupported, "
315                                 "adding nomem to $LDAP_THREAD_DEBUG ==\n", stderr );
316                         nomem = 1;
317                 }
318         }
319         if( nomem ) {
320                 noreinit = 1;
321                 wraptype = Wrap_noalloc;
322         }
323         unwrap_offset = -(wrap_offset = (wraptype == Wrap_adjptr));
324 #endif
325         options_done = 1;
326 }
327
328
329 #ifndef LDAP_THREAD_DEBUG_WRAP
330
331 #define WRAPPED(ptr)                    (ptr)
332 #define GET_OWNER(ptr)                  0
333 #define SET_OWNER(ptr, thread)  ((void) 0)
334 #define RESET_OWNER(ptr)                ((void) 0)
335 #define ASSERT_OWNER(ptr, msg)  ((void) 0)
336 #define ASSERT_NO_OWNER(ptr, msg) ((void) 0)
337
338 #define init_usage(ptr, msg)    ((void) 0)
339 #define check_usage(ptr, msg)   ((void) 0)
340 #define destroy_usage(ptr)              ((void) 0)
341
342 #else /* LDAP_THREAD_DEBUG_WRAP */
343
344 /* Specialize this if the initializer is not appropriate. */
345 /* The ASSERT_NO_OWNER() definition may also need an override. */
346 #ifndef LDAP_DEBUG_THREAD_NONE
347 #define LDAP_DEBUG_THREAD_NONE { -1 } /* "no thread" ldap_int_thread_t value */
348 #endif
349
350 static const ldap_int_thread_t ldap_debug_thread_none = LDAP_DEBUG_THREAD_NONE;
351
352 #define THREAD_MUTEX_OWNER(mutex) \
353         ldap_int_thread_equal( (mutex)->owner, ldap_int_thread_self() )
354
355 void
356 ldap_debug_thread_assert_mutex_owner(
357         const char *file,
358         int line,
359         const char *msg,
360         ldap_pvt_thread_mutex_t *mutex )
361 {
362         if( !(noerror || THREAD_MUTEX_OWNER( mutex )) ) {
363                 errmsg( file, line, msg, "ASSERT_MUTEX_OWNER", 0 );
364                 if( !noabort ) abort();
365         }
366 }
367
368 #define WRAPPED(ptr)                    (&(ptr)->wrapped)
369 #define GET_OWNER(ptr)                  ((ptr)->owner)
370 #define SET_OWNER(ptr, thread)  ((ptr)->owner = (thread))
371 #define RESET_OWNER(ptr)                ((ptr)->owner = ldap_debug_thread_none)
372 #define ASSERT_OWNER(ptr, msg)  ERROR_IF( !THREAD_MUTEX_OWNER( ptr ), msg )
373 #ifndef ASSERT_NO_OWNER
374 #define ASSERT_NO_OWNER(ptr, msg) ERROR_IF( \
375         !ldap_int_thread_equal( (ptr)->owner, ldap_debug_thread_none ), msg )
376 #endif
377
378 /* Try to provoke memory access error (for malloc debuggers) */
379 #define PEEK(mem) {if (-*(volatile const unsigned char *)(mem)) debug_noop();}
380
381 static void debug_noop( void );
382 static int debug_already_initialized( const ldap_debug_usage_info_t *usage );
383
384 /* Names used to give clearer error messages */
385 #define IS_COPY_OR_MOVED(usage) ((usage)->self != SCRAMBLE( usage ))
386 enum { Is_destroyed = 1 };
387
388 #define DUMMY_ADDR(usage) \
389         (wraptype == Wrap_scramble \
390          ? UNSCRAMBLE_dummyp( (usage)->mem.num ) \
391          : (usage)->mem.ptr + unwrap_offset)
392
393 /* Mark resource as initialized */
394 static void
395 init_usage( ldap_debug_usage_info_t *usage, const char *msg )
396 {
397         if( !options_done )
398                 get_options();
399         if( !nomem ) {
400                 if( !noreinit ) {
401                         MEMERROR_IF( debug_already_initialized( usage ), msg, {
402                                 /* Provoke malloc debuggers */
403                                 unsigned char *dummy = DUMMY_ADDR( usage );
404                                 PEEK( dummy );
405                                 free( dummy );
406                                 free( dummy );
407                         } );
408                 }
409                 usage->self = SCRAMBLE( usage );
410                 if( wraptype != Wrap_noalloc ) {
411                         unsigned char *dummy = malloc( 1 );
412                         assert( dummy != NULL );
413                         if( wraptype == Wrap_scramble ) {
414                                 usage->mem.num = SCRAMBLE( dummy );
415                                 assert( UNSCRAMBLE_dummyp( usage->mem.num ) == dummy );
416                         } else {
417                                 usage->mem.ptr = dummy + wrap_offset;
418                         }
419                 }
420         }
421         usage->magic = ldap_debug_magic;
422         usage->state = ldap_debug_state_inited;
423 }
424
425 /* Check that resource is initialized and not copied/realloced */
426 static void
427 check_usage( const ldap_debug_usage_info_t *usage, const char *msg )
428 {
429         if( usage->magic != ldap_debug_magic ) {
430                 ERROR( usage->magic, msg );
431                 return;
432         }
433         switch( usage->state ) {
434         case ldap_debug_state_destroyed:
435                 MEMERROR_IF( Is_destroyed, msg, {
436                         PEEK( DUMMY_ADDR( usage ) );
437                 } );
438                 break;
439         default:
440                 ERROR( usage->state, msg );
441                 break;
442         case ldap_debug_state_inited:
443                 if( !nomem ) {
444                         MEMERROR_IF( IS_COPY_OR_MOVED( usage ), msg, {
445                                 PEEK( DUMMY_ADDR( usage ) );
446                                 PEEK( UNSCRAMBLE_usagep( usage->self ) );
447                         } );
448                 }
449                 break;
450         }
451 }
452
453 /* Mark resource as destroyed. */
454 /* Does not check for errors, call check_usage()/init_usage() first. */
455 static void
456 destroy_usage( ldap_debug_usage_info_t *usage )
457 {
458         if( usage->state == ldap_debug_state_inited ) {
459                 if( wraptype != Wrap_noalloc ) {
460                         free( DUMMY_ADDR( usage ) );
461                         /* Do not reset the DUMMY_ADDR, leave it for malloc debuggers
462                          * in case the resource is used after it is freed. */
463                 }
464                 usage->state = ldap_debug_state_destroyed;
465         }
466 }
467
468 /* Define these after they are used, so they are hopefully not inlined */
469
470 static void
471 debug_noop( void )
472 {
473 }
474
475 /* Return true if the resource is initialized and not copied/realloced. */
476 /* Valid programs access uninitialized memory here unless "noreinit".   */
477 static int
478 debug_already_initialized( const ldap_debug_usage_info_t *usage )
479 {
480         return (usage->state == ldap_debug_state_inited &&
481                 !IS_COPY_OR_MOVED( usage ) &&
482                 usage->magic == ldap_debug_magic);
483 }
484
485 #endif /* LDAP_THREAD_DEBUG_WRAP */
486
487
488 #if !(LDAP_THREAD_DEBUG_THREAD_ID +0)
489
490 typedef int ldap_debug_thread_t;
491 #define init_thread_info()      {}
492 #define with_thread_info_lock(statements) { statements; }
493 #define thread_info_detached(t) 0
494 #define add_thread_info(msg, thr, det)  ((void) 0)
495 #define remove_thread_info(tinfo, msg)  ((void) 0)
496 #define get_thread_info(thread, msg)    NULL
497
498 #else /* LDAP_THREAD_DEBUG_THREAD_ID */
499
500 /*
501  * Thread ID tracking.  Currently acieves little.
502  * Should be either expanded or deleted.
503  */
504
505 /*
506  * Array of threads.  Used instead of making ldap_pvt_thread_t a wrapper
507  * around ldap_int_thread_t, which would slow down ldap_pvt_thread_self().
508  */
509 typedef struct {
510         ldap_pvt_thread_t           wrapped;
511         ldap_debug_usage_info_t     usage;
512         int                         detached;
513         int                         idx;
514 } ldap_debug_thread_t;
515
516 static ldap_debug_thread_t      **thread_info;
517 static unsigned int             thread_info_size, thread_info_used;
518 static ldap_int_thread_mutex_t  thread_info_mutex;
519
520 #define init_thread_info() { \
521         if( threadID ) { \
522                 int mutex_init_rc = ldap_int_thread_mutex_init( &thread_info_mutex ); \
523                 assert( mutex_init_rc == 0 ); \
524         } \
525 }
526
527 #define with_thread_info_lock(statements) { \
528         int rc_wtl_ = ldap_int_thread_mutex_lock( &thread_info_mutex ); \
529         assert( rc_wtl_ == 0 ); \
530         { statements; } \
531         rc_wtl_ = ldap_int_thread_mutex_unlock( &thread_info_mutex ); \
532         assert( rc_wtl_ == 0 ); \
533 }
534
535 #define thread_info_detached(t) ((t)->detached)
536
537 static void
538 add_thread_info(
539         const char *msg,
540         const ldap_pvt_thread_t *thread,
541         int detached )
542 {
543         ldap_debug_thread_t *t;
544         if( thread_info_used >= thread_info_size ) {
545                 unsigned int more = thread_info_size + 8;
546                 unsigned int new_size = thread_info_size + more;
547                 t = calloc( more, sizeof(ldap_debug_thread_t) );
548                 assert( t != NULL );
549                 thread_info = realloc( thread_info, new_size * sizeof(*thread_info) );
550                 assert( thread_info != NULL );
551                 while( thread_info_size < new_size ) {
552                         t->idx = thread_info_size;
553                         thread_info[thread_info_size++] = t++;
554                 }
555         }
556         t = thread_info[thread_info_used];
557         init_usage( &t->usage, msg );
558         t->wrapped = *thread;
559         t->detached = detached;
560         thread_info_used++;
561 }
562
563 static void
564 remove_thread_info( ldap_debug_thread_t *t, const char *msg )
565 {
566                 ldap_debug_thread_t *last;
567                 int idx;
568                 check_usage( &t->usage, msg );
569                 destroy_usage( &t->usage );
570                 idx = t->idx;
571                 assert( thread_info[idx] == t );
572                 last = thread_info[--thread_info_used];
573                 assert( last->idx == thread_info_used );
574                 (thread_info[idx]              = last)->idx = idx;
575                 (thread_info[thread_info_used] = t   )->idx = thread_info_used;
576 }
577
578 ldap_debug_thread_t *
579 get_thread_info( ldap_pvt_thread_t *thread, const char *msg )
580 {
581         unsigned int i;
582         ldap_debug_thread_t *t;
583         for( i = 0; i < thread_info_used; i++ ) {
584                 if( ldap_pvt_thread_equal( *thread, thread_info[i]->wrapped ) )
585                         break;
586         }
587         ERROR_IF( i == thread_info_used, msg );
588         t = thread_info[i];
589         check_usage( &t->usage, msg );
590         return t;
591 }
592
593 #endif /* LDAP_THREAD_DEBUG_THREAD_ID */
594
595
596 static char *
597 thread_name( char *buf, int bufsize, ldap_pvt_thread_t thread )
598 {
599         int i;
600         --bufsize;
601         if( bufsize > 2*sizeof(thread) )
602                 bufsize = 2*sizeof(thread);
603         for( i = 0; i < bufsize; i += 2 )
604                 snprintf( buf+i, 3, "%02x", ((unsigned char *)&thread)[i/2] );
605         return buf;
606 }
607
608
609 /* Add <adjust> (+/-1) to resource count <which> unless "nocount". */
610 static void
611 adjust_count( int which, int adjust )
612 {
613         int rc;
614         switch( count ) {
615         case Count_no:
616                 break;
617         case Count_yes:
618                 rc = ldap_int_thread_mutex_lock( &resource_mutexes[which] );
619                 assert( rc == 0 );
620                 resource_counts[which] += adjust;
621                 rc = ldap_int_thread_mutex_unlock( &resource_mutexes[which] );
622                 assert( rc == 0 );
623         case Count_reported:
624                 fputs( "== thr_debug: More thread activity after exit ==\n", stderr );
625                 count = Count_reported_more;
626                 /* FALL THROUGH */
627         case Count_reported_more:
628                 /* Not used, but result might be inspected with debugger */
629                 /* (Hopefully threading is disabled by now...) */
630                 resource_counts[which] += adjust;
631                 break;
632         }
633 }
634
635
636 /* Wrappers for LDAP_THREAD_IMPLEMENTATION: */
637
638 /* Used instead of ldap_int_thread_initialize by ldap_pvt_thread_initialize */
639 int
640 ldap_debug_thread_initialize( void )
641 {
642         int i, rc, rc2;
643         if( !options_done )
644                 get_options();
645         ERROR_IF( threading_enabled, "ldap_debug_thread_initialize" );
646         threading_enabled = 1;
647         rc = ldap_int_thread_initialize();
648         if( rc ) {
649                 ERROR( rc, "ldap_debug_thread_initialize:threads" );
650                 threading_enabled = 0;
651         } else {
652                 init_thread_info();
653                 if( count != Count_no ) {
654                         for( i = rc2 = 0; i < Idx_max; i++ )
655                                 rc2 |= ldap_int_thread_mutex_init( &resource_mutexes[i] );
656                         assert( rc2 == 0 );
657                         /* FIXME: Only for static libldap_r as in init.c? If so, why? */
658                         atexit( count_resource_leaks );
659                 }
660         }
661         return rc;
662 }
663
664 /* Used instead of ldap_int_thread_destroy by ldap_pvt_thread_destroy */
665 int
666 ldap_debug_thread_destroy( void )
667 {
668         int rc;
669         ERROR_IF( !threading_enabled, "ldap_debug_thread_destroy" );
670         /* sleep(1) -- need to wait for thread pool to finish? */
671         rc = ldap_int_thread_destroy();
672         if( rc ) {
673                 ERROR( rc, "ldap_debug_thread_destroy:threads" );
674         } else {
675                 threading_enabled = 0;
676         }
677         return rc;
678 }
679
680 int
681 ldap_pvt_thread_set_concurrency( int n )
682 {
683         int rc;
684         ERROR_IF( !threading_enabled, "ldap_pvt_thread_set_concurrency" );
685         rc = ldap_int_thread_set_concurrency( n );
686         ERROR_IF( rc, "ldap_pvt_thread_set_concurrency" );
687         return rc;
688 }
689
690 int
691 ldap_pvt_thread_get_concurrency( void )
692 {
693         int rc;
694         ERROR_IF( !threading_enabled, "ldap_pvt_thread_get_concurrency" );
695         rc = ldap_int_thread_get_concurrency();
696         ERROR_IF( rc, "ldap_pvt_thread_get_concurrency" );
697         return rc;
698 }
699
700 unsigned int
701 ldap_pvt_thread_sleep( unsigned int interval )
702 {
703         int rc;
704         ERROR_IF( !threading_enabled, "ldap_pvt_thread_sleep" );
705         rc = ldap_int_thread_sleep( interval );
706         ERROR_IF( rc, "ldap_pvt_thread_sleep" );
707         return 0;
708 }
709
710 int
711 ldap_pvt_thread_create(
712         ldap_pvt_thread_t *thread,
713         int detach,
714         void *(*start_routine)( void * ),
715         void *arg )
716 {
717         int rc;
718         if( !options_done )
719                 get_options();
720         ERROR_IF( !threading_enabled, "ldap_pvt_thread_create" );
721         if( threadID ) {
722                 with_thread_info_lock({
723                         rc = ldap_int_thread_create( thread, detach, start_routine, arg );
724                         if( rc == 0 )
725                                 add_thread_info( "ldap_pvt_thread_create", thread, detach );
726                 });
727         } else {
728                 rc = ldap_int_thread_create( thread, detach, start_routine, arg );
729         }
730         if( rc ) {
731                 ERROR( rc, "ldap_pvt_thread_create" );
732         } else {
733                 if( tracethreads ) {
734                         char buf[40], buf2[40];
735                         fprintf( stderr,
736                                 "== thr_debug: Created thread %s%s from thread %s ==\n",
737                                 thread_name( buf, sizeof(buf), *thread ),
738                                 detach ? " (detached)" : "",
739                                 thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) );
740                 }
741                 adjust_count( Idx_unexited_thread, +1 );
742                 if( !detach )
743                         adjust_count( Idx_unjoined_thread, +1 );
744         }
745         return rc;
746 }
747
748 void
749 ldap_pvt_thread_exit( void *retval )
750 {
751         ldap_pvt_thread_t thread;
752         ERROR_IF( !threading_enabled, "ldap_pvt_thread_exit" );
753         thread = ldap_pvt_thread_self();
754         if( tracethreads ) {
755                 char buf[40];
756                 fprintf( stderr, "== thr_debug: Exiting thread %s ==\n",
757                         thread_name( buf, sizeof(buf), thread ) );
758         }
759         if( threadID ) {
760                 with_thread_info_lock({
761                         ldap_debug_thread_t *t = get_thread_info(
762                                 &thread, "ldap_pvt_thread_exit" );
763                         if( thread_info_detached( t ) )
764                                 remove_thread_info( t, "ldap_pvt_thread_exit" );
765                 });
766         }
767         adjust_count( Idx_unexited_thread, -1 );
768         ldap_int_thread_exit( retval );
769 }
770
771 int
772 ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
773 {
774         int rc;
775         ldap_debug_thread_t *t = NULL;
776         ERROR_IF( !threading_enabled, "ldap_pvt_thread_join" );
777         if( tracethreads ) {
778                 char buf[40], buf2[40];
779                 fprintf( stderr, "== thr_debug: Joining thread %s in thread %s ==\n",
780                         thread_name( buf, sizeof(buf), thread ),
781                         thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) );
782         }
783         if( threadID )
784                 with_thread_info_lock( {
785                         t = get_thread_info( &thread, "ldap_pvt_thread_join" );
786                         ERROR_IF( thread_info_detached( t ), "ldap_pvt_thread_join" );
787                 } );
788         rc = ldap_int_thread_join( thread, thread_return );
789         if( rc ) {
790                 ERROR( rc, "ldap_pvt_thread_join" );
791         } else {
792                 if( threadID )
793                         with_thread_info_lock(
794                                 remove_thread_info( t, "ldap_pvt_thread_join" ) );
795                 adjust_count( Idx_unjoined_thread, -1 );
796         }
797         return rc;
798 }
799
800 int
801 ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
802 {
803         int rc;
804         ERROR_IF( !threading_enabled, "ldap_pvt_thread_kill" );
805         if( tracethreads ) {
806                 char buf[40], buf2[40];
807                 fprintf( stderr,
808                         "== thr_debug: Killing thread %s (sig %i) from thread %s ==\n",
809                         thread_name( buf, sizeof(buf), thread ), signo,
810                         thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) );
811         }
812         rc = ldap_int_thread_kill( thread, signo );
813         ERROR_IF( rc, "ldap_pvt_thread_kill" );
814         return rc;
815 }
816
817 int
818 ldap_pvt_thread_yield( void )
819 {
820         int rc;
821         ERROR_IF( !threading_enabled, "ldap_pvt_thread_yield" );
822         rc = ldap_int_thread_yield();
823         ERROR_IF( rc, "ldap_pvt_thread_yield" );
824         return rc;
825 }
826
827 ldap_pvt_thread_t
828 ldap_pvt_thread_self( void )
829 {
830 #if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */
831         ERROR_IF( !threading_enabled, "ldap_pvt_thread_self" );
832 #endif
833         return ldap_int_thread_self();
834 }
835
836 int
837 ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
838 {
839         int rc;
840         init_usage( &cond->usage, "ldap_pvt_thread_cond_init" );
841         rc = ldap_int_thread_cond_init( WRAPPED( cond ) );
842         if( rc ) {
843                 ERROR( rc, "ldap_pvt_thread_cond_init" );
844                 destroy_usage( &cond->usage );
845         } else {
846                 adjust_count( Idx_cond, +1 );
847         }
848         return rc;
849 }
850
851 int
852 ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cond )
853 {
854         int rc;
855         check_usage( &cond->usage, "ldap_pvt_thread_cond_destroy" );
856         rc = ldap_int_thread_cond_destroy( WRAPPED( cond ) );
857         if( rc ) {
858                 ERROR( rc, "ldap_pvt_thread_cond_destroy" );
859         } else {
860                 destroy_usage( &cond->usage );
861                 adjust_count( Idx_cond, -1 );
862         }
863         return rc;
864 }
865
866 int
867 ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
868 {
869         int rc;
870         check_usage( &cond->usage, "ldap_pvt_thread_cond_signal" );
871         rc = ldap_int_thread_cond_signal( WRAPPED( cond ) );
872         ERROR_IF( rc, "ldap_pvt_thread_cond_signal" );
873         return rc;
874 }
875
876 int
877 ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond )
878 {
879         int rc;
880         check_usage( &cond->usage, "ldap_pvt_thread_cond_broadcast" );
881         rc = ldap_int_thread_cond_broadcast( WRAPPED( cond ) );
882         ERROR_IF( rc, "ldap_pvt_thread_cond_broadcast" );
883         return rc;
884 }
885
886 int
887 ldap_pvt_thread_cond_wait(
888         ldap_pvt_thread_cond_t *cond,
889         ldap_pvt_thread_mutex_t *mutex )
890 {
891         int rc;
892         ldap_int_thread_t owner;
893         check_usage( &cond->usage, "ldap_pvt_thread_cond_wait:cond" );
894         check_usage( &mutex->usage, "ldap_pvt_thread_cond_wait:mutex" );
895         adjust_count( Idx_locked_mutex, -1 );
896         owner = GET_OWNER( mutex );
897         ASSERT_OWNER( mutex, "ldap_pvt_thread_cond_wait" );
898         RESET_OWNER( mutex );
899         rc = ldap_int_thread_cond_wait( WRAPPED( cond ), WRAPPED( mutex ) );
900         ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_cond_wait" );
901         SET_OWNER( mutex, rc ? owner : ldap_int_thread_self() );
902         adjust_count( Idx_locked_mutex, +1 );
903         ERROR_IF( rc, "ldap_pvt_thread_cond_wait" );
904         return rc;
905 }
906
907 int
908 ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
909 {
910         int rc;
911         init_usage( &mutex->usage, "ldap_pvt_thread_mutex_init" );
912         rc = ldap_int_thread_mutex_init( WRAPPED( mutex ) );
913         if( rc ) {
914                 ERROR( rc, "ldap_pvt_thread_mutex_init" );
915                 destroy_usage( &mutex->usage );
916         } else {
917                 RESET_OWNER( mutex );
918                 adjust_count( Idx_mutex, +1 );
919         }
920         return rc;
921 }
922
923 int
924 ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
925 {
926         int rc;
927         check_usage( &mutex->usage, "ldap_pvt_thread_mutex_destroy" );
928         ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_destroy" );
929         rc = ldap_int_thread_mutex_destroy( WRAPPED( mutex ) );
930         if( rc ) {
931                 ERROR( rc, "ldap_pvt_thread_mutex_destroy" );
932         } else {
933                 destroy_usage( &mutex->usage );
934                 RESET_OWNER( mutex );
935                 adjust_count( Idx_mutex, -1 );
936         }
937         return rc;
938 }
939
940 int
941 ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
942 {
943         int rc;
944         check_usage( &mutex->usage, "ldap_pvt_thread_mutex_lock" );
945         rc = ldap_int_thread_mutex_lock( WRAPPED( mutex ) );
946         if( rc ) {
947                 ERROR_IF( rc, "ldap_pvt_thread_mutex_lock" );
948         } else {
949                 ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_lock" );
950                 SET_OWNER( mutex, ldap_int_thread_self() );
951                 adjust_count( Idx_locked_mutex, +1 );
952         }
953         return rc;
954 }
955
956 int
957 ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex )
958 {
959         int rc;
960         check_usage( &mutex->usage, "ldap_pvt_thread_mutex_trylock" );
961         rc = ldap_int_thread_mutex_trylock( WRAPPED( mutex ) );
962         if( rc == 0 ) {
963                 ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_trylock" );
964                 SET_OWNER( mutex, ldap_int_thread_self() );
965                 adjust_count( Idx_locked_mutex, +1 );
966         }
967         return rc;
968 }
969
970 int
971 ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
972 {
973         int rc;
974         check_usage( &mutex->usage, "ldap_pvt_thread_mutex_unlock" );
975         ASSERT_OWNER( mutex, "ldap_pvt_thread_mutex_unlock" );
976         RESET_OWNER( mutex ); /* Breaks if this thread did not own the mutex */
977         rc = ldap_int_thread_mutex_unlock( WRAPPED( mutex ) );
978         if( rc ) {
979                 ERROR_IF( rc, "ldap_pvt_thread_mutex_unlock" );
980         } else {
981                 adjust_count( Idx_locked_mutex, -1 );
982         }
983         return rc;
984 }
985
986
987 /* Wrappers for LDAP_THREAD_RDWR_IMPLEMENTATION: */
988
989 int
990 ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rwlock )
991 {
992         int rc;
993         init_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_init" );
994         rc = ldap_int_thread_rdwr_init( WRAPPED( rwlock ) );
995         if( rc ) {
996                 ERROR( rc, "ldap_pvt_thread_rdwr_init" );
997                 destroy_usage( &rwlock->usage );
998         } else {
999                 adjust_count( Idx_rdwr, +1 );
1000         }
1001         return rc;
1002 }
1003
1004 int
1005 ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rwlock )
1006 {
1007         int rc;
1008         check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_destroy" );
1009         rc = ldap_int_thread_rdwr_destroy( WRAPPED( rwlock ) );
1010         if( rc ) {
1011                 ERROR( rc, "ldap_pvt_thread_rdwr_destroy" );
1012         } else {
1013                 destroy_usage( &rwlock->usage );
1014                 adjust_count( Idx_rdwr, -1 );
1015         }
1016         return rc;
1017 }
1018
1019 int
1020 ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rwlock )
1021 {
1022         int rc;
1023         check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_rlock" );
1024         rc = ldap_int_thread_rdwr_rlock( WRAPPED( rwlock ) );
1025         ERROR_IF( rc, "ldap_pvt_thread_rdwr_rlock" );
1026         return rc;
1027 }
1028
1029 int
1030 ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rwlock )
1031 {
1032         check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_rtrylock" );
1033         return ldap_int_thread_rdwr_rtrylock( WRAPPED( rwlock ) );
1034 }
1035
1036 int
1037 ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rwlock )
1038 {
1039         int rc;
1040         check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_runlock" );
1041         rc = ldap_int_thread_rdwr_runlock( WRAPPED( rwlock ) );
1042         ERROR_IF( rc, "ldap_pvt_thread_rdwr_runlock" );
1043         return rc;
1044 }
1045
1046 int
1047 ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rwlock )
1048 {
1049         int rc;
1050         check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wlock" );
1051         rc = ldap_int_thread_rdwr_wlock( WRAPPED( rwlock ) );
1052         ERROR_IF( rc, "ldap_pvt_thread_rdwr_wlock" );
1053         return rc;
1054 }
1055
1056 int
1057 ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rwlock )
1058 {
1059         check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wtrylock" );
1060         return ldap_int_thread_rdwr_wtrylock( WRAPPED( rwlock ) );
1061 }
1062
1063 int
1064 ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rwlock )
1065 {
1066         int rc;
1067         check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wunlock" );
1068         rc = ldap_int_thread_rdwr_wunlock( WRAPPED( rwlock ) );
1069         ERROR_IF( rc, "ldap_pvt_thread_rdwr_wunlock" );
1070         return rc;
1071 }
1072
1073 #if defined(LDAP_RDWR_DEBUG) && !defined(LDAP_THREAD_HAVE_RDWR)
1074
1075 int
1076 ldap_pvt_thread_rdwr_readers( ldap_pvt_thread_rdwr_t *rwlock )
1077 {
1078         check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_readers" );
1079         return ldap_int_thread_rdwr_readers( WRAPPED( rwlock ) );
1080 }
1081
1082 int
1083 ldap_pvt_thread_rdwr_writers( ldap_pvt_thread_rdwr_t *rwlock )
1084 {
1085         check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_writers" );
1086         return ldap_int_thread_rdwr_writers( WRAPPED( rwlock ) );
1087 }
1088
1089 int
1090 ldap_pvt_thread_rdwr_active( ldap_pvt_thread_rdwr_t *rwlock )
1091 {
1092         check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_active" );
1093         return ldap_int_thread_rdwr_active( WRAPPED( rwlock ) );
1094 }
1095
1096 #endif /* LDAP_RDWR_DEBUG && !LDAP_THREAD_HAVE_RDWR */
1097
1098
1099 /* Some wrappers for LDAP_THREAD_POOL_IMPLEMENTATION: */
1100 #ifdef LDAP_THREAD_POOL_IMPLEMENTATION
1101
1102 int
1103 ldap_pvt_thread_pool_init(
1104         ldap_pvt_thread_pool_t *tpool,
1105         int max_threads,
1106         int max_pending )
1107 {
1108         int rc;
1109         if( !options_done )
1110                 get_options();
1111         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_init" );
1112         rc = ldap_int_thread_pool_init( tpool, max_threads, max_pending );
1113         if( rc ) {
1114                 ERROR( rc, "ldap_pvt_thread_pool_init" );
1115         } else {
1116                 adjust_count( Idx_tpool, +1 );
1117         }
1118         return rc;
1119 }
1120
1121 int
1122 ldap_pvt_thread_pool_submit(
1123         ldap_pvt_thread_pool_t *tpool,
1124         ldap_pvt_thread_start_t *start_routine, void *arg )
1125 {
1126         int rc, has_pool;
1127         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_submit" );
1128         has_pool = (tpool != NULL && *tpool != NULL);
1129         rc = ldap_int_thread_pool_submit( tpool, start_routine, arg );
1130         if( has_pool )
1131                 ERROR_IF( rc, "ldap_pvt_thread_pool_submit" );
1132         return rc;
1133 }
1134
1135 int
1136 ldap_pvt_thread_pool_maxthreads(
1137         ldap_pvt_thread_pool_t *tpool,
1138         int max_threads )
1139 {
1140         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_maxthreads" );
1141         return ldap_int_thread_pool_maxthreads( tpool, max_threads );
1142 }
1143
1144 int
1145 ldap_pvt_thread_pool_backload( ldap_pvt_thread_pool_t *tpool )
1146 {
1147         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_backload" );
1148         return ldap_int_thread_pool_backload( tpool );
1149 }
1150
1151 int
1152 ldap_pvt_thread_pool_destroy( ldap_pvt_thread_pool_t *tpool, int run_pending )
1153 {
1154         int rc, has_pool;
1155         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_destroy" );
1156         has_pool = (tpool != NULL && *tpool != NULL);
1157         rc = ldap_int_thread_pool_destroy( tpool, run_pending );
1158         if( has_pool ) {
1159                 if( rc ) {
1160                         ERROR( rc, "ldap_pvt_thread_pool_destroy" );
1161                 } else {
1162                         adjust_count( Idx_tpool, -1 );
1163                 }
1164         }
1165         return rc;
1166 }
1167
1168 int
1169 ldap_pvt_thread_pool_pause( ldap_pvt_thread_pool_t *tpool )
1170 {
1171         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_pause" );
1172         return ldap_int_thread_pool_pause( tpool );
1173 }
1174
1175 int
1176 ldap_pvt_thread_pool_resume( ldap_pvt_thread_pool_t *tpool )
1177 {
1178         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_resume" );
1179         return ldap_int_thread_pool_resume( tpool );
1180 }
1181
1182 int
1183 ldap_pvt_thread_pool_getkey(
1184         void *xctx,
1185         void *key,
1186         void **data,
1187         ldap_pvt_thread_pool_keyfree_t **kfree )
1188 {
1189 #if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */
1190         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_getkey" );
1191 #endif
1192         return ldap_int_thread_pool_getkey( xctx, key, data, kfree );
1193 }
1194
1195 int
1196 ldap_pvt_thread_pool_setkey(
1197         void *xctx,
1198         void *key,
1199         void *data,
1200         ldap_pvt_thread_pool_keyfree_t *kfree )
1201 {
1202         int rc;
1203         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_setkey" );
1204         rc = ldap_int_thread_pool_setkey( xctx, key, data, kfree );
1205         ERROR_IF( rc, "ldap_pvt_thread_pool_setkey" );
1206         return rc;
1207 }
1208
1209 void
1210 ldap_pvt_thread_pool_purgekey( void *key )
1211 {
1212         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_purgekey" );
1213         ldap_int_thread_pool_purgekey( key );
1214 }
1215
1216 void *
1217 ldap_pvt_thread_pool_context( void )
1218 {
1219 #if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */
1220         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_context" );
1221 #endif
1222         return ldap_int_thread_pool_context();
1223 }
1224
1225 void
1226 ldap_pvt_thread_pool_context_reset( void *vctx )
1227 {
1228         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_context_reset" );
1229         ldap_int_thread_pool_context_reset( vctx );
1230 }
1231
1232 #endif /* LDAP_THREAD_POOL_IMPLEMENTATION */
1233
1234 #endif /* LDAP_THREAD_DEBUG */