--- /dev/null
+/* ldap_thr_debug.h - preprocessor magic for LDAP_THREAD_DEBUG */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005 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 the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifdef LDAP_THREAD_DEBUG
+
+/*
+ * libldap_r .c files should include this file after ldap_pvt_thread.h,
+ * with the appropriate LDAP_THREAD*_IMPLEMENTATION macro defined.
+ */
+
+#ifndef _LDAP_PVT_THREAD_H
+#error "ldap_pvt_thread.h" must be included before "ldap_thr_debug.h"
+#endif
+
+/*
+ * Support for thr_debug.c:
+ *
+ * thr_debug.c defines the ldap_pvt_*() as wrappers around
+ * ldap_int_*(), and ldap_debug_*() around ldap_int_*().
+ *
+ * Renames ldap_pvt_thread_* names to ldap_int_thread_*, and a few
+ * ldap_int_*() names to ldap_debug_*(). Includes "ldap_pvt_thread.h"
+ * to declare these renamed functions, and undefines the macros
+ * afterwards when included from thr_debug.c. So,
+ *
+ * libldap_r/<not thr_debug.c> define ldap_int_* instead of ldap_pvt_*.
+ * In thread.c, ldap_pvt_thread_<initialize/destroy>() will call
+ * ldap_debug_*() instead of ldap_int_*().
+ * In tpool.c, ldap_int_thread_pool_shutdown() has thr_debug support
+ * which treats ldap_pvt_thread_pool_destroy() the same way.
+ */
+
+#ifndef LDAP_THREAD_IMPLEMENTATION /* for first part of threads.c */
+#define ldap_int_thread_initialize ldap_debug_thread_initialize
+#define ldap_int_thread_destroy ldap_debug_thread_destroy
+#else /* LDAP_THREAD_IMPLEMENTATION -- for thr_*.c and end of threads.c */
+#undef ldap_int_thread_initialize
+#undef ldap_int_thread_destroy
+#ifdef LDAP_THREAD_DEBUG_WRAP /* see ldap_pvt_thread.h */
+#define ldap_pvt_thread_mutex_t ldap_int_thread_mutex_t
+#define ldap_pvt_thread_cond_t ldap_int_thread_cond_t
+#endif
+#define ldap_pvt_thread_sleep ldap_int_thread_sleep
+#define ldap_pvt_thread_get_concurrency ldap_int_thread_get_concurrency
+#define ldap_pvt_thread_set_concurrency ldap_int_thread_set_concurrency
+#define ldap_pvt_thread_create ldap_int_thread_create
+#define ldap_pvt_thread_exit ldap_int_thread_exit
+#define ldap_pvt_thread_join ldap_int_thread_join
+#define ldap_pvt_thread_kill ldap_int_thread_kill
+#define ldap_pvt_thread_yield ldap_int_thread_yield
+#define ldap_pvt_thread_cond_init ldap_int_thread_cond_init
+#define ldap_pvt_thread_cond_destroy ldap_int_thread_cond_destroy
+#define ldap_pvt_thread_cond_signal ldap_int_thread_cond_signal
+#define ldap_pvt_thread_cond_broadcast ldap_int_thread_cond_broadcast
+#define ldap_pvt_thread_cond_wait ldap_int_thread_cond_wait
+#define ldap_pvt_thread_mutex_init ldap_int_thread_mutex_init
+#define ldap_pvt_thread_mutex_destroy ldap_int_thread_mutex_destroy
+#define ldap_pvt_thread_mutex_lock ldap_int_thread_mutex_lock
+#define ldap_pvt_thread_mutex_trylock ldap_int_thread_mutex_trylock
+#define ldap_pvt_thread_mutex_unlock ldap_int_thread_mutex_unlock
+#define ldap_pvt_thread_self ldap_int_thread_self
+#endif /* LDAP_THREAD_IMPLEMENTATION */
+
+#ifdef LDAP_THREAD_RDWR_IMPLEMENTATION /* rdwr.c, thr_debug.c */
+#ifdef LDAP_THREAD_DEBUG_WRAP /* see ldap_pvt_thread.h */
+#define ldap_pvt_thread_rdwr_t ldap_int_thread_rdwr_t
+#endif
+#define ldap_pvt_thread_rdwr_init ldap_int_thread_rdwr_init
+#define ldap_pvt_thread_rdwr_destroy ldap_int_thread_rdwr_destroy
+#define ldap_pvt_thread_rdwr_rlock ldap_int_thread_rdwr_rlock
+#define ldap_pvt_thread_rdwr_rtrylock ldap_int_thread_rdwr_rtrylock
+#define ldap_pvt_thread_rdwr_runlock ldap_int_thread_rdwr_runlock
+#define ldap_pvt_thread_rdwr_wlock ldap_int_thread_rdwr_wlock
+#define ldap_pvt_thread_rdwr_wtrylock ldap_int_thread_rdwr_wtrylock
+#define ldap_pvt_thread_rdwr_wunlock ldap_int_thread_rdwr_wunlock
+#define ldap_pvt_thread_rdwr_readers ldap_int_thread_rdwr_readers
+#define ldap_pvt_thread_rdwr_writers ldap_int_thread_rdwr_writers
+#define ldap_pvt_thread_rdwr_active ldap_int_thread_rdwr_active
+#endif /* LDAP_THREAD_RDWR_IMPLEMENTATION */
+
+#ifdef LDAP_THREAD_POOL_IMPLEMENTATION /* tpool.c, thr_stub.c, thr_debug.c */
+#ifdef LDAP_THREAD_DEBUG_WRAP /* see ldap_pvt_thread.h */
+#define ldap_pvt_thread_pool_t ldap_int_thread_pool_t
+#endif
+#define ldap_pvt_thread_pool_init ldap_int_thread_pool_init
+#define ldap_pvt_thread_pool_submit ldap_int_thread_pool_submit
+#define ldap_pvt_thread_pool_maxthreads ldap_int_thread_pool_maxthreads
+#define ldap_pvt_thread_pool_backload ldap_int_thread_pool_backload
+#define ldap_pvt_thread_pool_pause ldap_int_thread_pool_pause
+#define ldap_pvt_thread_pool_resume ldap_int_thread_pool_resume
+#define ldap_pvt_thread_pool_destroy ldap_int_thread_pool_destroy
+#define ldap_pvt_thread_pool_getkey ldap_int_thread_pool_getkey
+#define ldap_pvt_thread_pool_setkey ldap_int_thread_pool_setkey
+#define ldap_pvt_thread_pool_purgekey ldap_int_thread_pool_purgekey
+#define ldap_pvt_thread_pool_context ldap_int_thread_pool_context
+#define ldap_pvt_thread_pool_context_reset ldap_int_thread_pool_context_reset
+#endif /* LDAP_THREAD_POOL_IMPLEMENTATION */
+
+#undef _LDAP_PVT_THREAD_H
+#include "ldap_pvt_thread.h"
+
+#ifdef LDAP_THREAD_POOL_IMPLEMENTATION /* tpool.c */
+/*
+ * tpool.c:ldap_int_thread_pool_shutdown() needs this. Could not
+ * use it for ldap_pvt_thread.h above because of its use of LDAP_P().
+ */
+#undef ldap_pvt_thread_pool_destroy
+#define ldap_pvt_thread_pool_destroy(p,r) ldap_int_thread_pool_destroy(p,r)
+#endif
+
+#ifdef LDAP_THREAD_DEBUG_IMPLEMENTATION /* thr_debug.c */
+#undef ldap_pvt_thread_mutex_t
+#undef ldap_pvt_thread_cond_t
+#undef ldap_pvt_thread_sleep
+#undef ldap_pvt_thread_get_concurrency
+#undef ldap_pvt_thread_set_concurrency
+#undef ldap_pvt_thread_create
+#undef ldap_pvt_thread_exit
+#undef ldap_pvt_thread_join
+#undef ldap_pvt_thread_kill
+#undef ldap_pvt_thread_yield
+#undef ldap_pvt_thread_cond_init
+#undef ldap_pvt_thread_cond_destroy
+#undef ldap_pvt_thread_cond_signal
+#undef ldap_pvt_thread_cond_broadcast
+#undef ldap_pvt_thread_cond_wait
+#undef ldap_pvt_thread_mutex_init
+#undef ldap_pvt_thread_mutex_destroy
+#undef ldap_pvt_thread_mutex_lock
+#undef ldap_pvt_thread_mutex_trylock
+#undef ldap_pvt_thread_mutex_unlock
+#undef ldap_pvt_thread_self
+/* LDAP_THREAD_RDWR_IMPLEMENTATION: */
+#undef ldap_pvt_thread_rdwr_t
+#undef ldap_pvt_thread_rdwr_init
+#undef ldap_pvt_thread_rdwr_destroy
+#undef ldap_pvt_thread_rdwr_rlock
+#undef ldap_pvt_thread_rdwr_rtrylock
+#undef ldap_pvt_thread_rdwr_runlock
+#undef ldap_pvt_thread_rdwr_wlock
+#undef ldap_pvt_thread_rdwr_wtrylock
+#undef ldap_pvt_thread_rdwr_wunlock
+#undef ldap_pvt_thread_rdwr_readers
+#undef ldap_pvt_thread_rdwr_writers
+#undef ldap_pvt_thread_rdwr_active
+/* LDAP_THREAD_POOL_IMPLEMENTATION: */
+#undef ldap_pvt_thread_pool_t
+#undef ldap_pvt_thread_pool_init
+#undef ldap_pvt_thread_pool_submit
+#undef ldap_pvt_thread_pool_maxthreads
+#undef ldap_pvt_thread_pool_backload
+#undef ldap_pvt_thread_pool_pause
+#undef ldap_pvt_thread_pool_resume
+#undef ldap_pvt_thread_pool_destroy
+#undef ldap_pvt_thread_pool_getkey
+#undef ldap_pvt_thread_pool_setkey
+#undef ldap_pvt_thread_pool_purgekey
+#undef ldap_pvt_thread_pool_context
+#undef ldap_pvt_thread_pool_context_reset
+#endif /* LDAP_THREAD_DEBUG_IMPLEMENTATION */
+
+#endif /* LDAP_THREAD_DEBUG */
--- /dev/null
+/* 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 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 three types of thread operation debugging:
+ *
+ * - Print error messages and abort() when thread operations fail:
+ * Operations on threads, mutexes, condition variables, rdwr locks.
+ * Some thread pool operations are also checked, but not those for
+ * which failure can happen in normal slapd operation.
+ *
+ * - Wrap those types except threads and pools in structs that
+ * contain a state variable or a pointer to dummy allocated memory,
+ * and check that on all operations. The dummy memory variant lets
+ * malloc debuggers see some incorrect use as memory leaks, access
+ * to freed memory, etc.
+ *
+ * - 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.
+ *
+ * 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: Environment variable LDAP_THREAD_DEBUG.
+ *
+ * The variable may contain a comma- or space-separated option list.
+ * Options:
+ * off - Disable this package.
+ * Error checking:
+ * noabort - Do not abort() on errors.
+ * noerror - Do not report errors. Implies noabort.
+ * nocount - Do not report counts of unreleased resources.
+ * State variable/dummy memory, unless type wrapping is disabled:
+ * noalloc - Default. Use a state variable, not dummy memory.
+ * dupinit - Implies noalloc. Check if resources that have
+ * not been destroyed are reinitialized. Tools that
+ * report uninitialized memory access should disable
+ * such warnings about debug_already_initialized().
+ * alloc - Allocate dummy memory and store pointers as-is.
+ * Malloc debuggers might not notice unreleased
+ * resources in global variables as memory leaks.
+ * 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.
+ * Tracing:
+ * tracethreads - Report create/join/exit/kill of threads.
+ */
+
+#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 */
+
+
+/* Options from environment variable $LDAP_THREAD_DEBUG */
+enum { Count_no = 0, Count_yes, Count_reported, Count_reported_more };
+static int nodebug, noabort, noerror, count = Count_yes, options_done;
+#ifdef LDAP_THREAD_DEBUG_WRAP
+enum { Wrap_noalloc, Wrap_alloc, Wrap_scramble, Wrap_adjptr };
+static int dupinit, wraptype = Wrap_noalloc, wrap_offset, unwrap_offset;
+#endif
+static int tracethreads;
+
+static int threading_enabled;
+
+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];
+
+
+/*
+ * Making ldap_pvt_thread_t a wrapper around ldap_int_thread_t would
+ * slow down ldap_pvt_thread_self(), so keep a list of threads instead.
+ */
+typedef struct ldap_debug_thread_s {
+ ldap_pvt_thread_t wrapped;
+ ldap_debug_usage_info_t usage;
+ int detached;
+ int freeme, 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 WARN(var, msg) (warn (__FILE__, __LINE__, (msg), #var, (var)))
+#define ERROR(var,msg) (error(__FILE__, __LINE__, (msg), #var, (var)))
+#define WARN_IF(rc, msg) {if (rc) warn (__FILE__, __LINE__, (msg), #rc, (rc));}
+#define ERROR_IF(rc,msg) {if (rc) error(__FILE__, __LINE__, (msg), #rc, (rc));}
+
+#if 0
+static void
+warn( const char *file, int line, const char *msg, const char *var, int val )
+{
+ fprintf( stderr, "%s:%d: %s warning: %s is %d\n",
+ file, line, msg, var, val );
+}
+#endif
+
+static void
+error( const char *file, int line, const char *msg, const char *var, int val )
+{
+ if( !noerror ) {
+ fprintf( stderr, "%s:%d: %s error: %s is %d\n",
+ file, line, msg, var, val );
+ if( !noabort )
+ abort();
+ }
+}
+
+static void
+count_resource_leaks( void )
+{
+ int i, j;
+ char errbuf[200], *delim = "Leaked";
+ 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, "%s %d %s",
+ delim, resource_counts[i], resource_names[i] );
+ delim = ",";
+ }
+ }
+ if( j )
+ fprintf( stderr, "%s:%d: %s.\n", __FILE__, __LINE__, errbuf );
+ }
+}
+
+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 },
+#ifdef LDAP_THREAD_DEBUG_WRAP
+ { "noalloc", &wraptype, Wrap_noalloc },
+ { "dupinit", &dupinit, 1 },
+ { "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;
+ for( oi = option_info; oi->name; oi++ ) {
+ if( strncasecmp( oi->name, s, optlen ) == 0 ) {
+ if( oi->name && oi->name[optlen] == '\0' ) {
+ *oi->var = oi->val;
+ } else {
+ fprintf( stderr, "Unknown $%s option '%.*s'\n",
+ "LDAP_THREAD_DEBUG", (int) optlen, s );
+ }
+ break;
+ }
+ }
+ s += optlen;
+ }
+ }
+ if( nodebug ) {
+ noabort = noerror = 1;
+ tracethreads = dupinit = 0;
+ count = Count_no;
+ }
+#ifdef LDAP_THREAD_DEBUG_WRAP
+ if( nodebug || dupinit ) {
+ wraptype = Wrap_noalloc;
+ } else if( wraptype == Wrap_scramble ) {
+ const unsigned char *dummy = (const unsigned char *)&option_info;
+ if( sizeof(LDAP_UINTPTR_T) < sizeof(void *)
+ || (unsigned char *)~~(LDAP_UINTPTR_T) dummy != dummy
+ || (unsigned char *)~~(LDAP_UINTPTR_T) (unsigned char *)0 )
+ {
+ fprintf( stderr, "Misconfigured for $%s %s. Using %s.\n",
+ "LDAP_THREAD_DEBUG", "scramble", "adjptr" );
+ wraptype = Wrap_adjptr;
+ }
+ }
+ unwrap_offset = -(wrap_offset = (wraptype == Wrap_adjptr));
+#endif
+ options_done = 1;
+}
+
+
+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;
+}
+
+static void
+exit_thread_message( const ldap_pvt_thread_t thread )
+{
+ if( tracethreads ) {
+ char buf[40];
+ fprintf( stderr, "== Exiting thread %s ==\n",
+ thread_name( buf, sizeof(buf), thread ) );
+ }
+}
+
+
+#ifndef LDAP_THREAD_DEBUG_WRAP
+
+#define WRAPPED(ptr) (ptr)
+#define alloc_usage(ptr, msg) ((void) 0)
+#define check_usage(ptr, msg) ((void) 0)
+#define free_usage(ptr, msg) ((void) 0)
+
+#define with_threads_lock(statement) { statement; }
+#define get_new_thread_info(msg) NULL
+#define update_thread_info(ti, th, det) {}
+#define remove_thread_info(ti, msg) ((void)0)
+#define get_thread_info(thread, msg) NULL
+#define exiting_thread(msg) exit_thread_message(ldap_pvt_thread_self())
+
+#else /* LDAP_THREAD_DEBUG_WRAP */
+
+#define WRAPPED(ptr) (&(ptr)->wrapped)
+
+#define INITED_VALUE 0x12345678UL
+#define INITED_BYTE_VALUE 0xd5
+
+static int
+debug_already_initialized( const LDAP_UINTPTR_T *num )
+{
+ /* Valid programs will access uninitialized memory if dupinit */
+ return dupinit && *num == INITED_VALUE;
+}
+
+static void
+alloc_usage( ldap_debug_usage_info_t *usage, const char *msg )
+{
+ if( !options_done )
+ get_options();
+ if( wraptype == Wrap_noalloc ) {
+ ERROR_IF( debug_already_initialized( &usage->num ), msg );
+ usage->num = INITED_VALUE;
+ } else {
+ unsigned char *dummy = malloc( 1 );
+ assert( dummy != NULL );
+ *dummy = INITED_BYTE_VALUE;
+ if( wraptype == Wrap_scramble ) {
+ usage->num = ~(LDAP_UINTPTR_T) dummy;
+ assert( (unsigned char *)~usage->num == dummy );
+ } else {
+ usage->ptr = dummy + wrap_offset;
+ }
+ }
+}
+
+static void
+check_usage( ldap_debug_usage_info_t *usage, const char *msg )
+{
+ if( wraptype == Wrap_noalloc ) {
+ ERROR_IF( usage->num != INITED_VALUE, msg );
+ } else if( wraptype == Wrap_scramble ) {
+ ERROR_IF( !usage->num, msg );
+ ERROR_IF( *(unsigned char *)~usage->num != INITED_BYTE_VALUE, msg );
+ } else {
+ ERROR_IF( !usage->ptr, msg );
+ ERROR_IF( usage->ptr[unwrap_offset] != INITED_BYTE_VALUE, msg );
+ }
+}
+
+static void
+free_usage( ldap_debug_usage_info_t *usage, const char *msg )
+{
+ if( wraptype == Wrap_noalloc ) {
+ usage->num = ~(LDAP_UINTPTR_T)INITED_VALUE;
+ } else {
+ unsigned char *dummy = (wraptype == Wrap_scramble
+ ? (unsigned char *)~usage->num
+ : usage->ptr + unwrap_offset);
+ *(volatile unsigned char *)dummy = (unsigned char)-1;
+ free( dummy );
+ }
+}
+
+#define with_threads_lock(statement) { \
+ if( !nodebug ) { \
+ int rc_wtl_ = ldap_int_thread_mutex_lock( &thread_info_mutex ); \
+ assert( rc_wtl_ == 0 ); \
+ } \
+ statement; \
+ if( !nodebug ) { \
+ int rc_wtl_ = ldap_int_thread_mutex_unlock( &thread_info_mutex ); \
+ assert( rc_wtl_ == 0 ); \
+ } \
+}
+
+static ldap_debug_thread_t *
+get_new_thread_info( const char *msg )
+{
+ if( nodebug )
+ return NULL;
+ if( thread_info_used >= thread_info_size ) {
+ unsigned int more = thread_info_size + 1; /* debug value. increase. */
+ unsigned int new_size = thread_info_size + more;
+ ldap_debug_thread_t *t = calloc( more, sizeof(ldap_debug_thread_t) );
+ assert( t != NULL );
+ t->freeme = 1;
+ thread_info = realloc( thread_info, new_size * sizeof(*thread_info) );
+ assert( thread_info != NULL );
+ while( thread_info_size < new_size ) {
+ t->idx = thread_info_size;
+ thread_info[thread_info_size++] = t++;
+ }
+ }
+ alloc_usage( &thread_info[thread_info_used]->usage, msg );
+ return thread_info[thread_info_used++];
+}
+
+static void
+update_thread_info(
+ ldap_debug_thread_t *t,
+ const ldap_pvt_thread_t *thread,
+ int detached )
+{
+ if( !nodebug ) {
+ t->wrapped = *thread;
+ t->detached = detached;
+ }
+}
+
+static void
+remove_thread_info( ldap_debug_thread_t *t, const char *msg )
+{
+ if( !nodebug ) {
+ ldap_debug_thread_t *last;
+ int idx;
+ free_usage( &t->usage, msg );
+ 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;
+ }
+}
+
+ldap_debug_thread_t *
+get_thread_info( ldap_pvt_thread_t *thread, const char *msg )
+{
+ unsigned int i;
+ ldap_debug_thread_t *t;
+ if( nodebug )
+ return NULL;
+ 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;
+}
+
+static void
+exiting_thread( const char *msg )
+{
+ if( !nodebug ) {
+ ldap_pvt_thread_t thread;
+ thread = ldap_pvt_thread_self();
+ exit_thread_message( thread );
+ with_threads_lock({
+ ldap_debug_thread_t *t = get_thread_info( &thread, msg );
+ if( t->detached )
+ remove_thread_info( t, msg );
+ });
+ }
+}
+
+#endif /* LDAP_THREAD_DEBUG_WRAP */
+
+
+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 );
+ case Count_reported:
+ fputs( "...more ldap_debug_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 {
+ rc2 = ldap_int_thread_mutex_init( &thread_info_mutex );
+ assert( rc2 == 0 );
+ 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;
+}
+
+int
+ldap_pvt_thread_create(
+ ldap_pvt_thread_t *thread,
+ int detach,
+ void *(*start_routine)( void * ),
+ void *arg )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_create" );
+ if( !options_done )
+ get_options();
+ with_threads_lock({
+ ldap_debug_thread_t *t;
+ t = get_new_thread_info( "ldap_pvt_thread_create" );
+ rc = ldap_int_thread_create( thread, detach, start_routine, arg );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_create" );
+ remove_thread_info( t, "ldap_pvt_thread_init" );
+ } else {
+ update_thread_info( t, thread, detach );
+ }
+ });
+ if( rc == 0 ) {
+ if( tracethreads ) {
+ char buf[40];
+ fprintf( stderr, "== Created thread %s%s ==\n",
+ thread_name( buf, sizeof(buf), *thread ),
+ detach ? " (detached)" : "" );
+ }
+ adjust_count( Idx_unexited_thread, +1 );
+ if( !detach )
+ adjust_count( Idx_unjoined_thread, +1 );
+ }
+ return rc;
+}
+
+void
+ldap_pvt_thread_exit( void *retval )
+{
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_exit" );
+ adjust_count( Idx_unexited_thread, -1 );
+ exiting_thread( "ldap_pvt_thread_exit" );
+ ldap_int_thread_exit( retval );
+}
+
+int
+ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
+{
+ int rc;
+ ldap_debug_thread_t *t;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_join" );
+ if( tracethreads ) {
+ char buf[40];
+ fprintf( stderr, "== Joining thread %s ==\n",
+ thread_name( buf, sizeof(buf), thread ) );
+ }
+ with_threads_lock(
+ t = get_thread_info( &thread, "ldap_pvt_thread_join" ) );
+ rc = ldap_int_thread_join( thread, thread_return );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_join" );
+ } else {
+ with_threads_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];
+ fprintf( stderr, "== Killing thread %s (sig %i) ==\n",
+ thread_name( buf, sizeof(buf), thread ), signo );
+ }
+ 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;
+ alloc_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" );
+ free_usage( &cond->usage, "ldap_pvt_thread_cond_init" );
+ } 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 {
+ free_usage( &cond->usage, "ldap_pvt_thread_cond_destroy" );
+ 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;
+ 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 );
+ rc = ldap_int_thread_cond_wait( WRAPPED( cond ), WRAPPED( mutex ) );
+ 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;
+ alloc_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" );
+ free_usage( &mutex->usage, "ldap_pvt_thread_mutex_init" );
+ } else {
+ 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" );
+ rc = ldap_int_thread_mutex_destroy( WRAPPED( mutex ) );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_mutex_destroy" );
+ } else {
+ free_usage( &mutex->usage, "ldap_pvt_thread_mutex_destroy" );
+ 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 {
+ 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 )
+ 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" );
+ 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;
+ alloc_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" );
+ free_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_init" );
+ } 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 {
+ free_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_destroy" );
+ 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;
+}
+
+#ifdef LDAP_RDWR_DEBUG
+
+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 */
+
+
+/* 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;
+ 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 != NULL && *tpool != NULL);
+ 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 != NULL && *tpool != NULL);
+ 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 )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_setkey" );
+ rc = ldap_int_thread_pool_setkey( xctx, key, data, kfree );
+ 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 */