summaryrefslogtreecommitdiffstats
path: root/libraries/libldap_r/thr_debug.c
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/libldap_r/thr_debug.c')
-rw-r--r--libraries/libldap_r/thr_debug.c1292
1 files changed, 1292 insertions, 0 deletions
diff --git a/libraries/libldap_r/thr_debug.c b/libraries/libldap_r/thr_debug.c
new file mode 100644
index 0000000..24ac74b
--- /dev/null
+++ b/libraries/libldap_r/thr_debug.c
@@ -0,0 +1,1292 @@
+/* thr_debug.c - wrapper around the chosen thread wrapper, for debugging. */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * This package provides several types of thread operation debugging:
+ *
+ * - Check the results of operations on threads, mutexes, condition
+ * variables and read/write locks. Also check some thread pool
+ * operations, but not those for which failure can happen in normal
+ * slapd operation.
+ *
+ * - Wrap those types except threads and pools in structs with state
+ * information, and check that on all operations:
+ *
+ * + Check that the resources are initialized and are only used at
+ * their original address (i.e. not realloced or copied).
+ *
+ * + Check the owner (thread ID) on mutex operations.
+ *
+ * + Optionally allocate a reference to a byte of dummy memory.
+ * This lets malloc debuggers see some incorrect use as memory
+ * leaks, access to freed memory, etc.
+ *
+ * - Print an error message and by default abort() upon errors.
+ *
+ * - Print a count of leaked thread resources after cleanup.
+ *
+ * Compile-time (./configure) setup: Macros defined in CPPFLAGS.
+ *
+ * LDAP_THREAD_DEBUG or LDAP_THREAD_DEBUG=2
+ * Enables debugging, but value & 2 turns off type wrapping.
+ *
+ * LDAP_UINTPTR_T=integer type to hold pointers, preferably unsigned.
+ * Used by dummy memory option "scramble". Default = unsigned long.
+ *
+ * LDAP_DEBUG_THREAD_NONE = initializer for a "no thread" thread ID.
+ *
+ * In addition, you may need to set up an implementation-specific way
+ * to enable whatever error checking your thread library provides.
+ * Currently only implemented for Posix threads (pthreads), where
+ * you may need to define LDAP_INT_THREAD_MUTEXATTR. The default
+ * is PTHREAD_MUTEX_ERRORCHECK, or PTHREAD_MUTEX_ERRORCHECK_NP for
+ * Linux threads. See pthread_mutexattr_settype(3).
+ *
+ * Run-time configuration:
+ *
+ * Memory debugging tools:
+ * Tools that report uninitialized memory accesses should disable
+ * such warnings about the function debug_already_initialized().
+ * Alternatively, include "noreinit" (below) in $LDAP_THREAD_DEBUG.
+ *
+ * Environment variable $LDAP_THREAD_DEBUG:
+ * The variable may contain a comma- or space-separated option list.
+ * Options:
+ * off - Disable this package. (It still slows things down).
+ * tracethreads - Report create/join/exit/kill of threads.
+ * noabort - Do not abort() on errors.
+ * noerror - Do not report errors. Implies noabort.
+ * nocount - Do not report counts of unreleased resources.
+ * nosync - Disable tests that use synchronizaion and thus
+ * clearly affect thread scheduling:
+ * Implies nocount, and cancels threadID if that is set.
+ * Note that if you turn on tracethreads or malloc
+ * debugging, these also use library calls which may
+ * affect thread scheduling (fprintf and malloc).
+ * The following options do not apply if type wrapping is disabled:
+ * nomem - Do not check memory operations.
+ * Implies noreinit,noalloc.
+ * noreinit - Do not catch reinitialization of existing resources.
+ * (That test accesses uninitialized memory).
+ * threadID - Trace thread IDs. Currently mostly useless.
+ * Malloc debugging -- allocate dummy memory for initialized
+ * resources, so malloc debuggers will report them as memory leaks:
+ * noalloc - Default. Do not allocate dummy memory.
+ * alloc - Store a pointer to dummy memory. However, leak
+ * detectors might not catch unreleased resources in
+ * global variables.
+ * scramble - Store bitwise complement of dummy memory pointer.
+ * That never escapes memory leak detectors -
+ * but detection while the program is running will
+ * report active resources as leaks. Do not
+ * use this if a garbage collector is in use:-)
+ * adjptr - Point to end of dummy memory.
+ * Purify reports these as "potential leaks" (PLK).
+ * I have not checked other malloc debuggers.
+ */
+
+#include "portable.h"
+
+#if defined( LDAP_THREAD_DEBUG )
+
+#include <stdio.h>
+#include <ac/errno.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_IMPLEMENTATION
+#define LDAP_THREAD_DEBUG_IMPLEMENTATION
+#define LDAP_THREAD_RDWR_IMPLEMENTATION
+#define LDAP_THREAD_POOL_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* Get the underlying implementation */
+
+#ifndef LDAP_THREAD_DEBUG_WRAP
+#undef LDAP_THREAD_DEBUG_THREAD_ID
+#elif !defined LDAP_THREAD_DEBUG_THREAD_ID
+#define LDAP_THREAD_DEBUG_THREAD_ID 1
+#endif
+
+/* Use native malloc - the OpenLDAP wrappers may defeat malloc debuggers */
+#undef malloc
+#undef calloc
+#undef realloc
+#undef free
+
+
+/* Options from environment variable $LDAP_THREAD_DEBUG */
+enum { Count_no = 0, Count_yes, Count_reported, Count_reported_more };
+static int count = Count_yes;
+#ifdef LDAP_THREAD_DEBUG_WRAP
+enum { Wrap_noalloc, Wrap_alloc, Wrap_scramble, Wrap_adjptr };
+static int wraptype = Wrap_noalloc, wrap_offset, unwrap_offset;
+static int nomem, noreinit;
+#endif
+#if LDAP_THREAD_DEBUG_THREAD_ID +0
+static int threadID;
+#else
+enum { threadID = 0 };
+#endif
+static int nodebug, noabort, noerror, nosync, tracethreads;
+static int wrap_threads;
+static int options_done;
+
+
+/* ldap_pvt_thread_initialize() called, ldap_pvt_thread_destroy() not called */
+static int threading_enabled;
+
+
+/* Resource counts */
+enum {
+ Idx_unexited_thread, Idx_unjoined_thread, Idx_locked_mutex,
+ Idx_mutex, Idx_cond, Idx_rdwr, Idx_tpool, Idx_max
+};
+static int resource_counts[Idx_max];
+static const char *const resource_names[] = {
+ "unexited threads", "unjoined threads", "locked mutexes",
+ "mutexes", "conds", "rdwrs", "thread pools"
+};
+static ldap_int_thread_mutex_t resource_mutexes[Idx_max];
+
+
+/* Hide pointers from malloc debuggers. */
+#define SCRAMBLE(ptr) (~(LDAP_UINTPTR_T) (ptr))
+#define UNSCRAMBLE_usagep(num) ((ldap_debug_usage_info_t *) ~(num))
+#define UNSCRAMBLE_dummyp(num) ((unsigned char *) ~(num))
+
+
+#define WARN(var, msg) (warn (__FILE__, __LINE__, (msg), #var, (var)))
+#define WARN_IF(rc, msg) {if (rc) warn (__FILE__, __LINE__, (msg), #rc, (rc));}
+
+#define ERROR(var, msg) { \
+ if (!noerror) { \
+ errmsg(__FILE__, __LINE__, (msg), #var, (var)); \
+ if( !noabort ) abort(); \
+ } \
+}
+
+#define ERROR_IF(rc, msg) { \
+ if (!noerror) { \
+ int rc_ = (rc); \
+ if (rc_) { \
+ errmsg(__FILE__, __LINE__, (msg), #rc, rc_); \
+ if( !noabort ) abort(); \
+ } \
+ } \
+}
+
+#ifdef LDAP_THREAD_DEBUG_WRAP
+#define MEMERROR_IF(rc, msg, mem_act) { \
+ if (!noerror) { \
+ int rc_ = (rc); \
+ if (rc_) { \
+ errmsg(__FILE__, __LINE__, (msg), #rc, rc_); \
+ if( wraptype != Wrap_noalloc ) { mem_act; } \
+ if( !noabort ) abort(); \
+ } \
+ } \
+}
+#endif /* LDAP_THREAD_DEBUG_WRAP */
+
+#if 0
+static void
+warn( const char *file, int line, const char *msg, const char *var, int val )
+{
+ fprintf( stderr,
+ (strpbrk( var, "!=" )
+ ? "%s:%d: %s warning: %s\n"
+ : "%s:%d: %s warning: %s is %d\n"),
+ file, line, msg, var, val );
+}
+#endif
+
+static void
+errmsg( const char *file, int line, const char *msg, const char *var, int val )
+{
+ fprintf( stderr,
+ (strpbrk( var, "!=" )
+ ? "%s:%d: %s error: %s\n"
+ : "%s:%d: %s error: %s is %d\n"),
+ file, line, msg, var, val );
+}
+
+static void
+count_resource_leaks( void )
+{
+ int i, j;
+ char errbuf[200];
+ if( count == Count_yes ) {
+ count = Count_reported;
+#if 0 /* Could break if there are still threads after atexit */
+ for( i = j = 0; i < Idx_max; i++ )
+ j |= ldap_int_thread_mutex_destroy( &resource_mutexes[i] );
+ WARN_IF( j, "ldap_debug_thread_destroy:mutexes" );
+#endif
+ for( i = j = 0; i < Idx_max; i++ )
+ if( resource_counts[i] )
+ j += sprintf( errbuf + j, ", %d %s",
+ resource_counts[i], resource_names[i] );
+ if( j )
+ fprintf( stderr, "== thr_debug: Leaked%s. ==\n", errbuf + 1 );
+ }
+}
+
+static void
+get_options( void )
+{
+ static const struct option_info_s {
+ const char *name;
+ int *var, val;
+ } option_info[] = {
+ { "off", &nodebug, 1 },
+ { "noabort", &noabort, 1 },
+ { "noerror", &noerror, 1 },
+ { "nocount", &count, Count_no },
+ { "nosync", &nosync, 1 },
+#if LDAP_THREAD_DEBUG_THREAD_ID +0
+ { "threadID", &threadID, 1 },
+#endif
+#ifdef LDAP_THREAD_DEBUG_WRAP
+ { "nomem", &nomem, 1 },
+ { "noreinit", &noreinit, 1 },
+ { "noalloc", &wraptype, Wrap_noalloc },
+ { "alloc", &wraptype, Wrap_alloc },
+ { "adjptr", &wraptype, Wrap_adjptr },
+ { "scramble", &wraptype, Wrap_scramble },
+#endif
+ { "tracethreads", &tracethreads, 1 },
+ { NULL, NULL, 0 }
+ };
+ const char *s = getenv( "LDAP_THREAD_DEBUG" );
+ if( s != NULL ) {
+ while( *(s += strspn( s, ", \t\r\n" )) != '\0' ) {
+ size_t optlen = strcspn( s, ", \t\r\n" );
+ const struct option_info_s *oi = option_info;
+ while( oi->name &&
+ (strncasecmp( oi->name, s, optlen ) || oi->name[optlen]) )
+ oi++;
+ if( oi->name )
+ *oi->var = oi->val;
+ else
+ fprintf( stderr,
+ "== thr_debug: Unknown $%s option '%.*s' ==\n",
+ "LDAP_THREAD_DEBUG", (int) optlen, s );
+ s += optlen;
+ }
+ }
+ if( nodebug ) {
+ tracethreads = 0;
+ nosync = noerror = 1;
+ }
+ if( nosync )
+ count = Count_no;
+ if( noerror )
+ noabort = 1;
+#if LDAP_THREAD_DEBUG_THREAD_ID +0
+ if( nosync )
+ threadID = 0;
+#endif
+#ifdef LDAP_THREAD_DEBUG_WRAP
+ if( noerror )
+ nomem = 1;
+ if( !nomem ) {
+ static const ldap_debug_usage_info_t usage;
+ if( sizeof(LDAP_UINTPTR_T) < sizeof(unsigned char *)
+ || sizeof(LDAP_UINTPTR_T) < sizeof(ldap_debug_usage_info_t *)
+ || UNSCRAMBLE_usagep( SCRAMBLE( &usage ) ) != &usage
+ || UNSCRAMBLE_dummyp( SCRAMBLE( (unsigned char *) 0 ) ) )
+ {
+ fputs( "== thr_debug: Memory checks unsupported, "
+ "adding nomem to $LDAP_THREAD_DEBUG ==\n", stderr );
+ nomem = 1;
+ }
+ }
+ if( nomem ) {
+ noreinit = 1;
+ wraptype = Wrap_noalloc;
+ }
+ unwrap_offset = -(wrap_offset = (wraptype == Wrap_adjptr));
+#endif
+ wrap_threads = (tracethreads || threadID || count);
+ options_done = 1;
+}
+
+
+#ifndef LDAP_THREAD_DEBUG_WRAP
+
+#define WRAPPED(ptr) (ptr)
+#define GET_OWNER(ptr) 0
+#define SET_OWNER(ptr, thread) ((void) 0)
+#define RESET_OWNER(ptr) ((void) 0)
+#define ASSERT_OWNER(ptr, msg) ((void) 0)
+#define ASSERT_NO_OWNER(ptr, msg) ((void) 0)
+
+#define init_usage(ptr, msg) ((void) 0)
+#define check_usage(ptr, msg) ((void) 0)
+#define destroy_usage(ptr) ((void) 0)
+
+#else /* LDAP_THREAD_DEBUG_WRAP */
+
+/* Specialize this if the initializer is not appropriate. */
+/* The ASSERT_NO_OWNER() definition may also need an override. */
+#ifndef LDAP_DEBUG_THREAD_NONE
+#define LDAP_DEBUG_THREAD_NONE { -1 } /* "no thread" ldap_int_thread_t value */
+#endif
+
+static const ldap_int_thread_t ldap_debug_thread_none = LDAP_DEBUG_THREAD_NONE;
+
+#define THREAD_MUTEX_OWNER(mutex) \
+ ldap_int_thread_equal( (mutex)->owner, ldap_int_thread_self() )
+
+void
+ldap_debug_thread_assert_mutex_owner(
+ const char *file,
+ int line,
+ const char *msg,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ if( !(noerror || THREAD_MUTEX_OWNER( mutex )) ) {
+ errmsg( file, line, msg, "ASSERT_MUTEX_OWNER", 0 );
+ if( !noabort ) abort();
+ }
+}
+
+#define WRAPPED(ptr) (&(ptr)->wrapped)
+#define GET_OWNER(ptr) ((ptr)->owner)
+#define SET_OWNER(ptr, thread) ((ptr)->owner = (thread))
+#define RESET_OWNER(ptr) ((ptr)->owner = ldap_debug_thread_none)
+#define ASSERT_OWNER(ptr, msg) ERROR_IF( !THREAD_MUTEX_OWNER( ptr ), msg )
+#ifndef ASSERT_NO_OWNER
+#define ASSERT_NO_OWNER(ptr, msg) ERROR_IF( \
+ !ldap_int_thread_equal( (ptr)->owner, ldap_debug_thread_none ), msg )
+#endif
+
+/* Try to provoke memory access error (for malloc debuggers) */
+#define PEEK(mem) {if (-*(volatile const unsigned char *)(mem)) debug_noop();}
+
+static void debug_noop( void );
+static int debug_already_initialized( const ldap_debug_usage_info_t *usage );
+
+/* Name used for clearer error message */
+#define IS_COPY_OR_MOVED(usage) ((usage)->self != SCRAMBLE( usage ))
+
+#define DUMMY_ADDR(usage) \
+ (wraptype == Wrap_scramble \
+ ? UNSCRAMBLE_dummyp( (usage)->mem.num ) \
+ : (usage)->mem.ptr + unwrap_offset)
+
+/* Mark resource as initialized */
+static void
+init_usage( ldap_debug_usage_info_t *usage, const char *msg )
+{
+ if( !options_done )
+ get_options();
+ if( !nomem ) {
+ if( !noreinit ) {
+ MEMERROR_IF( debug_already_initialized( usage ), msg, {
+ /* Provoke malloc debuggers */
+ unsigned char *dummy = DUMMY_ADDR( usage );
+ PEEK( dummy );
+ free( dummy );
+ free( dummy );
+ } );
+ }
+ if( wraptype != Wrap_noalloc ) {
+ unsigned char *dummy = malloc( 1 );
+ assert( dummy != NULL );
+ if( wraptype == Wrap_scramble ) {
+ usage->mem.num = SCRAMBLE( dummy );
+ /* Verify that ptr<->integer casts work on this host */
+ assert( UNSCRAMBLE_dummyp( usage->mem.num ) == dummy );
+ } else {
+ usage->mem.ptr = dummy + wrap_offset;
+ }
+ }
+ } else {
+ /* Unused, but set for readability in debugger */
+ usage->mem.ptr = NULL;
+ }
+ usage->self = SCRAMBLE( usage ); /* If nomem, only for debugger */
+ usage->magic = ldap_debug_magic;
+ usage->state = ldap_debug_state_inited;
+}
+
+/* Check that resource is initialized and not copied/realloced */
+static void
+check_usage( const ldap_debug_usage_info_t *usage, const char *msg )
+{
+ enum { Is_destroyed = 1 }; /* Name used for clearer error message */
+
+ if( usage->magic != ldap_debug_magic ) {
+ ERROR( usage->magic, msg );
+ return;
+ }
+ switch( usage->state ) {
+ case ldap_debug_state_destroyed:
+ MEMERROR_IF( Is_destroyed, msg, {
+ PEEK( DUMMY_ADDR( usage ) );
+ } );
+ break;
+ default:
+ ERROR( usage->state, msg );
+ break;
+ case ldap_debug_state_inited:
+ if( !nomem ) {
+ MEMERROR_IF( IS_COPY_OR_MOVED( usage ), msg, {
+ PEEK( DUMMY_ADDR( usage ) );
+ PEEK( UNSCRAMBLE_usagep( usage->self ) );
+ } );
+ }
+ break;
+ }
+}
+
+/* Mark resource as destroyed. */
+/* Does not check for errors, call check_usage()/init_usage() first. */
+static void
+destroy_usage( ldap_debug_usage_info_t *usage )
+{
+ if( usage->state == ldap_debug_state_inited ) {
+ if( wraptype != Wrap_noalloc ) {
+ free( DUMMY_ADDR( usage ) );
+ /* Do not reset the DUMMY_ADDR, leave it for malloc debuggers
+ * in case the resource is used after it is freed. */
+ }
+ usage->state = ldap_debug_state_destroyed;
+ }
+}
+
+/* Define these after they are used, so they are hopefully not inlined */
+
+static void
+debug_noop( void )
+{
+}
+
+/*
+ * Valid programs access uninitialized memory here unless "noreinit".
+ *
+ * Returns true if the resource is initialized and not copied/realloced.
+ */
+LDAP_GCCATTR((noinline))
+static int
+debug_already_initialized( const ldap_debug_usage_info_t *usage )
+{
+ /*
+ * 'ret' keeps the Valgrind warning "Conditional jump or move
+ * depends on uninitialised value(s)" _inside_ this function.
+ */
+ volatile int ret = 0;
+ if( usage->state == ldap_debug_state_inited )
+ if( !IS_COPY_OR_MOVED( usage ) )
+ if( usage->magic == ldap_debug_magic )
+ ret = 1;
+ return ret;
+}
+
+#endif /* LDAP_THREAD_DEBUG_WRAP */
+
+
+#if !(LDAP_THREAD_DEBUG_THREAD_ID +0)
+
+typedef void ldap_debug_thread_t;
+#define init_thread_info() {}
+#define with_thread_info_lock(statements) { statements; }
+#define thread_info_detached(t) 0
+#define add_thread_info(msg, thr, det) ((void) 0)
+#define remove_thread_info(tinfo, msg) ((void) 0)
+#define get_thread_info(thread, msg) NULL
+
+#else /* LDAP_THREAD_DEBUG_THREAD_ID */
+
+/*
+ * Thread ID tracking. Currently acieves little.
+ * Should be either expanded or deleted.
+ */
+
+/*
+ * Array of threads. Used instead of making ldap_pvt_thread_t a wrapper
+ * around ldap_int_thread_t, which would slow down ldap_pvt_thread_self().
+ */
+typedef struct {
+ ldap_pvt_thread_t wrapped;
+ ldap_debug_usage_info_t usage;
+ int detached;
+ int idx;
+} ldap_debug_thread_t;
+
+static ldap_debug_thread_t **thread_info;
+static unsigned int thread_info_size, thread_info_used;
+static ldap_int_thread_mutex_t thread_info_mutex;
+
+#define init_thread_info() { \
+ if( threadID ) { \
+ int mutex_init_rc = ldap_int_thread_mutex_init( &thread_info_mutex ); \
+ assert( mutex_init_rc == 0 ); \
+ } \
+}
+
+#define with_thread_info_lock(statements) { \
+ int rc_wtl_ = ldap_int_thread_mutex_lock( &thread_info_mutex ); \
+ assert( rc_wtl_ == 0 ); \
+ { statements; } \
+ rc_wtl_ = ldap_int_thread_mutex_unlock( &thread_info_mutex ); \
+ assert( rc_wtl_ == 0 ); \
+}
+
+#define thread_info_detached(t) ((t)->detached)
+
+static void
+add_thread_info(
+ const char *msg,
+ const ldap_pvt_thread_t *thread,
+ int detached )
+{
+ ldap_debug_thread_t *t;
+
+ if( thread_info_used >= thread_info_size ) {
+ unsigned int more = thread_info_size + 8;
+ unsigned int new_size = thread_info_size + more;
+
+ t = calloc( more, sizeof(ldap_debug_thread_t) );
+ assert( t != NULL );
+ thread_info = realloc( thread_info, new_size * sizeof(*thread_info) );
+ assert( thread_info != NULL );
+ do {
+ t->idx = thread_info_size;
+ thread_info[thread_info_size++] = t++;
+ } while( thread_info_size < new_size );
+ }
+
+ t = thread_info[thread_info_used];
+ init_usage( &t->usage, msg );
+ t->wrapped = *thread;
+ t->detached = detached;
+ thread_info_used++;
+}
+
+static void
+remove_thread_info( ldap_debug_thread_t *t, const char *msg )
+{
+ ldap_debug_thread_t *last;
+ int idx;
+ check_usage( &t->usage, msg );
+ destroy_usage( &t->usage );
+ idx = t->idx;
+ assert( thread_info[idx] == t );
+ last = thread_info[--thread_info_used];
+ assert( last->idx == thread_info_used );
+ (thread_info[idx] = last)->idx = idx;
+ (thread_info[thread_info_used] = t )->idx = thread_info_used;
+}
+
+static ldap_debug_thread_t *
+get_thread_info( ldap_pvt_thread_t thread, const char *msg )
+{
+ unsigned int i;
+ ldap_debug_thread_t *t;
+ for( i = 0; i < thread_info_used; i++ ) {
+ if( ldap_pvt_thread_equal( thread, thread_info[i]->wrapped ) )
+ break;
+ }
+ ERROR_IF( i == thread_info_used, msg );
+ t = thread_info[i];
+ check_usage( &t->usage, msg );
+ return t;
+}
+
+#endif /* LDAP_THREAD_DEBUG_THREAD_ID */
+
+
+static char *
+thread_name( char *buf, int bufsize, ldap_pvt_thread_t thread )
+{
+ int i;
+ --bufsize;
+ if( bufsize > 2*sizeof(thread) )
+ bufsize = 2*sizeof(thread);
+ for( i = 0; i < bufsize; i += 2 )
+ snprintf( buf+i, 3, "%02x", ((unsigned char *)&thread)[i/2] );
+ return buf;
+}
+
+
+/* Add <adjust> (+/-1) to resource count <which> unless "nocount". */
+static void
+adjust_count( int which, int adjust )
+{
+ int rc;
+ switch( count ) {
+ case Count_no:
+ break;
+ case Count_yes:
+ rc = ldap_int_thread_mutex_lock( &resource_mutexes[which] );
+ assert( rc == 0 );
+ resource_counts[which] += adjust;
+ rc = ldap_int_thread_mutex_unlock( &resource_mutexes[which] );
+ assert( rc == 0 );
+ break;
+ case Count_reported:
+ fputs( "== thr_debug: More thread activity after exit ==\n", stderr );
+ count = Count_reported_more;
+ /* FALL THROUGH */
+ case Count_reported_more:
+ /* Not used, but result might be inspected with debugger */
+ /* (Hopefully threading is disabled by now...) */
+ resource_counts[which] += adjust;
+ break;
+ }
+}
+
+
+/* Wrappers for LDAP_THREAD_IMPLEMENTATION: */
+
+/* Used instead of ldap_int_thread_initialize by ldap_pvt_thread_initialize */
+int
+ldap_debug_thread_initialize( void )
+{
+ int i, rc, rc2;
+ if( !options_done )
+ get_options();
+ ERROR_IF( threading_enabled, "ldap_debug_thread_initialize" );
+ threading_enabled = 1;
+ rc = ldap_int_thread_initialize();
+ if( rc ) {
+ ERROR( rc, "ldap_debug_thread_initialize:threads" );
+ threading_enabled = 0;
+ } else {
+ init_thread_info();
+ if( count != Count_no ) {
+ for( i = rc2 = 0; i < Idx_max; i++ )
+ rc2 |= ldap_int_thread_mutex_init( &resource_mutexes[i] );
+ assert( rc2 == 0 );
+ /* FIXME: Only for static libldap_r as in init.c? If so, why? */
+ atexit( count_resource_leaks );
+ }
+ }
+ return rc;
+}
+
+/* Used instead of ldap_int_thread_destroy by ldap_pvt_thread_destroy */
+int
+ldap_debug_thread_destroy( void )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_debug_thread_destroy" );
+ /* sleep(1) -- need to wait for thread pool to finish? */
+ rc = ldap_int_thread_destroy();
+ if( rc ) {
+ ERROR( rc, "ldap_debug_thread_destroy:threads" );
+ } else {
+ threading_enabled = 0;
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_set_concurrency( int n )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_set_concurrency" );
+ rc = ldap_int_thread_set_concurrency( n );
+ ERROR_IF( rc, "ldap_pvt_thread_set_concurrency" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_get_concurrency( void )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_get_concurrency" );
+ rc = ldap_int_thread_get_concurrency();
+ ERROR_IF( rc, "ldap_pvt_thread_get_concurrency" );
+ return rc;
+}
+
+unsigned int
+ldap_pvt_thread_sleep( unsigned int interval )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_sleep" );
+ rc = ldap_int_thread_sleep( interval );
+ ERROR_IF( rc, "ldap_pvt_thread_sleep" );
+ return 0;
+}
+
+static void
+thread_exiting( const char *how, const char *msg )
+{
+ ldap_pvt_thread_t thread;
+#if 0 /* Detached threads may exit after ldap_debug_thread_destroy(). */
+ ERROR_IF( !threading_enabled, msg );
+#endif
+ thread = ldap_pvt_thread_self();
+ if( tracethreads ) {
+ char buf[40];
+ fprintf( stderr, "== thr_debug: %s thread %s ==\n",
+ how, thread_name( buf, sizeof(buf), thread ) );
+ }
+ if( threadID ) {
+ with_thread_info_lock({
+ ldap_debug_thread_t *t = get_thread_info( thread, msg );
+ if( thread_info_detached( t ) )
+ remove_thread_info( t, msg );
+ });
+ }
+ adjust_count( Idx_unexited_thread, -1 );
+}
+
+void
+ldap_pvt_thread_exit( void *retval )
+{
+ thread_exiting( "Exiting", "ldap_pvt_thread_exit" );
+ ldap_int_thread_exit( retval );
+}
+
+typedef struct {
+ void *(*start_routine)( void * );
+ void *arg;
+} ldap_debug_thread_call_t;
+
+static void *
+ldap_debug_thread_wrapper( void *arg )
+{
+ void *ret;
+ ldap_debug_thread_call_t call = *(ldap_debug_thread_call_t *)arg;
+ free( arg );
+ ret = call.start_routine( call.arg );
+ thread_exiting( "Returning from", "ldap_debug_thread_wrapper" );
+ return ret;
+}
+
+int
+ldap_pvt_thread_create(
+ ldap_pvt_thread_t *thread,
+ int detach,
+ void *(*start_routine)( void * ),
+ void *arg )
+{
+ int rc;
+ if( !options_done )
+ get_options();
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_create" );
+
+ if( wrap_threads ) {
+ ldap_debug_thread_call_t *call = malloc(
+ sizeof( ldap_debug_thread_call_t ) );
+ assert( call != NULL );
+ call->start_routine = start_routine;
+ call->arg = arg;
+ start_routine = ldap_debug_thread_wrapper;
+ arg = call;
+ }
+ if( threadID ) {
+ with_thread_info_lock({
+ rc = ldap_int_thread_create( thread, detach, start_routine, arg );
+ if( rc == 0 )
+ add_thread_info( "ldap_pvt_thread_create", thread, detach );
+ });
+ } else {
+ rc = ldap_int_thread_create( thread, detach, start_routine, arg );
+ }
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_create" );
+ if( wrap_threads )
+ free( arg );
+ } else {
+ if( tracethreads ) {
+ char buf[40], buf2[40];
+ fprintf( stderr,
+ "== thr_debug: Created thread %s%s from thread %s ==\n",
+ thread_name( buf, sizeof(buf), *thread ),
+ detach ? " (detached)" : "",
+ thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) );
+ }
+ adjust_count( Idx_unexited_thread, +1 );
+ if( !detach )
+ adjust_count( Idx_unjoined_thread, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
+{
+ int rc;
+ ldap_debug_thread_t *t = NULL;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_join" );
+ if( tracethreads ) {
+ char buf[40], buf2[40];
+ fprintf( stderr, "== thr_debug: Joining thread %s in thread %s ==\n",
+ thread_name( buf, sizeof(buf), thread ),
+ thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) );
+ }
+ if( threadID )
+ with_thread_info_lock( {
+ t = get_thread_info( thread, "ldap_pvt_thread_join" );
+ ERROR_IF( thread_info_detached( t ), "ldap_pvt_thread_join" );
+ } );
+ rc = ldap_int_thread_join( thread, thread_return );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_join" );
+ } else {
+ if( threadID )
+ with_thread_info_lock(
+ remove_thread_info( t, "ldap_pvt_thread_join" ) );
+ adjust_count( Idx_unjoined_thread, -1 );
+ }
+
+ return rc;
+}
+
+int
+ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_kill" );
+ if( tracethreads ) {
+ char buf[40], buf2[40];
+ fprintf( stderr,
+ "== thr_debug: Killing thread %s (sig %i) from thread %s ==\n",
+ thread_name( buf, sizeof(buf), thread ), signo,
+ thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) );
+ }
+ rc = ldap_int_thread_kill( thread, signo );
+ ERROR_IF( rc, "ldap_pvt_thread_kill" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_yield( void )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_yield" );
+ rc = ldap_int_thread_yield();
+ ERROR_IF( rc, "ldap_pvt_thread_yield" );
+ return rc;
+}
+
+ldap_pvt_thread_t
+ldap_pvt_thread_self( void )
+{
+#if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_self" );
+#endif
+ return ldap_int_thread_self();
+}
+
+int
+ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
+{
+ int rc;
+ init_usage( &cond->usage, "ldap_pvt_thread_cond_init" );
+ rc = ldap_int_thread_cond_init( WRAPPED( cond ) );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_cond_init" );
+ destroy_usage( &cond->usage );
+ } else {
+ adjust_count( Idx_cond, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cond )
+{
+ int rc;
+ check_usage( &cond->usage, "ldap_pvt_thread_cond_destroy" );
+ rc = ldap_int_thread_cond_destroy( WRAPPED( cond ) );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_cond_destroy" );
+ } else {
+ destroy_usage( &cond->usage );
+ adjust_count( Idx_cond, -1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
+{
+ int rc;
+ check_usage( &cond->usage, "ldap_pvt_thread_cond_signal" );
+ rc = ldap_int_thread_cond_signal( WRAPPED( cond ) );
+ ERROR_IF( rc, "ldap_pvt_thread_cond_signal" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond )
+{
+ int rc;
+ check_usage( &cond->usage, "ldap_pvt_thread_cond_broadcast" );
+ rc = ldap_int_thread_cond_broadcast( WRAPPED( cond ) );
+ ERROR_IF( rc, "ldap_pvt_thread_cond_broadcast" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_cond_wait(
+ ldap_pvt_thread_cond_t *cond,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ int rc;
+ ldap_int_thread_t owner;
+ check_usage( &cond->usage, "ldap_pvt_thread_cond_wait:cond" );
+ check_usage( &mutex->usage, "ldap_pvt_thread_cond_wait:mutex" );
+ adjust_count( Idx_locked_mutex, -1 );
+ owner = GET_OWNER( mutex );
+ ASSERT_OWNER( mutex, "ldap_pvt_thread_cond_wait" );
+ RESET_OWNER( mutex );
+ rc = ldap_int_thread_cond_wait( WRAPPED( cond ), WRAPPED( mutex ) );
+ ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_cond_wait" );
+ SET_OWNER( mutex, rc ? owner : ldap_int_thread_self() );
+ adjust_count( Idx_locked_mutex, +1 );
+ ERROR_IF( rc, "ldap_pvt_thread_cond_wait" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ int rc;
+ init_usage( &mutex->usage, "ldap_pvt_thread_mutex_init" );
+ rc = ldap_int_thread_mutex_init( WRAPPED( mutex ) );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_mutex_init" );
+ destroy_usage( &mutex->usage );
+ } else {
+ RESET_OWNER( mutex );
+ adjust_count( Idx_mutex, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
+{
+ int rc;
+ check_usage( &mutex->usage, "ldap_pvt_thread_mutex_destroy" );
+ ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_destroy" );
+ rc = ldap_int_thread_mutex_destroy( WRAPPED( mutex ) );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_mutex_destroy" );
+ } else {
+ destroy_usage( &mutex->usage );
+ RESET_OWNER( mutex );
+ adjust_count( Idx_mutex, -1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
+{
+ int rc;
+ check_usage( &mutex->usage, "ldap_pvt_thread_mutex_lock" );
+ rc = ldap_int_thread_mutex_lock( WRAPPED( mutex ) );
+ if( rc ) {
+ ERROR_IF( rc, "ldap_pvt_thread_mutex_lock" );
+ } else {
+ ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_lock" );
+ SET_OWNER( mutex, ldap_int_thread_self() );
+ adjust_count( Idx_locked_mutex, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex )
+{
+ int rc;
+ check_usage( &mutex->usage, "ldap_pvt_thread_mutex_trylock" );
+ rc = ldap_int_thread_mutex_trylock( WRAPPED( mutex ) );
+ if( rc == 0 ) {
+ ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_trylock" );
+ SET_OWNER( mutex, ldap_int_thread_self() );
+ adjust_count( Idx_locked_mutex, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
+{
+ int rc;
+ check_usage( &mutex->usage, "ldap_pvt_thread_mutex_unlock" );
+ ASSERT_OWNER( mutex, "ldap_pvt_thread_mutex_unlock" );
+ RESET_OWNER( mutex ); /* Breaks if this thread did not own the mutex */
+ rc = ldap_int_thread_mutex_unlock( WRAPPED( mutex ) );
+ if( rc ) {
+ ERROR_IF( rc, "ldap_pvt_thread_mutex_unlock" );
+ } else {
+ adjust_count( Idx_locked_mutex, -1 );
+ }
+ return rc;
+}
+
+
+/* Wrappers for LDAP_THREAD_RDWR_IMPLEMENTATION: */
+
+int
+ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ int rc;
+ init_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_init" );
+ rc = ldap_int_thread_rdwr_init( WRAPPED( rwlock ) );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_rdwr_init" );
+ destroy_usage( &rwlock->usage );
+ } else {
+ adjust_count( Idx_rdwr, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ int rc;
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_destroy" );
+ rc = ldap_int_thread_rdwr_destroy( WRAPPED( rwlock ) );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_rdwr_destroy" );
+ } else {
+ destroy_usage( &rwlock->usage );
+ adjust_count( Idx_rdwr, -1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ int rc;
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_rlock" );
+ rc = ldap_int_thread_rdwr_rlock( WRAPPED( rwlock ) );
+ ERROR_IF( rc, "ldap_pvt_thread_rdwr_rlock" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_rtrylock" );
+ return ldap_int_thread_rdwr_rtrylock( WRAPPED( rwlock ) );
+}
+
+int
+ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ int rc;
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_runlock" );
+ rc = ldap_int_thread_rdwr_runlock( WRAPPED( rwlock ) );
+ ERROR_IF( rc, "ldap_pvt_thread_rdwr_runlock" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ int rc;
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wlock" );
+ rc = ldap_int_thread_rdwr_wlock( WRAPPED( rwlock ) );
+ ERROR_IF( rc, "ldap_pvt_thread_rdwr_wlock" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wtrylock" );
+ return ldap_int_thread_rdwr_wtrylock( WRAPPED( rwlock ) );
+}
+
+int
+ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ int rc;
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wunlock" );
+ rc = ldap_int_thread_rdwr_wunlock( WRAPPED( rwlock ) );
+ ERROR_IF( rc, "ldap_pvt_thread_rdwr_wunlock" );
+ return rc;
+}
+
+#if defined(LDAP_RDWR_DEBUG) && !defined(LDAP_THREAD_HAVE_RDWR)
+
+int
+ldap_pvt_thread_rdwr_readers( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_readers" );
+ return ldap_int_thread_rdwr_readers( WRAPPED( rwlock ) );
+}
+
+int
+ldap_pvt_thread_rdwr_writers( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_writers" );
+ return ldap_int_thread_rdwr_writers( WRAPPED( rwlock ) );
+}
+
+int
+ldap_pvt_thread_rdwr_active( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_active" );
+ return ldap_int_thread_rdwr_active( WRAPPED( rwlock ) );
+}
+
+#endif /* LDAP_RDWR_DEBUG && !LDAP_THREAD_HAVE_RDWR */
+
+
+/* Some wrappers for LDAP_THREAD_POOL_IMPLEMENTATION: */
+#ifdef LDAP_THREAD_POOL_IMPLEMENTATION
+
+int
+ldap_pvt_thread_pool_init(
+ ldap_pvt_thread_pool_t *tpool,
+ int max_threads,
+ int max_pending )
+{
+ int rc;
+ if( !options_done )
+ get_options();
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_init" );
+ rc = ldap_int_thread_pool_init( tpool, max_threads, max_pending );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_pool_init" );
+ } else {
+ adjust_count( Idx_tpool, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_pool_submit(
+ ldap_pvt_thread_pool_t *tpool,
+ ldap_pvt_thread_start_t *start_routine, void *arg )
+{
+ int rc, has_pool;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_submit" );
+ has_pool = (tpool && *tpool);
+ rc = ldap_int_thread_pool_submit( tpool, start_routine, arg );
+ if( has_pool )
+ ERROR_IF( rc, "ldap_pvt_thread_pool_submit" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_pool_maxthreads(
+ ldap_pvt_thread_pool_t *tpool,
+ int max_threads )
+{
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_maxthreads" );
+ return ldap_int_thread_pool_maxthreads( tpool, max_threads );
+}
+
+int
+ldap_pvt_thread_pool_backload( ldap_pvt_thread_pool_t *tpool )
+{
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_backload" );
+ return ldap_int_thread_pool_backload( tpool );
+}
+
+int
+ldap_pvt_thread_pool_destroy( ldap_pvt_thread_pool_t *tpool, int run_pending )
+{
+ int rc, has_pool;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_destroy" );
+ has_pool = (tpool && *tpool);
+ rc = ldap_int_thread_pool_destroy( tpool, run_pending );
+ if( has_pool ) {
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_pool_destroy" );
+ } else {
+ adjust_count( Idx_tpool, -1 );
+ }
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_pool_pause( ldap_pvt_thread_pool_t *tpool )
+{
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_pause" );
+ return ldap_int_thread_pool_pause( tpool );
+}
+
+int
+ldap_pvt_thread_pool_resume( ldap_pvt_thread_pool_t *tpool )
+{
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_resume" );
+ return ldap_int_thread_pool_resume( tpool );
+}
+
+int
+ldap_pvt_thread_pool_getkey(
+ void *xctx,
+ void *key,
+ void **data,
+ ldap_pvt_thread_pool_keyfree_t **kfree )
+{
+#if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_getkey" );
+#endif
+ return ldap_int_thread_pool_getkey( xctx, key, data, kfree );
+}
+
+int
+ldap_pvt_thread_pool_setkey(
+ void *xctx,
+ void *key,
+ void *data,
+ ldap_pvt_thread_pool_keyfree_t *kfree,
+ void **olddatap,
+ ldap_pvt_thread_pool_keyfree_t **oldkfreep )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_setkey" );
+ rc = ldap_int_thread_pool_setkey(
+ xctx, key, data, kfree, olddatap, oldkfreep );
+ ERROR_IF( rc, "ldap_pvt_thread_pool_setkey" );
+ return rc;
+}
+
+void
+ldap_pvt_thread_pool_purgekey( void *key )
+{
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_purgekey" );
+ ldap_int_thread_pool_purgekey( key );
+}
+
+void *
+ldap_pvt_thread_pool_context( void )
+{
+#if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_context" );
+#endif
+ return ldap_int_thread_pool_context();
+}
+
+void
+ldap_pvt_thread_pool_context_reset( void *vctx )
+{
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_context_reset" );
+ ldap_int_thread_pool_context_reset( vctx );
+}
+
+#endif /* LDAP_THREAD_POOL_IMPLEMENTATION */
+
+#endif /* LDAP_THREAD_DEBUG */