mirror of
https://git.openldap.org/openldap/openldap.git
synced 2024-12-21 03:10:25 +08:00
1339 lines
36 KiB
C
1339 lines
36 KiB
C
/* 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 synchronization 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 achieves 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 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_recursive_init( ldap_pvt_thread_mutex_t *mutex )
|
|
{
|
|
int rc;
|
|
init_usage( &mutex->usage, "ldap_pvt_thread_mutex_recursive_init" );
|
|
rc = ldap_int_thread_mutex_recursive_init( WRAPPED( mutex ) );
|
|
if( rc ) {
|
|
ERROR( rc, "ldap_pvt_thread_mutex_recursive_init" );
|
|
destroy_usage( &mutex->usage );
|
|
} else {
|
|
RESET_OWNER( mutex );
|
|
adjust_count( Idx_mutex, +1 );
|
|
}
|
|
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_close( ldap_pvt_thread_pool_t *tpool, int run_pending )
|
|
{
|
|
int rc, has_pool;
|
|
ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_close" );
|
|
has_pool = (tpool && *tpool);
|
|
rc = ldap_int_thread_pool_close( tpool, run_pending );
|
|
if( has_pool && rc ) {
|
|
ERROR( rc, "ldap_pvt_thread_pool_close" );
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
ldap_pvt_thread_pool_free( ldap_pvt_thread_pool_t *tpool )
|
|
{
|
|
int rc, has_pool;
|
|
ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_free" );
|
|
has_pool = (tpool && *tpool);
|
|
rc = ldap_int_thread_pool_free( tpool );
|
|
if( has_pool ) {
|
|
if( rc ) {
|
|
ERROR( rc, "ldap_pvt_thread_pool_free" );
|
|
} 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 */
|