]> git.sur5r.net Git - openldap/blob - libraries/libldap_r/thr_debug.c
c5fde91ca7f74375efdc45a17765f919c5cbc999
[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-2017 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 synchronization 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 wrap_threads;
145 static int options_done;
146
147
148 /* ldap_pvt_thread_initialize() called, ldap_pvt_thread_destroy() not called */
149 static int threading_enabled;
150
151
152 /* Resource counts */
153 enum {
154         Idx_unexited_thread, Idx_unjoined_thread, Idx_locked_mutex,
155         Idx_mutex, Idx_cond, Idx_rdwr, Idx_tpool, Idx_max
156 };
157 static int resource_counts[Idx_max];
158 static const char *const resource_names[] = {
159         "unexited threads", "unjoined threads", "locked mutexes",
160         "mutexes", "conds", "rdwrs", "thread pools"
161 };
162 static 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
205 static void
206 warn( 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
216 static void
217 errmsg( 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
226 static void
227 count_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
247 static void
248 get_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
349 static 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
354 void
355 ldap_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
380 static void debug_noop( void );
381 static 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 */
392 static void
393 init_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 */
428 static void
429 check_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. */
459 static void
460 destroy_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
474 static void
475 debug_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  */
484 LDAP_GCCATTR((noinline))
485 static int
486 debug_already_initialized( const ldap_debug_usage_info_t *usage )
487 {
488         /*
489          * 'ret' keeps the Valgrind warning "Conditional jump or move
490          * depends on uninitialised value(s)" _inside_ this function.
491          */
492         volatile int ret = 0;
493         if( usage->state == ldap_debug_state_inited )
494                 if( !IS_COPY_OR_MOVED( usage ) )
495                 if( usage->magic == ldap_debug_magic )
496                                 ret = 1;
497         return ret;
498 }
499
500 #endif /* LDAP_THREAD_DEBUG_WRAP */
501
502
503 #if !(LDAP_THREAD_DEBUG_THREAD_ID +0)
504
505 typedef void ldap_debug_thread_t;
506 #define init_thread_info()      {}
507 #define with_thread_info_lock(statements) { statements; }
508 #define thread_info_detached(t) 0
509 #define add_thread_info(msg, thr, det)  ((void) 0)
510 #define remove_thread_info(tinfo, msg)  ((void) 0)
511 #define get_thread_info(thread, msg)    NULL
512
513 #else /* LDAP_THREAD_DEBUG_THREAD_ID */
514
515 /*
516  * Thread ID tracking.  Currently achieves little.
517  * Should be either expanded or deleted.
518  */
519
520 /*
521  * Array of threads.  Used instead of making ldap_pvt_thread_t a wrapper
522  * around ldap_int_thread_t, which would slow down ldap_pvt_thread_self().
523  */
524 typedef struct {
525         ldap_pvt_thread_t           wrapped;
526         ldap_debug_usage_info_t     usage;
527         int                         detached;
528         int                         idx;
529 } ldap_debug_thread_t;
530
531 static ldap_debug_thread_t      **thread_info;
532 static unsigned int             thread_info_size, thread_info_used;
533 static ldap_int_thread_mutex_t  thread_info_mutex;
534
535 #define init_thread_info() { \
536         if( threadID ) { \
537                 int mutex_init_rc = ldap_int_thread_mutex_init( &thread_info_mutex ); \
538                 assert( mutex_init_rc == 0 ); \
539         } \
540 }
541
542 #define with_thread_info_lock(statements) { \
543         int rc_wtl_ = ldap_int_thread_mutex_lock( &thread_info_mutex ); \
544         assert( rc_wtl_ == 0 ); \
545         { statements; } \
546         rc_wtl_ = ldap_int_thread_mutex_unlock( &thread_info_mutex ); \
547         assert( rc_wtl_ == 0 ); \
548 }
549
550 #define thread_info_detached(t) ((t)->detached)
551
552 static void
553 add_thread_info(
554         const char *msg,
555         const ldap_pvt_thread_t *thread,
556         int detached )
557 {
558         ldap_debug_thread_t *t;
559
560         if( thread_info_used >= thread_info_size ) {
561                 unsigned int more = thread_info_size + 8;
562                 unsigned int new_size = thread_info_size + more;
563
564                 t = calloc( more, sizeof(ldap_debug_thread_t) );
565                 assert( t != NULL );
566                 thread_info = realloc( thread_info, new_size * sizeof(*thread_info) );
567                 assert( thread_info != NULL );
568                 do {
569                         t->idx = thread_info_size;
570                         thread_info[thread_info_size++] = t++;
571                 } while( thread_info_size < new_size );
572         }
573
574         t = thread_info[thread_info_used];
575         init_usage( &t->usage, msg );
576         t->wrapped = *thread;
577         t->detached = detached;
578         thread_info_used++;
579 }
580
581 static void
582 remove_thread_info( ldap_debug_thread_t *t, const char *msg )
583 {
584                 ldap_debug_thread_t *last;
585                 int idx;
586                 check_usage( &t->usage, msg );
587                 destroy_usage( &t->usage );
588                 idx = t->idx;
589                 assert( thread_info[idx] == t );
590                 last = thread_info[--thread_info_used];
591                 assert( last->idx == thread_info_used );
592                 (thread_info[idx]              = last)->idx = idx;
593                 (thread_info[thread_info_used] = t   )->idx = thread_info_used;
594 }
595
596 static ldap_debug_thread_t *
597 get_thread_info( ldap_pvt_thread_t thread, const char *msg )
598 {
599         unsigned int i;
600         ldap_debug_thread_t *t;
601         for( i = 0; i < thread_info_used; i++ ) {
602                 if( ldap_pvt_thread_equal( thread, thread_info[i]->wrapped ) )
603                         break;
604         }
605         ERROR_IF( i == thread_info_used, msg );
606         t = thread_info[i];
607         check_usage( &t->usage, msg );
608         return t;
609 }
610
611 #endif /* LDAP_THREAD_DEBUG_THREAD_ID */
612
613
614 static char *
615 thread_name( char *buf, int bufsize, ldap_pvt_thread_t thread )
616 {
617         int i;
618         --bufsize;
619         if( bufsize > 2*sizeof(thread) )
620                 bufsize = 2*sizeof(thread);
621         for( i = 0; i < bufsize; i += 2 )
622                 snprintf( buf+i, 3, "%02x", ((unsigned char *)&thread)[i/2] );
623         return buf;
624 }
625
626
627 /* Add <adjust> (+/-1) to resource count <which> unless "nocount". */
628 static void
629 adjust_count( int which, int adjust )
630 {
631         int rc;
632         switch( count ) {
633         case Count_no:
634                 break;
635         case Count_yes:
636                 rc = ldap_int_thread_mutex_lock( &resource_mutexes[which] );
637                 assert( rc == 0 );
638                 resource_counts[which] += adjust;
639                 rc = ldap_int_thread_mutex_unlock( &resource_mutexes[which] );
640                 assert( rc == 0 );
641                 break;
642         case Count_reported:
643                 fputs( "== thr_debug: More thread activity after exit ==\n", stderr );
644                 count = Count_reported_more;
645                 /* FALL THROUGH */
646         case Count_reported_more:
647                 /* Not used, but result might be inspected with debugger */
648                 /* (Hopefully threading is disabled by now...) */
649                 resource_counts[which] += adjust;
650                 break;
651         }
652 }
653
654
655 /* Wrappers for LDAP_THREAD_IMPLEMENTATION: */
656
657 /* Used instead of ldap_int_thread_initialize by ldap_pvt_thread_initialize */
658 int
659 ldap_debug_thread_initialize( void )
660 {
661         int i, rc, rc2;
662         if( !options_done )
663                 get_options();
664         ERROR_IF( threading_enabled, "ldap_debug_thread_initialize" );
665         threading_enabled = 1;
666         rc = ldap_int_thread_initialize();
667         if( rc ) {
668                 ERROR( rc, "ldap_debug_thread_initialize:threads" );
669                 threading_enabled = 0;
670         } else {
671                 init_thread_info();
672                 if( count != Count_no ) {
673                         for( i = rc2 = 0; i < Idx_max; i++ )
674                                 rc2 |= ldap_int_thread_mutex_init( &resource_mutexes[i] );
675                         assert( rc2 == 0 );
676                         /* FIXME: Only for static libldap_r as in init.c? If so, why? */
677                         atexit( count_resource_leaks );
678                 }
679         }
680         return rc;
681 }
682
683 /* Used instead of ldap_int_thread_destroy by ldap_pvt_thread_destroy */
684 int
685 ldap_debug_thread_destroy( void )
686 {
687         int rc;
688         ERROR_IF( !threading_enabled, "ldap_debug_thread_destroy" );
689         /* sleep(1) -- need to wait for thread pool to finish? */
690         rc = ldap_int_thread_destroy();
691         if( rc ) {
692                 ERROR( rc, "ldap_debug_thread_destroy:threads" );
693         } else {
694                 threading_enabled = 0;
695         }
696         return rc;
697 }
698
699 int
700 ldap_pvt_thread_set_concurrency( int n )
701 {
702         int rc;
703         ERROR_IF( !threading_enabled, "ldap_pvt_thread_set_concurrency" );
704         rc = ldap_int_thread_set_concurrency( n );
705         ERROR_IF( rc, "ldap_pvt_thread_set_concurrency" );
706         return rc;
707 }
708
709 int
710 ldap_pvt_thread_get_concurrency( void )
711 {
712         int rc;
713         ERROR_IF( !threading_enabled, "ldap_pvt_thread_get_concurrency" );
714         rc = ldap_int_thread_get_concurrency();
715         ERROR_IF( rc, "ldap_pvt_thread_get_concurrency" );
716         return rc;
717 }
718
719 unsigned int
720 ldap_pvt_thread_sleep( unsigned int interval )
721 {
722         int rc;
723         ERROR_IF( !threading_enabled, "ldap_pvt_thread_sleep" );
724         rc = ldap_int_thread_sleep( interval );
725         ERROR_IF( rc, "ldap_pvt_thread_sleep" );
726         return 0;
727 }
728
729 static void
730 thread_exiting( const char *how, const char *msg )
731 {
732         ldap_pvt_thread_t thread;
733 #if 0 /* Detached threads may exit after ldap_debug_thread_destroy(). */
734         ERROR_IF( !threading_enabled, msg );
735 #endif
736         thread = ldap_pvt_thread_self();
737         if( tracethreads ) {
738                 char buf[40];
739                 fprintf( stderr, "== thr_debug: %s thread %s ==\n",
740                         how, thread_name( buf, sizeof(buf), thread ) );
741         }
742         if( threadID ) {
743                 with_thread_info_lock({
744                         ldap_debug_thread_t *t = get_thread_info( thread, msg );
745                         if( thread_info_detached( t ) )
746                                 remove_thread_info( t, msg );
747                 });
748         }
749         adjust_count( Idx_unexited_thread, -1 );
750 }
751
752 void
753 ldap_pvt_thread_exit( void *retval )
754 {
755         thread_exiting( "Exiting", "ldap_pvt_thread_exit" );
756         ldap_int_thread_exit( retval );
757 }
758
759 typedef struct {
760         void *(*start_routine)( void * );
761         void *arg;
762 } ldap_debug_thread_call_t;
763
764 static void *
765 ldap_debug_thread_wrapper( void *arg )
766 {
767         void *ret;
768         ldap_debug_thread_call_t call = *(ldap_debug_thread_call_t *)arg;
769         free( arg );
770         ret = call.start_routine( call.arg );
771         thread_exiting( "Returning from", "ldap_debug_thread_wrapper" );
772         return ret;
773 }
774
775 int
776 ldap_pvt_thread_create(
777         ldap_pvt_thread_t *thread,
778         int detach,
779         void *(*start_routine)( void * ),
780         void *arg )
781 {
782         int rc;
783         if( !options_done )
784                 get_options();
785         ERROR_IF( !threading_enabled, "ldap_pvt_thread_create" );
786
787         if( wrap_threads ) {
788                 ldap_debug_thread_call_t *call = malloc(
789                         sizeof( ldap_debug_thread_call_t ) );
790                 assert( call != NULL );
791                 call->start_routine = start_routine;
792                 call->arg = arg;
793                 start_routine = ldap_debug_thread_wrapper;
794                 arg = call;
795         }
796         if( threadID ) {
797                 with_thread_info_lock({
798                         rc = ldap_int_thread_create( thread, detach, start_routine, arg );
799                         if( rc == 0 )
800                                 add_thread_info( "ldap_pvt_thread_create", thread, detach );
801                 });
802         } else {
803                 rc = ldap_int_thread_create( thread, detach, start_routine, arg );
804         }
805         if( rc ) {
806                 ERROR( rc, "ldap_pvt_thread_create" );
807                 if( wrap_threads )
808                         free( arg );
809         } else {
810                 if( tracethreads ) {
811                         char buf[40], buf2[40];
812                         fprintf( stderr,
813                                 "== thr_debug: Created thread %s%s from thread %s ==\n",
814                                 thread_name( buf, sizeof(buf), *thread ),
815                                 detach ? " (detached)" : "",
816                                 thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) );
817                 }
818                 adjust_count( Idx_unexited_thread, +1 );
819                 if( !detach )
820                         adjust_count( Idx_unjoined_thread, +1 );
821         }
822         return rc;
823 }
824
825 int
826 ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
827 {
828         int rc;
829         ldap_debug_thread_t *t = NULL;
830         ERROR_IF( !threading_enabled, "ldap_pvt_thread_join" );
831         if( tracethreads ) {
832                 char buf[40], buf2[40];
833                 fprintf( stderr, "== thr_debug: Joining thread %s in thread %s ==\n",
834                         thread_name( buf, sizeof(buf), thread ),
835                         thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) );
836         }
837         if( threadID )
838                 with_thread_info_lock( {
839                         t = get_thread_info( thread, "ldap_pvt_thread_join" );
840                         ERROR_IF( thread_info_detached( t ), "ldap_pvt_thread_join" );
841                 } );
842         rc = ldap_int_thread_join( thread, thread_return );
843         if( rc ) {
844                 ERROR( rc, "ldap_pvt_thread_join" );
845         } else {
846                 if( threadID )
847                         with_thread_info_lock(
848                                 remove_thread_info( t, "ldap_pvt_thread_join" ) );
849                 adjust_count( Idx_unjoined_thread, -1 );
850         }
851
852         return rc;
853 }
854
855 int
856 ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
857 {
858         int rc;
859         ERROR_IF( !threading_enabled, "ldap_pvt_thread_kill" );
860         if( tracethreads ) {
861                 char buf[40], buf2[40];
862                 fprintf( stderr,
863                         "== thr_debug: Killing thread %s (sig %i) from thread %s ==\n",
864                         thread_name( buf, sizeof(buf), thread ), signo,
865                         thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) );
866         }
867         rc = ldap_int_thread_kill( thread, signo );
868         ERROR_IF( rc, "ldap_pvt_thread_kill" );
869         return rc;
870 }
871
872 int
873 ldap_pvt_thread_yield( void )
874 {
875         int rc;
876         ERROR_IF( !threading_enabled, "ldap_pvt_thread_yield" );
877         rc = ldap_int_thread_yield();
878         ERROR_IF( rc, "ldap_pvt_thread_yield" );
879         return rc;
880 }
881
882 ldap_pvt_thread_t
883 ldap_pvt_thread_self( void )
884 {
885 #if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */
886         ERROR_IF( !threading_enabled, "ldap_pvt_thread_self" );
887 #endif
888         return ldap_int_thread_self();
889 }
890
891 int
892 ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
893 {
894         int rc;
895         init_usage( &cond->usage, "ldap_pvt_thread_cond_init" );
896         rc = ldap_int_thread_cond_init( WRAPPED( cond ) );
897         if( rc ) {
898                 ERROR( rc, "ldap_pvt_thread_cond_init" );
899                 destroy_usage( &cond->usage );
900         } else {
901                 adjust_count( Idx_cond, +1 );
902         }
903         return rc;
904 }
905
906 int
907 ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cond )
908 {
909         int rc;
910         check_usage( &cond->usage, "ldap_pvt_thread_cond_destroy" );
911         rc = ldap_int_thread_cond_destroy( WRAPPED( cond ) );
912         if( rc ) {
913                 ERROR( rc, "ldap_pvt_thread_cond_destroy" );
914         } else {
915                 destroy_usage( &cond->usage );
916                 adjust_count( Idx_cond, -1 );
917         }
918         return rc;
919 }
920
921 int
922 ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
923 {
924         int rc;
925         check_usage( &cond->usage, "ldap_pvt_thread_cond_signal" );
926         rc = ldap_int_thread_cond_signal( WRAPPED( cond ) );
927         ERROR_IF( rc, "ldap_pvt_thread_cond_signal" );
928         return rc;
929 }
930
931 int
932 ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond )
933 {
934         int rc;
935         check_usage( &cond->usage, "ldap_pvt_thread_cond_broadcast" );
936         rc = ldap_int_thread_cond_broadcast( WRAPPED( cond ) );
937         ERROR_IF( rc, "ldap_pvt_thread_cond_broadcast" );
938         return rc;
939 }
940
941 int
942 ldap_pvt_thread_cond_wait(
943         ldap_pvt_thread_cond_t *cond,
944         ldap_pvt_thread_mutex_t *mutex )
945 {
946         int rc;
947         ldap_int_thread_t owner;
948         check_usage( &cond->usage, "ldap_pvt_thread_cond_wait:cond" );
949         check_usage( &mutex->usage, "ldap_pvt_thread_cond_wait:mutex" );
950         adjust_count( Idx_locked_mutex, -1 );
951         owner = GET_OWNER( mutex );
952         ASSERT_OWNER( mutex, "ldap_pvt_thread_cond_wait" );
953         RESET_OWNER( mutex );
954         rc = ldap_int_thread_cond_wait( WRAPPED( cond ), WRAPPED( mutex ) );
955         ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_cond_wait" );
956         SET_OWNER( mutex, rc ? owner : ldap_int_thread_self() );
957         adjust_count( Idx_locked_mutex, +1 );
958         ERROR_IF( rc, "ldap_pvt_thread_cond_wait" );
959         return rc;
960 }
961
962 int
963 ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
964 {
965         int rc;
966         init_usage( &mutex->usage, "ldap_pvt_thread_mutex_init" );
967         rc = ldap_int_thread_mutex_init( WRAPPED( mutex ) );
968         if( rc ) {
969                 ERROR( rc, "ldap_pvt_thread_mutex_init" );
970                 destroy_usage( &mutex->usage );
971         } else {
972                 RESET_OWNER( mutex );
973                 adjust_count( Idx_mutex, +1 );
974         }
975         return rc;
976 }
977
978 int
979 ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
980 {
981         int rc;
982         check_usage( &mutex->usage, "ldap_pvt_thread_mutex_destroy" );
983         ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_destroy" );
984         rc = ldap_int_thread_mutex_destroy( WRAPPED( mutex ) );
985         if( rc ) {
986                 ERROR( rc, "ldap_pvt_thread_mutex_destroy" );
987         } else {
988                 destroy_usage( &mutex->usage );
989                 RESET_OWNER( mutex );
990                 adjust_count( Idx_mutex, -1 );
991         }
992         return rc;
993 }
994
995 int
996 ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
997 {
998         int rc;
999         check_usage( &mutex->usage, "ldap_pvt_thread_mutex_lock" );
1000         rc = ldap_int_thread_mutex_lock( WRAPPED( mutex ) );
1001         if( rc ) {
1002                 ERROR_IF( rc, "ldap_pvt_thread_mutex_lock" );
1003         } else {
1004                 ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_lock" );
1005                 SET_OWNER( mutex, ldap_int_thread_self() );
1006                 adjust_count( Idx_locked_mutex, +1 );
1007         }
1008         return rc;
1009 }
1010
1011 int
1012 ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex )
1013 {
1014         int rc;
1015         check_usage( &mutex->usage, "ldap_pvt_thread_mutex_trylock" );
1016         rc = ldap_int_thread_mutex_trylock( WRAPPED( mutex ) );
1017         if( rc == 0 ) {
1018                 ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_trylock" );
1019                 SET_OWNER( mutex, ldap_int_thread_self() );
1020                 adjust_count( Idx_locked_mutex, +1 );
1021         }
1022         return rc;
1023 }
1024
1025 int
1026 ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
1027 {
1028         int rc;
1029         check_usage( &mutex->usage, "ldap_pvt_thread_mutex_unlock" );
1030         ASSERT_OWNER( mutex, "ldap_pvt_thread_mutex_unlock" );
1031         RESET_OWNER( mutex ); /* Breaks if this thread did not own the mutex */
1032         rc = ldap_int_thread_mutex_unlock( WRAPPED( mutex ) );
1033         if( rc ) {
1034                 ERROR_IF( rc, "ldap_pvt_thread_mutex_unlock" );
1035         } else {
1036                 adjust_count( Idx_locked_mutex, -1 );
1037         }
1038         return rc;
1039 }
1040
1041
1042 /* Wrappers for LDAP_THREAD_RDWR_IMPLEMENTATION: */
1043
1044 int
1045 ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rwlock )
1046 {
1047         int rc;
1048         init_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_init" );
1049         rc = ldap_int_thread_rdwr_init( WRAPPED( rwlock ) );
1050         if( rc ) {
1051                 ERROR( rc, "ldap_pvt_thread_rdwr_init" );
1052                 destroy_usage( &rwlock->usage );
1053         } else {
1054                 adjust_count( Idx_rdwr, +1 );
1055         }
1056         return rc;
1057 }
1058
1059 int
1060 ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rwlock )
1061 {
1062         int rc;
1063         check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_destroy" );
1064         rc = ldap_int_thread_rdwr_destroy( WRAPPED( rwlock ) );
1065         if( rc ) {
1066                 ERROR( rc, "ldap_pvt_thread_rdwr_destroy" );
1067         } else {
1068                 destroy_usage( &rwlock->usage );
1069                 adjust_count( Idx_rdwr, -1 );
1070         }
1071         return rc;
1072 }
1073
1074 int
1075 ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rwlock )
1076 {
1077         int rc;
1078         check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_rlock" );
1079         rc = ldap_int_thread_rdwr_rlock( WRAPPED( rwlock ) );
1080         ERROR_IF( rc, "ldap_pvt_thread_rdwr_rlock" );
1081         return rc;
1082 }
1083
1084 int
1085 ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rwlock )
1086 {
1087         check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_rtrylock" );
1088         return ldap_int_thread_rdwr_rtrylock( WRAPPED( rwlock ) );
1089 }
1090
1091 int
1092 ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rwlock )
1093 {
1094         int rc;
1095         check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_runlock" );
1096         rc = ldap_int_thread_rdwr_runlock( WRAPPED( rwlock ) );
1097         ERROR_IF( rc, "ldap_pvt_thread_rdwr_runlock" );
1098         return rc;
1099 }
1100
1101 int
1102 ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rwlock )
1103 {
1104         int rc;
1105         check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wlock" );
1106         rc = ldap_int_thread_rdwr_wlock( WRAPPED( rwlock ) );
1107         ERROR_IF( rc, "ldap_pvt_thread_rdwr_wlock" );
1108         return rc;
1109 }
1110
1111 int
1112 ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rwlock )
1113 {
1114         check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wtrylock" );
1115         return ldap_int_thread_rdwr_wtrylock( WRAPPED( rwlock ) );
1116 }
1117
1118 int
1119 ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rwlock )
1120 {
1121         int rc;
1122         check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wunlock" );
1123         rc = ldap_int_thread_rdwr_wunlock( WRAPPED( rwlock ) );
1124         ERROR_IF( rc, "ldap_pvt_thread_rdwr_wunlock" );
1125         return rc;
1126 }
1127
1128 #if defined(LDAP_RDWR_DEBUG) && !defined(LDAP_THREAD_HAVE_RDWR)
1129
1130 int
1131 ldap_pvt_thread_rdwr_readers( ldap_pvt_thread_rdwr_t *rwlock )
1132 {
1133         check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_readers" );
1134         return ldap_int_thread_rdwr_readers( WRAPPED( rwlock ) );
1135 }
1136
1137 int
1138 ldap_pvt_thread_rdwr_writers( ldap_pvt_thread_rdwr_t *rwlock )
1139 {
1140         check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_writers" );
1141         return ldap_int_thread_rdwr_writers( WRAPPED( rwlock ) );
1142 }
1143
1144 int
1145 ldap_pvt_thread_rdwr_active( ldap_pvt_thread_rdwr_t *rwlock )
1146 {
1147         check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_active" );
1148         return ldap_int_thread_rdwr_active( WRAPPED( rwlock ) );
1149 }
1150
1151 #endif /* LDAP_RDWR_DEBUG && !LDAP_THREAD_HAVE_RDWR */
1152
1153
1154 /* Some wrappers for LDAP_THREAD_POOL_IMPLEMENTATION: */
1155 #ifdef LDAP_THREAD_POOL_IMPLEMENTATION
1156
1157 int
1158 ldap_pvt_thread_pool_init(
1159         ldap_pvt_thread_pool_t *tpool,
1160         int max_threads,
1161         int max_pending )
1162 {
1163         int rc;
1164         if( !options_done )
1165                 get_options();
1166         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_init" );
1167         rc = ldap_int_thread_pool_init( tpool, max_threads, max_pending );
1168         if( rc ) {
1169                 ERROR( rc, "ldap_pvt_thread_pool_init" );
1170         } else {
1171                 adjust_count( Idx_tpool, +1 );
1172         }
1173         return rc;
1174 }
1175
1176 int
1177 ldap_pvt_thread_pool_submit(
1178         ldap_pvt_thread_pool_t *tpool,
1179         ldap_pvt_thread_start_t *start_routine, void *arg )
1180 {
1181         int rc, has_pool;
1182         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_submit" );
1183         has_pool = (tpool && *tpool);
1184         rc = ldap_int_thread_pool_submit( tpool, start_routine, arg );
1185         if( has_pool )
1186                 ERROR_IF( rc, "ldap_pvt_thread_pool_submit" );
1187         return rc;
1188 }
1189
1190 int
1191 ldap_pvt_thread_pool_maxthreads(
1192         ldap_pvt_thread_pool_t *tpool,
1193         int max_threads )
1194 {
1195         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_maxthreads" );
1196         return ldap_int_thread_pool_maxthreads( tpool, max_threads );
1197 }
1198
1199 int
1200 ldap_pvt_thread_pool_backload( ldap_pvt_thread_pool_t *tpool )
1201 {
1202         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_backload" );
1203         return ldap_int_thread_pool_backload( tpool );
1204 }
1205
1206 int
1207 ldap_pvt_thread_pool_destroy( ldap_pvt_thread_pool_t *tpool, int run_pending )
1208 {
1209         int rc, has_pool;
1210         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_destroy" );
1211         has_pool = (tpool && *tpool);
1212         rc = ldap_int_thread_pool_destroy( tpool, run_pending );
1213         if( has_pool ) {
1214                 if( rc ) {
1215                         ERROR( rc, "ldap_pvt_thread_pool_destroy" );
1216                 } else {
1217                         adjust_count( Idx_tpool, -1 );
1218                 }
1219         }
1220         return rc;
1221 }
1222
1223 int
1224 ldap_pvt_thread_pool_pause( ldap_pvt_thread_pool_t *tpool )
1225 {
1226         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_pause" );
1227         return ldap_int_thread_pool_pause( tpool );
1228 }
1229
1230 int
1231 ldap_pvt_thread_pool_resume( ldap_pvt_thread_pool_t *tpool )
1232 {
1233         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_resume" );
1234         return ldap_int_thread_pool_resume( tpool );
1235 }
1236
1237 int
1238 ldap_pvt_thread_pool_getkey(
1239         void *xctx,
1240         void *key,
1241         void **data,
1242         ldap_pvt_thread_pool_keyfree_t **kfree )
1243 {
1244 #if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */
1245         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_getkey" );
1246 #endif
1247         return ldap_int_thread_pool_getkey( xctx, key, data, kfree );
1248 }
1249
1250 int
1251 ldap_pvt_thread_pool_setkey(
1252         void *xctx,
1253         void *key,
1254         void *data,
1255         ldap_pvt_thread_pool_keyfree_t *kfree,
1256         void **olddatap,
1257         ldap_pvt_thread_pool_keyfree_t **oldkfreep )
1258 {
1259         int rc;
1260         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_setkey" );
1261         rc = ldap_int_thread_pool_setkey(
1262                 xctx, key, data, kfree, olddatap, oldkfreep );
1263         ERROR_IF( rc, "ldap_pvt_thread_pool_setkey" );
1264         return rc;
1265 }
1266
1267 void
1268 ldap_pvt_thread_pool_purgekey( void *key )
1269 {
1270         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_purgekey" );
1271         ldap_int_thread_pool_purgekey( key );
1272 }
1273
1274 void *
1275 ldap_pvt_thread_pool_context( void )
1276 {
1277 #if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */
1278         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_context" );
1279 #endif
1280         return ldap_int_thread_pool_context();
1281 }
1282
1283 void
1284 ldap_pvt_thread_pool_context_reset( void *vctx )
1285 {
1286         ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_context_reset" );
1287         ldap_int_thread_pool_context_reset( vctx );
1288 }
1289
1290 #endif /* LDAP_THREAD_POOL_IMPLEMENTATION */
1291
1292 #endif /* LDAP_THREAD_DEBUG */