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