summaryrefslogtreecommitdiffstats
path: root/libraries/libldap_r
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 11:11:40 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 11:11:40 +0000
commit7731832751ab9f3c6ddeb66f186d3d7fa1934a6d (patch)
treee91015872543a59be2aad26c2fea02e41b57005d /libraries/libldap_r
parentInitial commit. (diff)
downloadopenldap-7731832751ab9f3c6ddeb66f186d3d7fa1934a6d.tar.xz
openldap-7731832751ab9f3c6ddeb66f186d3d7fa1934a6d.zip
Adding upstream version 2.4.57+dfsg.upstream/2.4.57+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libraries/libldap_r')
-rw-r--r--libraries/libldap_r/Makefile.in88
-rw-r--r--libraries/libldap_r/ldap_thr_debug.h191
-rw-r--r--libraries/libldap_r/rdwr.c458
-rw-r--r--libraries/libldap_r/rmutex.c219
-rw-r--r--libraries/libldap_r/rq.c221
-rw-r--r--libraries/libldap_r/thr_cthreads.c180
-rw-r--r--libraries/libldap_r/thr_debug.c1292
-rw-r--r--libraries/libldap_r/thr_nt.c245
-rw-r--r--libraries/libldap_r/thr_posix.c388
-rw-r--r--libraries/libldap_r/thr_pth.c231
-rw-r--r--libraries/libldap_r/thr_stub.c305
-rw-r--r--libraries/libldap_r/thr_thr.c186
-rw-r--r--libraries/libldap_r/threads.c113
-rw-r--r--libraries/libldap_r/tpool.c1032
14 files changed, 5149 insertions, 0 deletions
diff --git a/libraries/libldap_r/Makefile.in b/libraries/libldap_r/Makefile.in
new file mode 100644
index 0000000..85eff3d
--- /dev/null
+++ b/libraries/libldap_r/Makefile.in
@@ -0,0 +1,88 @@
+# Makefile.in for LDAP -lldap
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-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 the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+LIBRARY = libldap_r.la
+
+PROGRAMS = apitest ltest
+
+XXDIR = $(srcdir)/../libldap
+XXSRCS = apitest.c test.c \
+ bind.c open.c result.c error.c compare.c search.c \
+ controls.c messages.c references.c extended.c cyrus.c \
+ modify.c add.c modrdn.c delete.c abandon.c \
+ sasl.c gssapi.c sbind.c unbind.c cancel.c \
+ filter.c free.c sort.c passwd.c whoami.c \
+ getdn.c getentry.c getattr.c getvalues.c addentry.c \
+ request.c os-ip.c url.c pagectrl.c sortctrl.c vlvctrl.c \
+ init.c options.c print.c string.c util-int.c schema.c \
+ charray.c os-local.c dnssrv.c utf-8.c utf-8-conv.c \
+ tls2.c tls_o.c tls_g.c tls_m.c \
+ turn.c ppolicy.c dds.c txn.c ldap_sync.c stctrl.c \
+ assertion.c deref.c ldif.c fetch.c
+SRCS = threads.c rdwr.c rmutex.c tpool.c rq.c \
+ thr_posix.c thr_cthreads.c thr_thr.c thr_nt.c \
+ thr_pth.c thr_stub.c thr_debug.c
+OBJS = threads.lo rdwr.lo rmutex.lo tpool.lo rq.lo \
+ thr_posix.lo thr_cthreads.lo thr_thr.lo thr_nt.lo \
+ thr_pth.lo thr_stub.lo thr_debug.lo \
+ bind.lo open.lo result.lo error.lo compare.lo search.lo \
+ controls.lo messages.lo references.lo extended.lo cyrus.lo \
+ modify.lo add.lo modrdn.lo delete.lo abandon.lo \
+ sasl.lo gssapi.lo sbind.lo unbind.lo cancel.lo \
+ filter.lo free.lo sort.lo passwd.lo whoami.lo \
+ getdn.lo getentry.lo getattr.lo getvalues.lo addentry.lo \
+ request.lo os-ip.lo url.lo pagectrl.lo sortctrl.lo vlvctrl.lo \
+ init.lo options.lo print.lo string.lo util-int.lo schema.lo \
+ charray.lo os-local.lo dnssrv.lo utf-8.lo utf-8-conv.lo \
+ tls2.lo tls_o.lo tls_g.lo tls_m.lo \
+ turn.lo ppolicy.lo dds.lo txn.lo ldap_sync.lo stctrl.lo \
+ assertion.lo deref.lo ldif.lo fetch.lo
+
+LDAP_INCDIR= ../../include
+LDAP_LIBDIR= ../../libraries
+
+LIB_DEFS = -DLDAP_LIBRARY
+
+XDEFS = -DLDAP_R_COMPILE -I$(XXDIR)
+XLIBS = $(LIBRARY) $(LDAP_LIBLBER_LA) $(LDAP_LIBLUTIL_A)
+XXLIBS = $(SECURITY_LIBS) $(LUTIL_LIBS)
+XXXLIBS = $(LTHREAD_LIBS)
+NT_LINK_LIBS = $(LDAP_LIBLBER_LA) $(AC_LIBS) $(SECURITY_LIBS)
+UNIX_LINK_LIBS = $(LDAP_LIBLBER_LA) $(AC_LIBS) $(SECURITY_LIBS) $(LTHREAD_LIBS)
+
+.links : Makefile
+ @for i in $(XXSRCS); do \
+ $(RM) $$i ; \
+ $(LN_S) $(XXDIR)/$$i . ; \
+ done
+ touch .links
+
+$(XXSRCS) : .links
+
+clean-local: FORCE
+ @$(RM) .links
+
+depend-common: .links
+
+apitest: $(XLIBS) apitest.o
+ $(LTLINK) -o $@ apitest.o $(LIBS)
+ltest: $(XLIBS) test.o
+ $(LTLINK) -o $@ test.o $(LIBS)
+
+install-local: $(CFFILES) FORCE
+ -$(MKDIR) $(DESTDIR)$(libdir)
+ $(LTINSTALL) $(INSTALLFLAGS) -m 644 $(LIBRARY) $(DESTDIR)$(libdir)
+ $(LTFINISH) $(DESTDIR)$(libdir)
+
diff --git a/libraries/libldap_r/ldap_thr_debug.h b/libraries/libldap_r/ldap_thr_debug.h
new file mode 100644
index 0000000..6bf9314
--- /dev/null
+++ b/libraries/libldap_r/ldap_thr_debug.h
@@ -0,0 +1,191 @@
+/* ldap_thr_debug.h - preprocessor magic for LDAP_THREAD_DEBUG */
+/* $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 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(s) 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 ldap_pvt_thread_* as wrappers around the real
+ * ldap_pvt_thread_* implementation, which this file renames to
+ * ldap_int_thread_*.
+ *
+ * Implementation:
+ *
+ * This file re#defines selected ldap_pvt_thread_* names to
+ * ldap_int_thread_*, which will be used from wrappers in thr_debug.c.
+ * Two ldap_int_*() calls are redirected to call ldap_debug_*(): These
+ * are wrappers around the originals, whose definitions are not renamed.
+ * This file then #includes ldap_pvt_thread.h to declare the renamed
+ * functions/types. If #included from thr_debug.c it finally #undefines
+ * the macros again.
+ *
+ * include/ldap_pvt_thread.h declares the typedefs ldap_pvt_thread*_t as
+ * either wrapper types ldap_debug_thread*_t or their usual definitions
+ * ldap_int_thread*_t, depending on the LDAP_THREAD_DEBUG_WRAP option.
+ * When defining the underlying implementation, this file then redirects
+ * the type names back to the original ldap_int_thread*_t types.
+ * include/ldap_<int,pvt>_thread.h also do some thr_debug magic.
+ *
+ * So,
+ * libldap_r/<not thr_debug.c> thus define ldap_int_thread_*() instead
+ * of ldap_pvt_thread_*().
+ * thr_debug.c defines the ldap_pvt_*() and ldap_debug_*() functions.
+ * In thread.c, ldap_pvt_thread_<initialize/destroy>() will call
+ * ldap_debug_thread_*() instead of ldap_int_thread_*().
+ * In tpool.c, ldap_int_thread_pool_shutdown() has explicit thr_debug.c
+ * 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 */
diff --git a/libraries/libldap_r/rdwr.c b/libraries/libldap_r/rdwr.c
new file mode 100644
index 0000000..424e83e
--- /dev/null
+++ b/libraries/libldap_r/rdwr.c
@@ -0,0 +1,458 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-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 work was initially developed by Kurt D. Zeilenga for inclusion
+ * in OpenLDAP Software. Additional significant contributors include:
+ * Stuart Lynne
+ */
+
+/*
+ * This is an improved implementation of Reader/Writer locks does
+ * not protect writers from starvation. That is, if a writer is
+ * currently waiting on a reader, any new reader will get
+ * the lock before the writer.
+ *
+ * Does not support cancellation nor does any status checking.
+ */
+/* Adapted from publically available examples for:
+ * "Programming with Posix Threads"
+ * by David R Butenhof, Addison-Wesley
+ * http://cseng.aw.com/bookpage.taf?ISBN=0-201-63392-2
+ */
+
+#include "portable.h"
+
+#include <ac/stdlib.h>
+
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_RDWR_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+
+/*
+ * implementations that provide their own compatible
+ * reader/writer locks define LDAP_THREAD_HAVE_RDWR
+ * in ldap_pvt_thread.h
+ */
+#ifndef LDAP_THREAD_HAVE_RDWR
+
+struct ldap_int_thread_rdwr_s {
+ ldap_pvt_thread_mutex_t ltrw_mutex;
+ ldap_pvt_thread_cond_t ltrw_read; /* wait for read */
+ ldap_pvt_thread_cond_t ltrw_write; /* wait for write */
+ int ltrw_valid;
+#define LDAP_PVT_THREAD_RDWR_VALID 0x0bad
+ int ltrw_r_active;
+ int ltrw_w_active;
+ int ltrw_r_wait;
+ int ltrw_w_wait;
+#ifdef LDAP_RDWR_DEBUG
+ /* keep track of who has these locks */
+#define MAX_READERS 32
+ int ltrw_more_readers; /* Set if ltrw_readers[] is incomplete */
+ ldap_pvt_thread_t ltrw_readers[MAX_READERS];
+ ldap_pvt_thread_t ltrw_writer;
+#endif
+};
+
+int
+ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+
+ rw = (struct ldap_int_thread_rdwr_s *) LDAP_CALLOC( 1,
+ sizeof( struct ldap_int_thread_rdwr_s ) );
+ if ( !rw )
+ return LDAP_NO_MEMORY;
+
+ /* we should check return results */
+ ldap_pvt_thread_mutex_init( &rw->ltrw_mutex );
+ ldap_pvt_thread_cond_init( &rw->ltrw_read );
+ ldap_pvt_thread_cond_init( &rw->ltrw_write );
+
+ rw->ltrw_valid = LDAP_PVT_THREAD_RDWR_VALID;
+
+ *rwlock = rw;
+ return 0;
+}
+
+int
+ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+
+ if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
+
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ /* active threads? */
+ if( rw->ltrw_r_active > 0 || rw->ltrw_w_active > 0) {
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+ return LDAP_PVT_THREAD_EBUSY;
+ }
+
+ /* waiting threads? */
+ if( rw->ltrw_r_wait > 0 || rw->ltrw_w_wait > 0) {
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+ return LDAP_PVT_THREAD_EBUSY;
+ }
+
+ rw->ltrw_valid = 0;
+
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+
+ ldap_pvt_thread_mutex_destroy( &rw->ltrw_mutex );
+ ldap_pvt_thread_cond_destroy( &rw->ltrw_read );
+ ldap_pvt_thread_cond_destroy( &rw->ltrw_write );
+
+ LDAP_FREE(rw);
+ *rwlock = NULL;
+ return 0;
+}
+
+int ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+
+ if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
+
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ if( rw->ltrw_w_active > 0 ) {
+ /* writer is active */
+
+ rw->ltrw_r_wait++;
+
+ do {
+ ldap_pvt_thread_cond_wait(
+ &rw->ltrw_read, &rw->ltrw_mutex );
+ } while( rw->ltrw_w_active > 0 );
+
+ rw->ltrw_r_wait--;
+ assert( rw->ltrw_r_wait >= 0 );
+ }
+
+#ifdef LDAP_RDWR_DEBUG
+ if( rw->ltrw_r_active < MAX_READERS )
+ rw->ltrw_readers[rw->ltrw_r_active] = ldap_pvt_thread_self();
+ else
+ rw->ltrw_more_readers = 1;
+#endif
+ rw->ltrw_r_active++;
+
+
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+
+ return 0;
+}
+
+int ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+
+ if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
+
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ if( rw->ltrw_w_active > 0) {
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+ return LDAP_PVT_THREAD_EBUSY;
+ }
+
+#ifdef LDAP_RDWR_DEBUG
+ if( rw->ltrw_r_active < MAX_READERS )
+ rw->ltrw_readers[rw->ltrw_r_active] = ldap_pvt_thread_self();
+ else
+ rw->ltrw_more_readers = 1;
+#endif
+ rw->ltrw_r_active++;
+
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+
+ return 0;
+}
+
+int ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+
+ if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
+
+ rw->ltrw_r_active--;
+#ifdef LDAP_RDWR_DEBUG
+ /* Remove us from the list of readers */
+ {
+ ldap_pvt_thread_t self = ldap_pvt_thread_self();
+ int i, j;
+ for( i = j = rw->ltrw_r_active; i >= 0; i--) {
+ if (rw->ltrw_readers[i] == self) {
+ rw->ltrw_readers[i] = rw->ltrw_readers[j];
+ rw->ltrw_readers[j] = 0;
+ break;
+ }
+ }
+ if( !rw->ltrw_more_readers )
+ assert( i >= 0 );
+ else if( j == 0 )
+ rw->ltrw_more_readers = 0;
+ }
+#endif
+
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ if (rw->ltrw_r_active == 0 && rw->ltrw_w_wait > 0 ) {
+ ldap_pvt_thread_cond_signal( &rw->ltrw_write );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+
+ return 0;
+}
+
+int ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+
+ if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
+
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ if ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 ) {
+ rw->ltrw_w_wait++;
+
+ do {
+ ldap_pvt_thread_cond_wait(
+ &rw->ltrw_write, &rw->ltrw_mutex );
+ } while ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 );
+
+ rw->ltrw_w_wait--;
+ assert( rw->ltrw_w_wait >= 0 );
+ }
+
+#ifdef LDAP_RDWR_DEBUG
+ rw->ltrw_writer = ldap_pvt_thread_self();
+#endif
+ rw->ltrw_w_active++;
+
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+
+ return 0;
+}
+
+int ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+
+ if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
+
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ if ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 ) {
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+ return LDAP_PVT_THREAD_EBUSY;
+ }
+
+#ifdef LDAP_RDWR_DEBUG
+ rw->ltrw_writer = ldap_pvt_thread_self();
+#endif
+ rw->ltrw_w_active++;
+
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+
+ return 0;
+}
+
+int ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+
+ if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
+
+ rw->ltrw_w_active--;
+
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ if (rw->ltrw_r_wait > 0) {
+ ldap_pvt_thread_cond_broadcast( &rw->ltrw_read );
+
+ } else if (rw->ltrw_w_wait > 0) {
+ ldap_pvt_thread_cond_signal( &rw->ltrw_write );
+ }
+
+#ifdef LDAP_RDWR_DEBUG
+ assert( rw->ltrw_writer == ldap_pvt_thread_self() );
+ rw->ltrw_writer = 0;
+#endif
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+
+ return 0;
+}
+
+#ifdef LDAP_RDWR_DEBUG
+
+/* just for testing,
+ * return 0 if false, suitable for assert(ldap_pvt_thread_rdwr_Xchk(rdwr))
+ *
+ * Currently they don't check if the calling thread is the one
+ * that has the lock, just that there is a reader or writer.
+ *
+ * Basically sufficent for testing that places that should have
+ * a lock are caught.
+ */
+
+int ldap_pvt_thread_rdwr_readers(ldap_pvt_thread_rdwr_t *rwlock)
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ return( rw->ltrw_r_active );
+}
+
+int ldap_pvt_thread_rdwr_writers(ldap_pvt_thread_rdwr_t *rwlock)
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ return( rw->ltrw_w_active );
+}
+
+int ldap_pvt_thread_rdwr_active(ldap_pvt_thread_rdwr_t *rwlock)
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ return(ldap_pvt_thread_rdwr_readers(rwlock) +
+ ldap_pvt_thread_rdwr_writers(rwlock));
+}
+
+#endif /* LDAP_RDWR_DEBUG */
+
+#endif /* LDAP_THREAD_HAVE_RDWR */
diff --git a/libraries/libldap_r/rmutex.c b/libraries/libldap_r/rmutex.c
new file mode 100644
index 0000000..6099a2a
--- /dev/null
+++ b/libraries/libldap_r/rmutex.c
@@ -0,0 +1,219 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2006-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 work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software.
+ */
+
+/*
+ * This is an implementation of recursive mutexes.
+ */
+
+#include "portable.h"
+
+#include <ac/stdlib.h>
+
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+
+struct ldap_int_thread_rmutex_s {
+ ldap_pvt_thread_mutex_t ltrm_mutex;
+ ldap_pvt_thread_cond_t ltrm_cond;
+ ldap_pvt_thread_t ltrm_owner;
+ int ltrm_valid;
+#define LDAP_PVT_THREAD_RMUTEX_VALID 0x0cdb
+ int ltrm_depth;
+ int ltrm_waits;
+};
+
+static const ldap_pvt_thread_t tid_zero;
+
+int
+ldap_pvt_thread_rmutex_init( ldap_pvt_thread_rmutex_t *rmutex )
+{
+ struct ldap_int_thread_rmutex_s *rm;
+
+ assert( rmutex != NULL );
+
+ rm = (struct ldap_int_thread_rmutex_s *) LDAP_CALLOC( 1,
+ sizeof( struct ldap_int_thread_rmutex_s ) );
+ if ( !rm )
+ return LDAP_NO_MEMORY;
+
+ /* we should check return results */
+ ldap_pvt_thread_mutex_init( &rm->ltrm_mutex );
+ ldap_pvt_thread_cond_init( &rm->ltrm_cond );
+
+ rm->ltrm_valid = LDAP_PVT_THREAD_RMUTEX_VALID;
+
+ *rmutex = rm;
+ return 0;
+}
+
+int
+ldap_pvt_thread_rmutex_destroy( ldap_pvt_thread_rmutex_t *rmutex )
+{
+ struct ldap_int_thread_rmutex_s *rm;
+
+ assert( rmutex != NULL );
+ rm = *rmutex;
+
+ assert( rm != NULL );
+ assert( rm->ltrm_valid == LDAP_PVT_THREAD_RMUTEX_VALID );
+
+ if( rm->ltrm_valid != LDAP_PVT_THREAD_RMUTEX_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rm->ltrm_mutex );
+
+ assert( rm->ltrm_depth >= 0 );
+ assert( rm->ltrm_waits >= 0 );
+
+ /* in use? */
+ if( rm->ltrm_depth > 0 || rm->ltrm_waits > 0 ) {
+ ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex );
+ return LDAP_PVT_THREAD_EBUSY;
+ }
+
+ rm->ltrm_valid = 0;
+
+ ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex );
+
+ ldap_pvt_thread_mutex_destroy( &rm->ltrm_mutex );
+ ldap_pvt_thread_cond_destroy( &rm->ltrm_cond );
+
+ LDAP_FREE(rm);
+ *rmutex = NULL;
+ return 0;
+}
+
+int ldap_pvt_thread_rmutex_lock( ldap_pvt_thread_rmutex_t *rmutex,
+ ldap_pvt_thread_t owner )
+{
+ struct ldap_int_thread_rmutex_s *rm;
+
+ assert( rmutex != NULL );
+ rm = *rmutex;
+
+ assert( rm != NULL );
+ assert( rm->ltrm_valid == LDAP_PVT_THREAD_RMUTEX_VALID );
+
+ if( rm->ltrm_valid != LDAP_PVT_THREAD_RMUTEX_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rm->ltrm_mutex );
+
+ assert( rm->ltrm_depth >= 0 );
+ assert( rm->ltrm_waits >= 0 );
+
+ if( rm->ltrm_depth > 0 ) {
+ /* already locked */
+ if ( !ldap_pvt_thread_equal( rm->ltrm_owner, owner )) {
+ rm->ltrm_waits++;
+ do {
+ ldap_pvt_thread_cond_wait( &rm->ltrm_cond,
+ &rm->ltrm_mutex );
+ } while( rm->ltrm_depth > 0 );
+
+ rm->ltrm_waits--;
+ assert( rm->ltrm_waits >= 0 );
+ rm->ltrm_owner = owner;
+ }
+ } else {
+ rm->ltrm_owner = owner;
+ }
+
+ rm->ltrm_depth++;
+
+ ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex );
+
+ return 0;
+}
+
+int ldap_pvt_thread_rmutex_trylock( ldap_pvt_thread_rmutex_t *rmutex,
+ ldap_pvt_thread_t owner )
+{
+ struct ldap_int_thread_rmutex_s *rm;
+
+ assert( rmutex != NULL );
+ rm = *rmutex;
+
+ assert( rm != NULL );
+ assert( rm->ltrm_valid == LDAP_PVT_THREAD_RMUTEX_VALID );
+
+ if( rm->ltrm_valid != LDAP_PVT_THREAD_RMUTEX_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rm->ltrm_mutex );
+
+ assert( rm->ltrm_depth >= 0 );
+ assert( rm->ltrm_waits >= 0 );
+
+ if( rm->ltrm_depth > 0 ) {
+ if ( !ldap_pvt_thread_equal( owner, rm->ltrm_owner )) {
+ ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex );
+ return LDAP_PVT_THREAD_EBUSY;
+ }
+ } else {
+ rm->ltrm_owner = owner;
+ }
+
+ rm->ltrm_depth++;
+
+ ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex );
+
+ return 0;
+}
+
+int ldap_pvt_thread_rmutex_unlock( ldap_pvt_thread_rmutex_t *rmutex,
+ ldap_pvt_thread_t owner )
+{
+ struct ldap_int_thread_rmutex_s *rm;
+
+ assert( rmutex != NULL );
+ rm = *rmutex;
+
+ assert( rm != NULL );
+ assert( rm->ltrm_valid == LDAP_PVT_THREAD_RMUTEX_VALID );
+
+ if( rm->ltrm_valid != LDAP_PVT_THREAD_RMUTEX_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rm->ltrm_mutex );
+
+ if( !ldap_pvt_thread_equal( owner, rm->ltrm_owner )) {
+ ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex );
+ return LDAP_PVT_THREAD_EINVAL;
+ }
+
+ rm->ltrm_depth--;
+ if ( !rm->ltrm_depth )
+ rm->ltrm_owner = tid_zero;
+
+ assert( rm->ltrm_depth >= 0 );
+ assert( rm->ltrm_waits >= 0 );
+
+ if ( !rm->ltrm_depth && rm->ltrm_waits ) {
+ ldap_pvt_thread_cond_signal( &rm->ltrm_cond );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex );
+
+ return 0;
+}
+
diff --git a/libraries/libldap_r/rq.c b/libraries/libldap_r/rq.c
new file mode 100644
index 0000000..5857cc6
--- /dev/null
+++ b/libraries/libldap_r/rq.c
@@ -0,0 +1,221 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2021 The OpenLDAP Foundation.
+ * Portions Copyright 2003 IBM Corporation.
+ * 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 work was initially developed by Jong Hyuk Choi for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdarg.h>
+#include <ac/stdlib.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_pvt_thread.h"
+#include "ldap_queue.h"
+#include "ldap_rq.h"
+
+struct re_s *
+ldap_pvt_runqueue_insert(
+ struct runqueue_s* rq,
+ time_t interval,
+ ldap_pvt_thread_start_t *routine,
+ void *arg,
+ char *tname,
+ char *tspec
+)
+{
+ struct re_s* entry;
+
+ entry = (struct re_s *) LDAP_CALLOC( 1, sizeof( struct re_s ));
+ if ( entry ) {
+ entry->interval.tv_sec = interval;
+ entry->interval.tv_usec = 0;
+ entry->next_sched.tv_sec = time( NULL );
+ entry->next_sched.tv_usec = 0;
+ entry->routine = routine;
+ entry->arg = arg;
+ entry->tname = tname;
+ entry->tspec = tspec;
+ LDAP_STAILQ_INSERT_HEAD( &rq->task_list, entry, tnext );
+ }
+ return entry;
+}
+
+struct re_s *
+ldap_pvt_runqueue_find(
+ struct runqueue_s *rq,
+ ldap_pvt_thread_start_t *routine,
+ void *arg
+)
+{
+ struct re_s* e;
+
+ LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) {
+ if ( e->routine == routine && e->arg == arg )
+ return e;
+ }
+ return NULL;
+}
+
+void
+ldap_pvt_runqueue_remove(
+ struct runqueue_s* rq,
+ struct re_s* entry
+)
+{
+ struct re_s* e;
+
+ LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) {
+ if ( e == entry)
+ break;
+ }
+
+ assert( e == entry );
+
+ LDAP_STAILQ_REMOVE( &rq->task_list, entry, re_s, tnext );
+
+ LDAP_FREE( entry );
+}
+
+struct re_s*
+ldap_pvt_runqueue_next_sched(
+ struct runqueue_s* rq,
+ struct timeval* next_run
+)
+{
+ struct re_s* entry;
+
+ entry = LDAP_STAILQ_FIRST( &rq->task_list );
+ if ( entry == NULL || entry->next_sched.tv_sec == 0 ) {
+ return NULL;
+ } else {
+ *next_run = entry->next_sched;
+ return entry;
+ }
+}
+
+void
+ldap_pvt_runqueue_runtask(
+ struct runqueue_s* rq,
+ struct re_s* entry
+)
+{
+ LDAP_STAILQ_INSERT_TAIL( &rq->run_list, entry, rnext );
+}
+
+void
+ldap_pvt_runqueue_stoptask(
+ struct runqueue_s* rq,
+ struct re_s* entry
+)
+{
+ LDAP_STAILQ_REMOVE( &rq->run_list, entry, re_s, rnext );
+}
+
+int
+ldap_pvt_runqueue_isrunning(
+ struct runqueue_s* rq,
+ struct re_s* entry
+)
+{
+ struct re_s* e;
+
+ LDAP_STAILQ_FOREACH( e, &rq->run_list, rnext ) {
+ if ( e == entry ) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void
+ldap_pvt_runqueue_resched(
+ struct runqueue_s* rq,
+ struct re_s* entry,
+ int defer
+)
+{
+ struct re_s* prev;
+ struct re_s* e;
+
+ LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) {
+ if ( e == entry )
+ break;
+ }
+
+ assert ( e == entry );
+
+ LDAP_STAILQ_REMOVE( &rq->task_list, entry, re_s, tnext );
+
+ if ( !defer ) {
+ entry->next_sched.tv_sec = time( NULL ) + entry->interval.tv_sec;
+ } else {
+ entry->next_sched.tv_sec = 0;
+ }
+
+ if ( LDAP_STAILQ_EMPTY( &rq->task_list )) {
+ LDAP_STAILQ_INSERT_HEAD( &rq->task_list, entry, tnext );
+ } else if ( entry->next_sched.tv_sec == 0 ) {
+ LDAP_STAILQ_INSERT_TAIL( &rq->task_list, entry, tnext );
+ } else {
+ prev = NULL;
+ LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) {
+ if ( e->next_sched.tv_sec == 0 ) {
+ if ( prev == NULL ) {
+ LDAP_STAILQ_INSERT_HEAD( &rq->task_list, entry, tnext );
+ } else {
+ LDAP_STAILQ_INSERT_AFTER( &rq->task_list, prev, entry, tnext );
+ }
+ return;
+ } else if ( e->next_sched.tv_sec > entry->next_sched.tv_sec ) {
+ if ( prev == NULL ) {
+ LDAP_STAILQ_INSERT_HEAD( &rq->task_list, entry, tnext );
+ } else {
+ LDAP_STAILQ_INSERT_AFTER( &rq->task_list, prev, entry, tnext );
+ }
+ return;
+ }
+ prev = e;
+ }
+ LDAP_STAILQ_INSERT_TAIL( &rq->task_list, entry, tnext );
+ }
+}
+
+int
+ldap_pvt_runqueue_persistent_backload(
+ struct runqueue_s* rq
+)
+{
+ struct re_s* e;
+ int count = 0;
+
+ ldap_pvt_thread_mutex_lock( &rq->rq_mutex );
+ if ( !LDAP_STAILQ_EMPTY( &rq->task_list )) {
+ LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) {
+ if ( e->next_sched.tv_sec == 0 )
+ count++;
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &rq->rq_mutex );
+ return count;
+}
+
diff --git a/libraries/libldap_r/thr_cthreads.c b/libraries/libldap_r/thr_cthreads.c
new file mode 100644
index 0000000..6cc4789
--- /dev/null
+++ b/libraries/libldap_r/thr_cthreads.c
@@ -0,0 +1,180 @@
+/* thr_cthreads.c - wrapper for mach cthreads */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-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 work was initially developed by Luke Howard for inclusion
+ * in U-MICH LDAP 3.3.
+ */
+
+#include "portable.h"
+
+#if defined( HAVE_MACH_CTHREADS )
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+
+int
+ldap_int_thread_initialize( void )
+{
+ return 0;
+}
+
+int
+ldap_int_thread_destroy( void )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_create( ldap_pvt_thread_t * thread,
+ int detach,
+ void *(*start_routine)( void *), void *arg)
+{
+ *thread = cthread_fork( (cthread_fn_t) start_routine, arg);
+ return ( *thread == NULL ? -1 : 0 );
+}
+
+void
+ldap_pvt_thread_exit( void *retval )
+{
+ cthread_exit( (any_t) retval );
+}
+
+int
+ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
+{
+ void *status;
+ status = (void *) cthread_join ( thread );
+ if (thread_return != NULL)
+ {
+ *thread_return = status;
+ }
+ return 0;
+}
+
+int
+ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_yield( void )
+{
+ cthread_yield();
+ return 0;
+}
+
+int
+ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
+{
+ condition_init( cond );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cond )
+{
+ condition_clear( cond );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
+{
+ condition_signal( cond );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond )
+{
+ condition_broadcast( cond );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ condition_wait( cond, mutex );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ mutex_init( mutex );
+ mutex->name = NULL;
+ return ( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
+{
+ mutex_clear( mutex );
+ return ( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
+{
+ mutex_lock( mutex );
+ return ( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
+{
+ mutex_unlock( mutex );
+ return ( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return mutex_try_lock( mutex );
+}
+
+ldap_pvt_thread_t
+ldap_pvt_thread_self( void )
+{
+ return cthread_self();
+}
+
+int
+ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *key )
+{
+ return cthread_keycreate( key );
+}
+
+int
+ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key )
+{
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data )
+{
+ return cthread_setspecific( key, data );
+}
+
+int
+ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data )
+{
+ return cthread_getspecific( key, data );
+}
+
+#endif /* HAVE_MACH_CTHREADS */
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 */
diff --git a/libraries/libldap_r/thr_nt.c b/libraries/libldap_r/thr_nt.c
new file mode 100644
index 0000000..1de232d
--- /dev/null
+++ b/libraries/libldap_r/thr_nt.c
@@ -0,0 +1,245 @@
+/* thr_nt.c - wrapper around NT threads */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-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>.
+ */
+
+#include "portable.h"
+
+#if defined( HAVE_NT_THREADS )
+
+#define _WIN32_WINNT 0x0400
+#include <windows.h>
+#include <process.h>
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+
+typedef struct ldap_int_thread_s {
+ long tid;
+ HANDLE thd;
+} ldap_int_thread_s;
+
+#ifndef NT_MAX_THREADS
+#define NT_MAX_THREADS 1024
+#endif
+
+static ldap_int_thread_s tids[NT_MAX_THREADS];
+static int ntids;
+
+
+/* mingw compiler very sensitive about getting prototypes right */
+typedef unsigned __stdcall thrfunc_t(void *);
+
+int
+ldap_int_thread_initialize( void )
+{
+ return 0;
+}
+
+int
+ldap_int_thread_destroy( void )
+{
+ return 0;
+}
+
+int
+ldap_int_mutex_firstcreate( ldap_int_thread_mutex_t *mutex )
+{
+ if ( *mutex == NULL ) {
+ HANDLE p = CreateMutex( NULL, 0, NULL );
+ if ( InterlockedCompareExchangePointer((PVOID*)mutex, (PVOID)p, NULL) != NULL)
+ CloseHandle( p );
+ }
+ return 0;
+}
+
+int
+ldap_pvt_thread_create( ldap_pvt_thread_t * thread,
+ int detach,
+ void *(*start_routine)( void *),
+ void *arg)
+{
+ unsigned tid;
+ HANDLE thd;
+ int rc = -1;
+
+ thd = (HANDLE) _beginthreadex(NULL, LDAP_PVT_THREAD_STACK_SIZE, (thrfunc_t *) start_routine,
+ arg, 0, &tid);
+
+ if ( thd ) {
+ *thread = (ldap_pvt_thread_t) tid;
+ tids[ntids].tid = tid;
+ tids[ntids].thd = thd;
+ ntids++;
+ rc = 0;
+ }
+ return rc;
+}
+
+void
+ldap_pvt_thread_exit( void *retval )
+{
+ _endthread( );
+}
+
+int
+ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
+{
+ DWORD status;
+ int i;
+
+ for (i=0; i<ntids; i++) {
+ if ( tids[i].tid == thread )
+ break;
+ }
+ if ( i > ntids ) return -1;
+
+ status = WaitForSingleObject( tids[i].thd, INFINITE );
+ for (; i<ntids; i++) {
+ tids[i] = tids[i+1];
+ }
+ ntids--;
+ return status == WAIT_FAILED ? -1 : 0;
+}
+
+int
+ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_yield( void )
+{
+ Sleep( 0 );
+ return 0;
+}
+
+int
+ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
+{
+ *cond = CreateEvent( NULL, FALSE, FALSE, NULL );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cv )
+{
+ CloseHandle( *cv );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
+{
+ SetEvent( *cond );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ SignalObjectAndWait( *mutex, *cond, INFINITE, FALSE );
+ WaitForSingleObject( *mutex, INFINITE );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond )
+{
+ while ( WaitForSingleObject( *cond, 0 ) == WAIT_TIMEOUT )
+ SetEvent( *cond );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ *mutex = CreateMutex( NULL, 0, NULL );
+ return ( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
+{
+ CloseHandle( *mutex );
+ return ( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
+{
+ DWORD status;
+ status = WaitForSingleObject( *mutex, INFINITE );
+ return status == WAIT_FAILED ? -1 : 0;
+}
+
+int
+ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
+{
+ ReleaseMutex( *mutex );
+ return ( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mp )
+{
+ DWORD status;
+ status = WaitForSingleObject( *mp, 0 );
+ return status == WAIT_FAILED || status == WAIT_TIMEOUT
+ ? -1 : 0;
+}
+
+ldap_pvt_thread_t
+ldap_pvt_thread_self( void )
+{
+ return GetCurrentThreadId();
+}
+
+int
+ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *keyp )
+{
+ DWORD key = TlsAlloc();
+ if ( key != TLS_OUT_OF_INDEXES ) {
+ *keyp = key;
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+int
+ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key )
+{
+ /* TlsFree returns 0 on failure */
+ return( TlsFree( key ) == 0 );
+}
+
+int
+ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data )
+{
+ return ( TlsSetValue( key, data ) == 0 );
+}
+
+int
+ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data )
+{
+ void *ptr = TlsGetValue( key );
+ *data = ptr;
+ return( ptr ? GetLastError() : 0 );
+}
+
+#endif
diff --git a/libraries/libldap_r/thr_posix.c b/libraries/libldap_r/thr_posix.c
new file mode 100644
index 0000000..e4b4357
--- /dev/null
+++ b/libraries/libldap_r/thr_posix.c
@@ -0,0 +1,388 @@
+/* thr_posix.c - wrapper around posix and posixish thread implementations. */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-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>.
+ */
+
+#include "portable.h"
+
+#if defined( HAVE_PTHREADS )
+
+#include <ac/errno.h>
+
+#ifdef REPLACE_BROKEN_YIELD
+#ifndef HAVE_NANOSLEEP
+#include <ac/socket.h>
+#endif
+#include <ac/time.h>
+#endif
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_IMPLEMENTATION
+#define LDAP_THREAD_RDWR_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+#include <signal.h> /* For pthread_kill() */
+
+#if HAVE_PTHREADS < 6
+# define LDAP_INT_THREAD_ATTR_DEFAULT pthread_attr_default
+# define LDAP_INT_THREAD_CONDATTR_DEFAULT pthread_condattr_default
+# define LDAP_INT_THREAD_MUTEXATTR_DEFAULT pthread_mutexattr_default
+#else
+# define LDAP_INT_THREAD_ATTR_DEFAULT NULL
+# define LDAP_INT_THREAD_CONDATTR_DEFAULT NULL
+# define LDAP_INT_THREAD_MUTEXATTR_DEFAULT NULL
+#endif
+
+#ifdef LDAP_THREAD_DEBUG
+# if defined LDAP_INT_THREAD_MUTEXATTR /* May be defined in CPPFLAGS */
+# elif defined HAVE_PTHREAD_KILL_OTHER_THREADS_NP
+ /* LinuxThreads hack */
+# define LDAP_INT_THREAD_MUTEXATTR PTHREAD_MUTEX_ERRORCHECK_NP
+# else
+# define LDAP_INT_THREAD_MUTEXATTR PTHREAD_MUTEX_ERRORCHECK
+# endif
+static pthread_mutexattr_t mutex_attr;
+# undef LDAP_INT_THREAD_MUTEXATTR_DEFAULT
+# define LDAP_INT_THREAD_MUTEXATTR_DEFAULT &mutex_attr
+#endif
+
+#if HAVE_PTHREADS < 7
+#define ERRVAL(val) ((val) < 0 ? errno : 0)
+#else
+#define ERRVAL(val) (val)
+#endif
+
+int
+ldap_int_thread_initialize( void )
+{
+#ifdef LDAP_INT_THREAD_MUTEXATTR
+ pthread_mutexattr_init( &mutex_attr );
+ pthread_mutexattr_settype( &mutex_attr, LDAP_INT_THREAD_MUTEXATTR );
+#endif
+ return 0;
+}
+
+int
+ldap_int_thread_destroy( void )
+{
+#ifdef HAVE_PTHREAD_KILL_OTHER_THREADS_NP
+ /* LinuxThreads: kill clones */
+ pthread_kill_other_threads_np();
+#endif
+#ifdef LDAP_INT_THREAD_MUTEXATTR
+ pthread_mutexattr_destroy( &mutex_attr );
+#endif
+ return 0;
+}
+
+#ifdef LDAP_THREAD_HAVE_SETCONCURRENCY
+int
+ldap_pvt_thread_set_concurrency(int n)
+{
+#ifdef HAVE_PTHREAD_SETCONCURRENCY
+ return pthread_setconcurrency( n );
+#elif defined(HAVE_THR_SETCONCURRENCY)
+ return thr_setconcurrency( n );
+#else
+ return 0;
+#endif
+}
+#endif
+
+#ifdef LDAP_THREAD_HAVE_GETCONCURRENCY
+int
+ldap_pvt_thread_get_concurrency(void)
+{
+#ifdef HAVE_PTHREAD_GETCONCURRENCY
+ return pthread_getconcurrency();
+#elif defined(HAVE_THR_GETCONCURRENCY)
+ return thr_getconcurrency();
+#else
+ return 0;
+#endif
+}
+#endif
+
+/* detachstate appeared in Draft 6, but without manifest constants.
+ * in Draft 7 they were called PTHREAD_CREATE_UNDETACHED and ...DETACHED.
+ * in Draft 8 on, ...UNDETACHED became ...JOINABLE.
+ */
+#ifndef PTHREAD_CREATE_JOINABLE
+#ifdef PTHREAD_CREATE_UNDETACHED
+#define PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED
+#else
+#define PTHREAD_CREATE_JOINABLE 0
+#endif
+#endif
+
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+
+int
+ldap_pvt_thread_create( ldap_pvt_thread_t * thread,
+ int detach,
+ void *(*start_routine)( void * ),
+ void *arg)
+{
+ int rtn;
+ pthread_attr_t attr;
+
+/* Always create the thread attrs, so we can set stacksize if we need to */
+#if HAVE_PTHREADS > 5
+ pthread_attr_init(&attr);
+#else
+ pthread_attr_create(&attr);
+#endif
+
+#ifdef LDAP_PVT_THREAD_SET_STACK_SIZE
+ /* this should be tunable */
+ pthread_attr_setstacksize( &attr, LDAP_PVT_THREAD_STACK_SIZE );
+#endif
+
+#if HAVE_PTHREADS > 5
+ detach = detach ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE;
+#if HAVE_PTHREADS == 6
+ pthread_attr_setdetachstate(&attr, &detach);
+#else
+ pthread_attr_setdetachstate(&attr, detach);
+#endif
+#endif
+
+#if HAVE_PTHREADS < 5
+ rtn = pthread_create( thread, attr, start_routine, arg );
+#else
+ rtn = pthread_create( thread, &attr, start_routine, arg );
+#endif
+
+#if HAVE_PTHREADS > 5
+ pthread_attr_destroy(&attr);
+#else
+ pthread_attr_delete(&attr);
+ if( detach ) {
+ pthread_detach( thread );
+ }
+#endif
+
+#if HAVE_PTHREADS < 7
+ if ( rtn < 0 ) rtn = errno;
+#endif
+ return rtn;
+}
+
+void
+ldap_pvt_thread_exit( void *retval )
+{
+ pthread_exit( retval );
+}
+
+int
+ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
+{
+#if HAVE_PTHREADS < 7
+ void *dummy;
+ if (thread_return==NULL)
+ thread_return=&dummy;
+#endif
+ return ERRVAL( pthread_join( thread, thread_return ) );
+}
+
+int
+ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
+{
+#if defined(HAVE_PTHREAD_KILL) && HAVE_PTHREADS > 4
+ /* MacOS 10.1 is detected as v10 but has no pthread_kill() */
+ return ERRVAL( pthread_kill( thread, signo ) );
+#else
+ /* pthread package with DCE */
+ if (kill( getpid(), signo )<0)
+ return errno;
+ return 0;
+#endif
+}
+
+int
+ldap_pvt_thread_yield( void )
+{
+#ifdef REPLACE_BROKEN_YIELD
+#ifdef HAVE_NANOSLEEP
+ struct timespec t = { 0, 0 };
+ nanosleep(&t, NULL);
+#else
+ struct timeval tv = {0,0};
+ select( 0, NULL, NULL, NULL, &tv );
+#endif
+ return 0;
+
+#elif defined(HAVE_THR_YIELD)
+ thr_yield();
+ return 0;
+
+#elif HAVE_PTHREADS == 10
+ return sched_yield();
+
+#elif defined(_POSIX_THREAD_IS_GNU_PTH)
+ sched_yield();
+ return 0;
+
+#elif HAVE_PTHREADS == 6
+ pthread_yield(NULL);
+ return 0;
+
+#else
+ pthread_yield();
+ return 0;
+#endif
+}
+
+int
+ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
+{
+ return ERRVAL( pthread_cond_init(
+ cond, LDAP_INT_THREAD_CONDATTR_DEFAULT ) );
+}
+
+int
+ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cond )
+{
+ return ERRVAL( pthread_cond_destroy( cond ) );
+}
+
+int
+ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
+{
+ return ERRVAL( pthread_cond_signal( cond ) );
+}
+
+int
+ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond )
+{
+ return ERRVAL( pthread_cond_broadcast( cond ) );
+}
+
+int
+ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ return ERRVAL( pthread_cond_wait( cond, mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ return ERRVAL( pthread_mutex_init(
+ mutex, LDAP_INT_THREAD_MUTEXATTR_DEFAULT ) );
+}
+
+int
+ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
+{
+ return ERRVAL( pthread_mutex_destroy( mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return ERRVAL( pthread_mutex_lock( mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return ERRVAL( pthread_mutex_trylock( mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return ERRVAL( pthread_mutex_unlock( mutex ) );
+}
+
+ldap_pvt_thread_t ldap_pvt_thread_self( void )
+{
+ return pthread_self();
+}
+
+int
+ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *key )
+{
+ return pthread_key_create( key, NULL );
+}
+
+int
+ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key )
+{
+ return pthread_key_delete( key );
+}
+
+int
+ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data )
+{
+ return pthread_setspecific( key, data );
+}
+
+int
+ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data )
+{
+ *data = pthread_getspecific( key );
+ return 0;
+}
+
+#ifdef LDAP_THREAD_HAVE_RDWR
+#ifdef HAVE_PTHREAD_RWLOCK_DESTROY
+int
+ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_init( rw, NULL ) );
+}
+
+int
+ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_destroy( rw ) );
+}
+
+int ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_rdlock( rw ) );
+}
+
+int ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_tryrdlock( rw ) );
+}
+
+int ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_unlock( rw ) );
+}
+
+int ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_wrlock( rw ) );
+}
+
+int ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_trywrlock( rw ) );
+}
+
+int ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_unlock( rw ) );
+}
+
+#endif /* HAVE_PTHREAD_RWLOCK_DESTROY */
+#endif /* LDAP_THREAD_HAVE_RDWR */
+#endif /* HAVE_PTHREADS */
+
diff --git a/libraries/libldap_r/thr_pth.c b/libraries/libldap_r/thr_pth.c
new file mode 100644
index 0000000..13377a0
--- /dev/null
+++ b/libraries/libldap_r/thr_pth.c
@@ -0,0 +1,231 @@
+/* thr_pth.c - wrappers around GNU Pth */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-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>.
+ */
+
+#include "portable.h"
+
+#if defined( HAVE_GNU_PTH )
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_IMPLEMENTATION
+#define LDAP_THREAD_RDWR_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+
+#include <errno.h>
+
+/*******************
+ * *
+ * GNU Pth Threads *
+ * *
+ *******************/
+
+static pth_attr_t detach_attr;
+static pth_attr_t joined_attr;
+
+int
+ldap_int_thread_initialize( void )
+{
+ if( !pth_init() ) {
+ return -1;
+ }
+ detach_attr = pth_attr_new();
+ joined_attr = pth_attr_new();
+#ifdef LDAP_PVT_THREAD_SET_STACK_SIZE
+ pth_attr_set( joined_attr, PTH_ATTR_STACK_SIZE, LDAP_PVT_THREAD_STACK_SIZE );
+ pth_attr_set( detach_attr, PTH_ATTR_STACK_SIZE, LDAP_PVT_THREAD_STACK_SIZE );
+#endif
+ return pth_attr_set( detach_attr, PTH_ATTR_JOINABLE, FALSE );
+}
+
+int
+ldap_int_thread_destroy( void )
+{
+ pth_attr_destroy(detach_attr);
+ pth_kill();
+ return 0;
+}
+
+int
+ldap_pvt_thread_create( ldap_pvt_thread_t * thread,
+ int detach,
+ void *(*start_routine)( void *),
+ void *arg)
+{
+ *thread = pth_spawn( detach ? detach_attr : joined_attr,
+ start_routine, arg );
+
+ return *thread == NULL ? errno : 0;
+}
+
+void
+ldap_pvt_thread_exit( void *retval )
+{
+ pth_exit( retval );
+}
+
+int ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
+{
+ return pth_join( thread, thread_return ) ? 0 : errno;
+}
+
+int
+ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
+{
+ return pth_raise( thread, signo ) ? 0 : errno;
+}
+
+int
+ldap_pvt_thread_yield( void )
+{
+ return pth_yield(NULL) ? 0 : errno;
+}
+
+int
+ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
+{
+ return( pth_cond_init( cond ) ? 0 : errno );
+}
+
+int
+ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
+{
+ return( pth_cond_notify( cond, 0 ) ? 0 : errno );
+}
+
+int
+ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond )
+{
+ return( pth_cond_notify( cond, 1 ) ? 0 : errno );
+}
+
+int
+ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ return( pth_cond_await( cond, mutex, NULL ) ? 0 : errno );
+}
+
+int
+ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cv )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( pth_mutex_init( mutex ) ? 0 : errno );
+}
+
+int
+ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( pth_mutex_acquire( mutex, 0, NULL ) ? 0 : errno );
+}
+
+int
+ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( pth_mutex_release( mutex ) ? 0 : errno );
+}
+
+int
+ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( pth_mutex_acquire( mutex, 1, NULL ) ? 0 : errno );
+}
+
+ldap_pvt_thread_t
+ldap_pvt_thread_self( void )
+{
+ return pth_self();
+}
+
+int
+ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *key )
+{
+ return pth_key_create( key, NULL );
+}
+
+int
+ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key )
+{
+ return pth_key_delete( key );
+}
+
+int
+ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data )
+{
+ return pth_key_setdata( key, data );
+}
+
+int
+ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data )
+{
+ *data = pth_key_getdata( key );
+ return 0;
+}
+
+#ifdef LDAP_THREAD_HAVE_RDWR
+int
+ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rw )
+{
+ return pth_rwlock_init( rw ) ? 0 : errno;
+}
+
+int
+ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rw )
+{
+ return 0;
+}
+
+int ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return pth_rwlock_acquire( rw, PTH_RWLOCK_RD, 0, NULL ) ? 0 : errno;
+}
+
+int ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return pth_rwlock_acquire( rw, PTH_RWLOCK_RD, 1, NULL ) ? 0 : errno;
+}
+
+int ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return pth_rwlock_release( rw ) ? 0 : errno;
+}
+
+int ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return pth_rwlock_acquire( rw, PTH_RWLOCK_RW, 0, NULL ) ? 0 : errno;
+}
+
+int ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return pth_rwlock_acquire( rw, PTH_RWLOCK_RW, 1, NULL ) ? 0 : errno;
+}
+
+int ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return pth_rwlock_release( rw ) ? 0 : errno;
+}
+
+#endif /* LDAP_THREAD_HAVE_RDWR */
+#endif /* HAVE_GNU_PTH */
diff --git a/libraries/libldap_r/thr_stub.c b/libraries/libldap_r/thr_stub.c
new file mode 100644
index 0000000..05357d9
--- /dev/null
+++ b/libraries/libldap_r/thr_stub.c
@@ -0,0 +1,305 @@
+/* thr_stub.c - stubs for the threads */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-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>.
+ */
+
+#include "portable.h"
+
+#if defined( NO_THREADS )
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_IMPLEMENTATION
+#define LDAP_THREAD_POOL_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+
+/***********************************************************************
+ * *
+ * no threads package defined for this system - fake ok returns from *
+ * all threads routines (making it single-threaded). *
+ * *
+ ***********************************************************************/
+
+int
+ldap_int_thread_initialize( void )
+{
+ return 0;
+}
+
+int
+ldap_int_thread_destroy( void )
+{
+ return 0;
+}
+
+static void* ldap_int_status = NULL;
+
+int
+ldap_pvt_thread_create( ldap_pvt_thread_t * thread,
+ int detach,
+ void *(*start_routine)(void *),
+ void *arg)
+{
+ if( ! detach ) ldap_int_status = NULL;
+ start_routine( arg );
+ return 0;
+}
+
+void
+ldap_pvt_thread_exit( void *retval )
+{
+ if( retval != NULL ) {
+ ldap_int_status = retval;
+ }
+ return;
+}
+
+int
+ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **status )
+{
+ if(status != NULL) *status = ldap_int_status;
+ return 0;
+}
+
+int
+ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_yield( void )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cond )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return 0;
+}
+
+/*
+ * NO_THREADS requires a separate tpool implementation since
+ * generic ldap_pvt_thread_pool_wrapper loops forever.
+ */
+int
+ldap_pvt_thread_pool_init (
+ ldap_pvt_thread_pool_t *pool_out,
+ int max_concurrency, int max_pending )
+{
+ *pool_out = (ldap_pvt_thread_pool_t) 0;
+ return(0);
+}
+
+int
+ldap_pvt_thread_pool_submit (
+ ldap_pvt_thread_pool_t *pool,
+ ldap_pvt_thread_start_t *start_routine, void *arg )
+{
+ (start_routine)(NULL, arg);
+ return(0);
+}
+
+int
+ldap_pvt_thread_pool_retract (
+ ldap_pvt_thread_pool_t *pool,
+ ldap_pvt_thread_start_t *start_routine, void *arg )
+{
+ return(0);
+}
+
+int
+ldap_pvt_thread_pool_maxthreads ( ldap_pvt_thread_pool_t *tpool, int max_threads )
+{
+ return(0);
+}
+
+int
+ldap_pvt_thread_pool_query( ldap_pvt_thread_pool_t *tpool,
+ ldap_pvt_thread_pool_param_t param, void *value )
+{
+ *(int *)value = -1;
+ return(-1);
+}
+
+int
+ldap_pvt_thread_pool_backload (
+ ldap_pvt_thread_pool_t *pool )
+{
+ return(0);
+}
+
+int
+ldap_pvt_thread_pool_destroy (
+ ldap_pvt_thread_pool_t *pool, int run_pending )
+{
+ return(0);
+}
+
+void
+ldap_pvt_thread_pool_idle ( ldap_pvt_thread_pool_t *pool )
+{
+ return;
+}
+
+void
+ldap_pvt_thread_pool_unidle ( ldap_pvt_thread_pool_t *pool )
+{
+ return;
+}
+
+int ldap_pvt_thread_pool_getkey (
+ void *ctx, void *key, void **data, ldap_pvt_thread_pool_keyfree_t **kfree )
+{
+ return(0);
+}
+
+int ldap_pvt_thread_pool_setkey (
+ void *ctx, void *key,
+ void *data, ldap_pvt_thread_pool_keyfree_t *kfree,
+ void **olddatap, ldap_pvt_thread_pool_keyfree_t **oldkfreep )
+{
+ if ( olddatap ) *olddatap = NULL;
+ if ( oldkfreep ) *oldkfreep = 0;
+ return(0);
+}
+
+void ldap_pvt_thread_pool_purgekey( void *key )
+{
+}
+
+int ldap_pvt_thread_pool_pause (
+ ldap_pvt_thread_pool_t *tpool )
+{
+ return(0);
+}
+
+int ldap_pvt_thread_pool_resume (
+ ldap_pvt_thread_pool_t *tpool )
+{
+ return(0);
+}
+
+int ldap_pvt_thread_pool_pausing( ldap_pvt_thread_pool_t *tpool )
+{
+ return(0);
+}
+
+ldap_pvt_thread_pool_pausecheck( ldap_pvt_thread_pool_t *tpool )
+{
+ return(0);
+}
+
+void *ldap_pvt_thread_pool_context( )
+{
+ return(NULL);
+}
+
+void ldap_pvt_thread_pool_context_reset( void *vctx )
+{
+}
+
+ldap_pvt_thread_t
+ldap_pvt_thread_self( void )
+{
+ return(0);
+}
+
+int
+ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *key )
+{
+ return(0);
+}
+
+int
+ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key )
+{
+ return(0);
+}
+
+int
+ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data )
+{
+ return(0);
+}
+
+int
+ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data )
+{
+ return(0);
+}
+
+ldap_pvt_thread_t
+ldap_pvt_thread_pool_tid( void *vctx )
+{
+
+ return(0);
+}
+
+#endif /* NO_THREADS */
diff --git a/libraries/libldap_r/thr_thr.c b/libraries/libldap_r/thr_thr.c
new file mode 100644
index 0000000..dfb41a6
--- /dev/null
+++ b/libraries/libldap_r/thr_thr.c
@@ -0,0 +1,186 @@
+/* thr_thr.c - wrappers around solaris threads */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-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>.
+ */
+
+#include "portable.h"
+
+#if defined( HAVE_THR )
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+
+/*******************
+ * *
+ * Solaris Threads *
+ * *
+ *******************/
+
+int
+ldap_int_thread_initialize( void )
+{
+ return 0;
+}
+
+int
+ldap_int_thread_destroy( void )
+{
+ return 0;
+}
+
+#ifdef LDAP_THREAD_HAVE_SETCONCURRENCY
+int
+ldap_pvt_thread_set_concurrency(int n)
+{
+ return thr_setconcurrency( n );
+}
+#endif
+
+#ifdef LDAP_THREAD_HAVE_GETCONCURRENCY
+int
+ldap_pvt_thread_get_concurrency(void)
+{
+ return thr_getconcurrency();
+}
+#endif
+
+int
+ldap_pvt_thread_create( ldap_pvt_thread_t * thread,
+ int detach,
+ void *(*start_routine)( void *),
+ void *arg)
+{
+ return( thr_create( NULL, LDAP_PVT_THREAD_STACK_SIZE, start_routine,
+ arg, detach ? THR_DETACHED : 0, thread ) );
+}
+
+void
+ldap_pvt_thread_exit( void *retval )
+{
+ thr_exit( NULL );
+}
+
+int ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
+{
+ thr_join( thread, NULL, thread_return );
+ return 0;
+}
+
+int
+ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
+{
+ thr_kill( thread, signo );
+ return 0;
+}
+
+int
+ldap_pvt_thread_yield( void )
+{
+ thr_yield();
+ return 0;
+}
+
+int
+ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
+{
+ return( cond_init( cond, USYNC_THREAD, NULL ) );
+}
+
+int
+ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
+{
+ return( cond_signal( cond ) );
+}
+
+int
+ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cv )
+{
+ return( cond_broadcast( cv ) );
+}
+
+int
+ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ return( cond_wait( cond, mutex ) );
+}
+
+int
+ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cv )
+{
+ return( cond_destroy( cv ) );
+}
+
+int
+ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( mutex_init( mutex, USYNC_THREAD, NULL ) );
+}
+
+int
+ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( mutex_destroy( mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( mutex_lock( mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( mutex_unlock( mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mp )
+{
+ return( mutex_trylock( mp ) );
+}
+
+ldap_pvt_thread_t
+ldap_pvt_thread_self( void )
+{
+ return thr_self();
+}
+
+int
+ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *key )
+{
+ return thr_keycreate( key, NULL );
+}
+
+int
+ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key )
+{
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data )
+{
+ return thr_setspecific( key, data );
+}
+
+int
+ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data )
+{
+ return thr_getspecific( key, data );
+}
+
+#endif /* HAVE_THR */
diff --git a/libraries/libldap_r/threads.c b/libraries/libldap_r/threads.c
new file mode 100644
index 0000000..ff9ed8f
--- /dev/null
+++ b/libraries/libldap_r/threads.c
@@ -0,0 +1,113 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-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>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdarg.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#include "ldap_thr_debug.h" /* May redirect thread initialize/destroy calls */
+
+
+/*
+ * Common LDAP thread routines
+ * see thr_*.c for implementation specific routines
+ * see rdwr.c for generic reader/writer lock implementation
+ * see tpool.c for generic thread pool implementation
+ */
+
+
+int ldap_pvt_thread_initialize( void )
+{
+ int rc;
+ static int init = 0;
+ ldap_pvt_thread_rmutex_t rm;
+ ldap_pvt_thread_t tid;
+
+ /* we only get one shot at this */
+ if( init++ ) return -1;
+
+ rc = ldap_int_thread_initialize();
+ if( rc ) return rc;
+
+#ifndef LDAP_THREAD_HAVE_TPOOL
+ rc = ldap_int_thread_pool_startup();
+ if( rc ) return rc;
+#endif
+
+ /* kludge to pull symbol definitions in */
+ ldap_pvt_thread_rmutex_init( &rm );
+ tid = ldap_pvt_thread_self();
+ ldap_pvt_thread_rmutex_lock( &rm, tid );
+ ldap_pvt_thread_rmutex_trylock( &rm, tid );
+ ldap_pvt_thread_rmutex_unlock( &rm, tid );
+ ldap_pvt_thread_rmutex_unlock( &rm, tid );
+ ldap_pvt_thread_rmutex_destroy( &rm );
+
+ return 0;
+}
+
+int ldap_pvt_thread_destroy( void )
+{
+#ifndef LDAP_THREAD_HAVE_TPOOL
+ (void) ldap_int_thread_pool_shutdown();
+#endif
+ return ldap_int_thread_destroy();
+}
+
+
+/*
+ * Default implementations of some LDAP thread routines
+ */
+
+#define LDAP_THREAD_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+
+
+#ifndef LDAP_THREAD_HAVE_GETCONCURRENCY
+int
+ldap_pvt_thread_get_concurrency ( void )
+{
+ return 1;
+}
+#endif
+
+#ifndef LDAP_THREAD_HAVE_SETCONCURRENCY
+int
+ldap_pvt_thread_set_concurrency ( int concurrency )
+{
+ return 1;
+}
+#endif
+
+#ifndef LDAP_THREAD_HAVE_SLEEP
+/*
+ * Here we assume we have fully preemptive threads and that sleep()
+ * does the right thing.
+ */
+unsigned int
+ldap_pvt_thread_sleep(
+ unsigned int interval
+)
+{
+ sleep( interval );
+ return 0;
+}
+#endif
diff --git a/libraries/libldap_r/tpool.c b/libraries/libldap_r/tpool.c
new file mode 100644
index 0000000..17cee3b
--- /dev/null
+++ b/libraries/libldap_r/tpool.c
@@ -0,0 +1,1032 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-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>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/signal.h>
+#include <ac/stdarg.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/errno.h>
+
+#include "ldap-int.h"
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#include "ldap_queue.h"
+#define LDAP_THREAD_POOL_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename symbols defined below */
+
+#ifndef LDAP_THREAD_HAVE_TPOOL
+
+/* Thread-specific key with data and optional free function */
+typedef struct ldap_int_tpool_key_s {
+ void *ltk_key;
+ void *ltk_data;
+ ldap_pvt_thread_pool_keyfree_t *ltk_free;
+} ldap_int_tpool_key_t;
+
+/* Max number of thread-specific keys we store per thread.
+ * We don't expect to use many...
+ */
+#define MAXKEYS 32
+
+/* Max number of threads */
+#define LDAP_MAXTHR 1024 /* must be a power of 2 */
+
+/* (Theoretical) max number of pending requests */
+#define MAX_PENDING (INT_MAX/2) /* INT_MAX - (room to avoid overflow) */
+
+/* pool->ltp_pause values */
+enum { NOT_PAUSED = 0, WANT_PAUSE = 1, PAUSED = 2 };
+
+/* Context: thread ID and thread-specific key/data pairs */
+typedef struct ldap_int_thread_userctx_s {
+ ldap_pvt_thread_t ltu_id;
+ ldap_int_tpool_key_t ltu_key[MAXKEYS];
+} ldap_int_thread_userctx_t;
+
+
+/* Simple {thread ID -> context} hash table; key=ctx->ltu_id.
+ * Protected by ldap_pvt_thread_pool_mutex except during pauses,
+ * when it is read-only (used by pool_purgekey and pool_context).
+ * Protected by tpool->ltp_mutex during pauses.
+ */
+static struct {
+ ldap_int_thread_userctx_t *ctx;
+ /* ctx is valid when not NULL or DELETED_THREAD_CTX */
+# define DELETED_THREAD_CTX (&ldap_int_main_thrctx + 1) /* dummy addr */
+} thread_keys[LDAP_MAXTHR];
+
+#define TID_HASH(tid, hash) do { \
+ unsigned const char *ptr_ = (unsigned const char *)&(tid); \
+ unsigned i_; \
+ for (i_ = 0, (hash) = ptr_[0]; ++i_ < sizeof(tid);) \
+ (hash) += ((hash) << 5) ^ ptr_[i_]; \
+} while(0)
+
+
+/* Task for a thread to perform */
+typedef struct ldap_int_thread_task_s {
+ union {
+ LDAP_STAILQ_ENTRY(ldap_int_thread_task_s) q;
+ LDAP_SLIST_ENTRY(ldap_int_thread_task_s) l;
+ } ltt_next;
+ ldap_pvt_thread_start_t *ltt_start_routine;
+ void *ltt_arg;
+} ldap_int_thread_task_t;
+
+typedef LDAP_STAILQ_HEAD(tcq, ldap_int_thread_task_s) ldap_int_tpool_plist_t;
+
+struct ldap_int_thread_pool_s {
+ LDAP_STAILQ_ENTRY(ldap_int_thread_pool_s) ltp_next;
+
+ /* protect members below, and protect thread_keys[] during pauses */
+ ldap_pvt_thread_mutex_t ltp_mutex;
+
+ /* not paused and something to do for pool_<wrapper/pause/destroy>() */
+ ldap_pvt_thread_cond_t ltp_cond;
+
+ /* ltp_active_count <= 1 && ltp_pause */
+ ldap_pvt_thread_cond_t ltp_pcond;
+
+ /* ltp_pause == 0 ? &ltp_pending_list : &empty_pending_list,
+ * maintaned to reduce work for pool_wrapper()
+ */
+ ldap_int_tpool_plist_t *ltp_work_list;
+
+ /* pending tasks, and unused task objects */
+ ldap_int_tpool_plist_t ltp_pending_list;
+ LDAP_SLIST_HEAD(tcl, ldap_int_thread_task_s) ltp_free_list;
+
+ /* The pool is finishing, waiting for its threads to close.
+ * They close when ltp_pending_list is done. pool_submit()
+ * rejects new tasks. ltp_max_pending = -(its old value).
+ */
+ int ltp_finishing;
+
+ /* Some active task needs to be the sole active task.
+ * Atomic variable so ldap_pvt_thread_pool_pausing() can read it.
+ * Note: Pauses adjust ltp_<open_count/vary_open_count/work_list>,
+ * so pool_<submit/wrapper>() mostly can avoid testing ltp_pause.
+ */
+ volatile sig_atomic_t ltp_pause;
+
+ /* Max number of threads in pool, or 0 for default (LDAP_MAXTHR) */
+ int ltp_max_count;
+
+ /* Max pending + paused + idle tasks, negated when ltp_finishing */
+ int ltp_max_pending;
+
+ int ltp_pending_count; /* Pending + paused + idle tasks */
+ int ltp_active_count; /* Active, not paused/idle tasks */
+ int ltp_open_count; /* Number of threads, negated when ltp_pause */
+ int ltp_starting; /* Currenlty starting threads */
+
+ /* >0 if paused or we may open a thread, <0 if we should close a thread.
+ * Updated when ltp_<finishing/pause/max_count/open_count> change.
+ * Maintained to reduce the time ltp_mutex must be locked in
+ * ldap_pvt_thread_pool_<submit/wrapper>().
+ */
+ int ltp_vary_open_count;
+# define SET_VARY_OPEN_COUNT(pool) \
+ ((pool)->ltp_vary_open_count = \
+ (pool)->ltp_pause ? 1 : \
+ (pool)->ltp_finishing ? -1 : \
+ ((pool)->ltp_max_count ? (pool)->ltp_max_count : LDAP_MAXTHR) \
+ - (pool)->ltp_open_count)
+};
+
+static ldap_int_tpool_plist_t empty_pending_list =
+ LDAP_STAILQ_HEAD_INITIALIZER(empty_pending_list);
+
+static int ldap_int_has_thread_pool = 0;
+static LDAP_STAILQ_HEAD(tpq, ldap_int_thread_pool_s)
+ ldap_int_thread_pool_list =
+ LDAP_STAILQ_HEAD_INITIALIZER(ldap_int_thread_pool_list);
+
+static ldap_pvt_thread_mutex_t ldap_pvt_thread_pool_mutex;
+
+static void *ldap_int_thread_pool_wrapper( void *pool );
+
+static ldap_pvt_thread_key_t ldap_tpool_key;
+
+/* Context of the main thread */
+static ldap_int_thread_userctx_t ldap_int_main_thrctx;
+
+int
+ldap_int_thread_pool_startup ( void )
+{
+ ldap_int_main_thrctx.ltu_id = ldap_pvt_thread_self();
+ ldap_pvt_thread_key_create( &ldap_tpool_key );
+ return ldap_pvt_thread_mutex_init(&ldap_pvt_thread_pool_mutex);
+}
+
+int
+ldap_int_thread_pool_shutdown ( void )
+{
+ struct ldap_int_thread_pool_s *pool;
+
+ while ((pool = LDAP_STAILQ_FIRST(&ldap_int_thread_pool_list)) != NULL) {
+ (ldap_pvt_thread_pool_destroy)(&pool, 0); /* ignore thr_debug macro */
+ }
+ ldap_pvt_thread_mutex_destroy(&ldap_pvt_thread_pool_mutex);
+ ldap_pvt_thread_key_destroy( ldap_tpool_key );
+ return(0);
+}
+
+
+/* Create a thread pool */
+int
+ldap_pvt_thread_pool_init (
+ ldap_pvt_thread_pool_t *tpool,
+ int max_threads,
+ int max_pending )
+{
+ ldap_pvt_thread_pool_t pool;
+ int rc;
+
+ /* multiple pools are currently not supported (ITS#4943) */
+ assert(!ldap_int_has_thread_pool);
+
+ if (! (0 <= max_threads && max_threads <= LDAP_MAXTHR))
+ max_threads = 0;
+ if (! (1 <= max_pending && max_pending <= MAX_PENDING))
+ max_pending = MAX_PENDING;
+
+ *tpool = NULL;
+ pool = (ldap_pvt_thread_pool_t) LDAP_CALLOC(1,
+ sizeof(struct ldap_int_thread_pool_s));
+
+ if (pool == NULL) return(-1);
+
+ rc = ldap_pvt_thread_mutex_init(&pool->ltp_mutex);
+ if (rc != 0) {
+fail1:
+ LDAP_FREE(pool);
+ return(rc);
+ }
+ rc = ldap_pvt_thread_cond_init(&pool->ltp_cond);
+ if (rc != 0) {
+fail2:
+ ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
+ goto fail1;
+ }
+ rc = ldap_pvt_thread_cond_init(&pool->ltp_pcond);
+ if (rc != 0) {
+ ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
+ goto fail2;
+ }
+
+ ldap_int_has_thread_pool = 1;
+
+ pool->ltp_max_count = max_threads;
+ SET_VARY_OPEN_COUNT(pool);
+ pool->ltp_max_pending = max_pending;
+
+ LDAP_STAILQ_INIT(&pool->ltp_pending_list);
+ pool->ltp_work_list = &pool->ltp_pending_list;
+ LDAP_SLIST_INIT(&pool->ltp_free_list);
+
+ ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
+ LDAP_STAILQ_INSERT_TAIL(&ldap_int_thread_pool_list, pool, ltp_next);
+ ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
+
+ /* Start no threads just yet. That can break if the process forks
+ * later, as slapd does in order to daemonize. On at least POSIX,
+ * only the forking thread would survive in the child. Yet fork()
+ * can't unlock/clean up other threads' locks and data structures,
+ * unless pthread_atfork() handlers have been set up to do so.
+ */
+
+ *tpool = pool;
+ return(0);
+}
+
+
+/* Submit a task to be performed by the thread pool */
+int
+ldap_pvt_thread_pool_submit (
+ ldap_pvt_thread_pool_t *tpool,
+ ldap_pvt_thread_start_t *start_routine, void *arg )
+{
+ struct ldap_int_thread_pool_s *pool;
+ ldap_int_thread_task_t *task;
+ ldap_pvt_thread_t thr;
+
+ if (tpool == NULL)
+ return(-1);
+
+ pool = *tpool;
+
+ if (pool == NULL)
+ return(-1);
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+
+ if (pool->ltp_pending_count >= pool->ltp_max_pending)
+ goto failed;
+
+ task = LDAP_SLIST_FIRST(&pool->ltp_free_list);
+ if (task) {
+ LDAP_SLIST_REMOVE_HEAD(&pool->ltp_free_list, ltt_next.l);
+ } else {
+ task = (ldap_int_thread_task_t *) LDAP_MALLOC(sizeof(*task));
+ if (task == NULL)
+ goto failed;
+ }
+
+ task->ltt_start_routine = start_routine;
+ task->ltt_arg = arg;
+
+ pool->ltp_pending_count++;
+ LDAP_STAILQ_INSERT_TAIL(&pool->ltp_pending_list, task, ltt_next.q);
+
+ /* true if ltp_pause != 0 or we should open (create) a thread */
+ if (pool->ltp_vary_open_count > 0 &&
+ pool->ltp_open_count < pool->ltp_active_count+pool->ltp_pending_count)
+ {
+ if (pool->ltp_pause)
+ goto done;
+
+ pool->ltp_starting++;
+ pool->ltp_open_count++;
+ SET_VARY_OPEN_COUNT(pool);
+
+ if (0 != ldap_pvt_thread_create(
+ &thr, 1, ldap_int_thread_pool_wrapper, pool))
+ {
+ /* couldn't create thread. back out of
+ * ltp_open_count and check for even worse things.
+ */
+ pool->ltp_starting--;
+ pool->ltp_open_count--;
+ SET_VARY_OPEN_COUNT(pool);
+
+ if (pool->ltp_open_count == 0) {
+ /* no open threads at all?!?
+ */
+ ldap_int_thread_task_t *ptr;
+
+ /* let pool_destroy know there are no more threads */
+ ldap_pvt_thread_cond_signal(&pool->ltp_cond);
+
+ LDAP_STAILQ_FOREACH(ptr, &pool->ltp_pending_list, ltt_next.q)
+ if (ptr == task) break;
+ if (ptr == task) {
+ /* no open threads, task not handled, so
+ * back out of ltp_pending_count, free the task,
+ * report the error.
+ */
+ pool->ltp_pending_count--;
+ LDAP_STAILQ_REMOVE(&pool->ltp_pending_list, task,
+ ldap_int_thread_task_s, ltt_next.q);
+ LDAP_SLIST_INSERT_HEAD(&pool->ltp_free_list, task,
+ ltt_next.l);
+ goto failed;
+ }
+ }
+ /* there is another open thread, so this
+ * task will be handled eventually.
+ */
+ }
+ }
+ ldap_pvt_thread_cond_signal(&pool->ltp_cond);
+
+ done:
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+ return(0);
+
+ failed:
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+ return(-1);
+}
+
+static void *
+no_task( void *ctx, void *arg )
+{
+ return NULL;
+}
+
+/* Cancel a pending task that was previously submitted.
+ * Return 1 if the task was successfully cancelled, 0 if
+ * not found, -1 for invalid parameters
+ */
+int
+ldap_pvt_thread_pool_retract (
+ ldap_pvt_thread_pool_t *tpool,
+ ldap_pvt_thread_start_t *start_routine, void *arg )
+{
+ struct ldap_int_thread_pool_s *pool;
+ ldap_int_thread_task_t *task;
+
+ if (tpool == NULL)
+ return(-1);
+
+ pool = *tpool;
+
+ if (pool == NULL)
+ return(-1);
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+ LDAP_STAILQ_FOREACH(task, &pool->ltp_pending_list, ltt_next.q)
+ if (task->ltt_start_routine == start_routine &&
+ task->ltt_arg == arg) {
+ /* Could LDAP_STAILQ_REMOVE the task, but that
+ * walks ltp_pending_list again to find it.
+ */
+ task->ltt_start_routine = no_task;
+ task->ltt_arg = NULL;
+ break;
+ }
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+ return task != NULL;
+}
+
+/* Set max #threads. value <= 0 means max supported #threads (LDAP_MAXTHR) */
+int
+ldap_pvt_thread_pool_maxthreads(
+ ldap_pvt_thread_pool_t *tpool,
+ int max_threads )
+{
+ struct ldap_int_thread_pool_s *pool;
+
+ if (! (0 <= max_threads && max_threads <= LDAP_MAXTHR))
+ max_threads = 0;
+
+ if (tpool == NULL)
+ return(-1);
+
+ pool = *tpool;
+
+ if (pool == NULL)
+ return(-1);
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+
+ pool->ltp_max_count = max_threads;
+ SET_VARY_OPEN_COUNT(pool);
+
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+ return(0);
+}
+
+/* Inspect the pool */
+int
+ldap_pvt_thread_pool_query(
+ ldap_pvt_thread_pool_t *tpool,
+ ldap_pvt_thread_pool_param_t param,
+ void *value )
+{
+ struct ldap_int_thread_pool_s *pool;
+ int count = -1;
+
+ if ( tpool == NULL || value == NULL ) {
+ return -1;
+ }
+
+ pool = *tpool;
+
+ if ( pool == NULL ) {
+ return 0;
+ }
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+ switch ( param ) {
+ case LDAP_PVT_THREAD_POOL_PARAM_MAX:
+ count = pool->ltp_max_count;
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_MAX_PENDING:
+ count = pool->ltp_max_pending;
+ if (count < 0)
+ count = -count;
+ if (count == MAX_PENDING)
+ count = 0;
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_OPEN:
+ count = pool->ltp_open_count;
+ if (count < 0)
+ count = -count;
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_STARTING:
+ count = pool->ltp_starting;
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_ACTIVE:
+ count = pool->ltp_active_count;
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_PAUSING:
+ count = (pool->ltp_pause != 0);
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_PENDING:
+ count = pool->ltp_pending_count;
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD:
+ count = pool->ltp_pending_count + pool->ltp_active_count;
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_ACTIVE_MAX:
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_PENDING_MAX:
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD_MAX:
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_STATE:
+ *((char **)value) =
+ pool->ltp_pause ? "pausing" :
+ !pool->ltp_finishing ? "running" :
+ pool->ltp_pending_count ? "finishing" : "stopping";
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_UNKNOWN:
+ break;
+ }
+ ldap_pvt_thread_mutex_unlock( &pool->ltp_mutex );
+
+ if ( count > -1 ) {
+ *((int *)value) = count;
+ }
+
+ return ( count == -1 ? -1 : 0 );
+}
+
+/*
+ * true if pool is pausing; does not lock any mutex to check.
+ * 0 if not pause, 1 if pause, -1 if error or no pool.
+ */
+int
+ldap_pvt_thread_pool_pausing( ldap_pvt_thread_pool_t *tpool )
+{
+ int rc = -1;
+ struct ldap_int_thread_pool_s *pool;
+
+ if ( tpool != NULL && (pool = *tpool) != NULL ) {
+ rc = (pool->ltp_pause != 0);
+ }
+
+ return rc;
+}
+
+/*
+ * wrapper for ldap_pvt_thread_pool_query(), left around
+ * for backwards compatibility
+ */
+int
+ldap_pvt_thread_pool_backload ( ldap_pvt_thread_pool_t *tpool )
+{
+ int rc, count;
+
+ rc = ldap_pvt_thread_pool_query( tpool,
+ LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD, (void *)&count );
+
+ if ( rc == 0 ) {
+ return count;
+ }
+
+ return rc;
+}
+
+/* Destroy the pool after making its threads finish */
+int
+ldap_pvt_thread_pool_destroy ( ldap_pvt_thread_pool_t *tpool, int run_pending )
+{
+ struct ldap_int_thread_pool_s *pool, *pptr;
+ ldap_int_thread_task_t *task;
+
+ if (tpool == NULL)
+ return(-1);
+
+ pool = *tpool;
+
+ if (pool == NULL) return(-1);
+
+ ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
+ LDAP_STAILQ_FOREACH(pptr, &ldap_int_thread_pool_list, ltp_next)
+ if (pptr == pool) break;
+ if (pptr == pool)
+ LDAP_STAILQ_REMOVE(&ldap_int_thread_pool_list, pool,
+ ldap_int_thread_pool_s, ltp_next);
+ ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
+
+ if (pool != pptr) return(-1);
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+
+ pool->ltp_finishing = 1;
+ SET_VARY_OPEN_COUNT(pool);
+ if (pool->ltp_max_pending > 0)
+ pool->ltp_max_pending = -pool->ltp_max_pending;
+
+ if (!run_pending) {
+ while ((task = LDAP_STAILQ_FIRST(&pool->ltp_pending_list)) != NULL) {
+ LDAP_STAILQ_REMOVE_HEAD(&pool->ltp_pending_list, ltt_next.q);
+ LDAP_FREE(task);
+ }
+ pool->ltp_pending_count = 0;
+ }
+
+ while (pool->ltp_open_count) {
+ if (!pool->ltp_pause)
+ ldap_pvt_thread_cond_broadcast(&pool->ltp_cond);
+ ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex);
+ }
+
+ while ((task = LDAP_SLIST_FIRST(&pool->ltp_free_list)) != NULL)
+ {
+ LDAP_SLIST_REMOVE_HEAD(&pool->ltp_free_list, ltt_next.l);
+ LDAP_FREE(task);
+ }
+
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+ ldap_pvt_thread_cond_destroy(&pool->ltp_pcond);
+ ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
+ ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
+ LDAP_FREE(pool);
+ *tpool = NULL;
+ ldap_int_has_thread_pool = 0;
+ return(0);
+}
+
+/* Thread loop. Accept and handle submitted tasks. */
+static void *
+ldap_int_thread_pool_wrapper (
+ void *xpool )
+{
+ struct ldap_int_thread_pool_s *pool = xpool;
+ ldap_int_thread_task_t *task;
+ ldap_int_tpool_plist_t *work_list;
+ ldap_int_thread_userctx_t ctx, *kctx;
+ unsigned i, keyslot, hash;
+
+ assert(pool != NULL);
+
+ for ( i=0; i<MAXKEYS; i++ ) {
+ ctx.ltu_key[i].ltk_key = NULL;
+ }
+
+ ctx.ltu_id = ldap_pvt_thread_self();
+ TID_HASH(ctx.ltu_id, hash);
+
+ ldap_pvt_thread_key_setdata( ldap_tpool_key, &ctx );
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+
+ /* thread_keys[] is read-only when paused */
+ while (pool->ltp_pause)
+ ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex);
+
+ /* find a key slot to give this thread ID and store a
+ * pointer to our keys there; start at the thread ID
+ * itself (mod LDAP_MAXTHR) and look for an empty slot.
+ */
+ ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
+ for (keyslot = hash & (LDAP_MAXTHR-1);
+ (kctx = thread_keys[keyslot].ctx) && kctx != DELETED_THREAD_CTX;
+ keyslot = (keyslot+1) & (LDAP_MAXTHR-1));
+ thread_keys[keyslot].ctx = &ctx;
+ ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
+
+ pool->ltp_starting--;
+ pool->ltp_active_count++;
+
+ for (;;) {
+ work_list = pool->ltp_work_list; /* help the compiler a bit */
+ task = LDAP_STAILQ_FIRST(work_list);
+ if (task == NULL) { /* paused or no pending tasks */
+ if (--(pool->ltp_active_count) < 2) {
+ /* Notify pool_pause it is the sole active thread. */
+ ldap_pvt_thread_cond_signal(&pool->ltp_pcond);
+ }
+
+ do {
+ if (pool->ltp_vary_open_count < 0) {
+ /* Not paused, and either finishing or too many
+ * threads running (can happen if ltp_max_count
+ * was reduced). Let this thread die.
+ */
+ goto done;
+ }
+
+ /* We could check an idle timer here, and let the
+ * thread die if it has been inactive for a while.
+ * Only die if there are other open threads (i.e.,
+ * always have at least one thread open).
+ * The check should be like this:
+ * if (pool->ltp_open_count>1 && pool->ltp_starting==0)
+ * check timer, wait if ltp_pause, leave thread;
+ *
+ * Just use pthread_cond_timedwait() if we want to
+ * check idle time.
+ */
+ ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex);
+
+ work_list = pool->ltp_work_list;
+ task = LDAP_STAILQ_FIRST(work_list);
+ } while (task == NULL);
+
+ pool->ltp_active_count++;
+ }
+
+ LDAP_STAILQ_REMOVE_HEAD(work_list, ltt_next.q);
+ pool->ltp_pending_count--;
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+
+ task->ltt_start_routine(&ctx, task->ltt_arg);
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+ LDAP_SLIST_INSERT_HEAD(&pool->ltp_free_list, task, ltt_next.l);
+ }
+ done:
+
+ assert(!pool->ltp_pause); /* thread_keys writable, ltp_open_count >= 0 */
+
+ /* The ltp_mutex lock protects ctx->ltu_key from pool_purgekey()
+ * during this call, since it prevents new pauses. */
+ ldap_pvt_thread_pool_context_reset(&ctx);
+
+ ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
+ thread_keys[keyslot].ctx = DELETED_THREAD_CTX;
+ ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
+
+ pool->ltp_open_count--;
+ SET_VARY_OPEN_COUNT(pool);
+ /* let pool_destroy know we're all done */
+ if (pool->ltp_open_count == 0)
+ ldap_pvt_thread_cond_signal(&pool->ltp_cond);
+
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+
+ ldap_pvt_thread_exit(NULL);
+ return(NULL);
+}
+
+/* Arguments > ltp_pause to handle_pause(,PAUSE_ARG()). arg=PAUSE_ARG
+ * ensures (arg-ltp_pause) sets GO_* at need and keeps DO_PAUSE/GO_*.
+ */
+#define GO_IDLE 8
+#define GO_UNIDLE 16
+#define CHECK_PAUSE 32 /* if ltp_pause: GO_IDLE; wait; GO_UNIDLE */
+#define DO_PAUSE 64 /* CHECK_PAUSE; pause the pool */
+#define PAUSE_ARG(a) \
+ ((a) | ((a) & (GO_IDLE|GO_UNIDLE) ? GO_IDLE-1 : CHECK_PAUSE))
+
+static int
+handle_pause( ldap_pvt_thread_pool_t *tpool, int pause_type )
+{
+ struct ldap_int_thread_pool_s *pool;
+ int ret = 0, pause, max_ltp_pause;
+
+ if (tpool == NULL)
+ return(-1);
+
+ pool = *tpool;
+
+ if (pool == NULL)
+ return(0);
+
+ if (pause_type == CHECK_PAUSE && !pool->ltp_pause)
+ return(0);
+
+ /* Let pool_unidle() ignore requests for new pauses */
+ max_ltp_pause = pause_type==PAUSE_ARG(GO_UNIDLE) ? WANT_PAUSE : NOT_PAUSED;
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+
+ pause = pool->ltp_pause; /* NOT_PAUSED, WANT_PAUSE or PAUSED */
+
+ /* If ltp_pause and not GO_IDLE|GO_UNIDLE: Set GO_IDLE,GO_UNIDLE */
+ pause_type -= pause;
+
+ if (pause_type & GO_IDLE) {
+ pool->ltp_pending_count++;
+ pool->ltp_active_count--;
+ if (pause && pool->ltp_active_count < 2) {
+ /* Tell the task waiting to DO_PAUSE it can proceed */
+ ldap_pvt_thread_cond_signal(&pool->ltp_pcond);
+ }
+ }
+
+ if (pause_type & GO_UNIDLE) {
+ /* Wait out pause if any, then cancel GO_IDLE */
+ if (pause > max_ltp_pause) {
+ ret = 1;
+ do {
+ ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex);
+ } while (pool->ltp_pause > max_ltp_pause);
+ }
+ pool->ltp_pending_count--;
+ pool->ltp_active_count++;
+ }
+
+ if (pause_type & DO_PAUSE) {
+ /* Tell everyone else to pause or finish, then await that */
+ ret = 0;
+ assert(!pool->ltp_pause);
+ pool->ltp_pause = WANT_PAUSE;
+ /* Let ldap_pvt_thread_pool_submit() through to its ltp_pause test,
+ * and do not finish threads in ldap_pvt_thread_pool_wrapper() */
+ pool->ltp_open_count = -pool->ltp_open_count;
+ SET_VARY_OPEN_COUNT(pool);
+ /* Hide pending tasks from ldap_pvt_thread_pool_wrapper() */
+ pool->ltp_work_list = &empty_pending_list;
+ /* Wait for this task to become the sole active task */
+ while (pool->ltp_active_count > 1) {
+ ldap_pvt_thread_cond_wait(&pool->ltp_pcond, &pool->ltp_mutex);
+ }
+ assert(pool->ltp_pause == WANT_PAUSE);
+ pool->ltp_pause = PAUSED;
+ }
+
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+ return(ret);
+}
+
+/* Consider this task idle: It will not block pool_pause() in other tasks. */
+void
+ldap_pvt_thread_pool_idle( ldap_pvt_thread_pool_t *tpool )
+{
+ handle_pause(tpool, PAUSE_ARG(GO_IDLE));
+}
+
+/* Cancel pool_idle(). If the pool is paused, wait it out first. */
+void
+ldap_pvt_thread_pool_unidle( ldap_pvt_thread_pool_t *tpool )
+{
+ handle_pause(tpool, PAUSE_ARG(GO_UNIDLE));
+}
+
+/*
+ * If a pause was requested, wait for it. If several threads
+ * are waiting to pause, let through one or more pauses.
+ * The calling task must be active, not idle.
+ * Return 1 if we waited, 0 if not, -1 at parameter error.
+ */
+int
+ldap_pvt_thread_pool_pausecheck( ldap_pvt_thread_pool_t *tpool )
+{
+ return handle_pause(tpool, PAUSE_ARG(CHECK_PAUSE));
+}
+
+/*
+ * Pause the pool. The calling task must be active, not idle.
+ * Return when all other tasks are paused or idle.
+ */
+int
+ldap_pvt_thread_pool_pause( ldap_pvt_thread_pool_t *tpool )
+{
+ return handle_pause(tpool, PAUSE_ARG(DO_PAUSE));
+}
+
+/* End a pause */
+int
+ldap_pvt_thread_pool_resume (
+ ldap_pvt_thread_pool_t *tpool )
+{
+ struct ldap_int_thread_pool_s *pool;
+
+ if (tpool == NULL)
+ return(-1);
+
+ pool = *tpool;
+
+ if (pool == NULL)
+ return(0);
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+
+ assert(pool->ltp_pause == PAUSED);
+ pool->ltp_pause = 0;
+ if (pool->ltp_open_count <= 0) /* true when paused, but be paranoid */
+ pool->ltp_open_count = -pool->ltp_open_count;
+ SET_VARY_OPEN_COUNT(pool);
+ pool->ltp_work_list = &pool->ltp_pending_list;
+
+ ldap_pvt_thread_cond_broadcast(&pool->ltp_cond);
+
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+ return(0);
+}
+
+/*
+ * Get the key's data and optionally free function in the given context.
+ */
+int ldap_pvt_thread_pool_getkey(
+ void *xctx,
+ void *key,
+ void **data,
+ ldap_pvt_thread_pool_keyfree_t **kfree )
+{
+ ldap_int_thread_userctx_t *ctx = xctx;
+ int i;
+
+ if ( !ctx || !key || !data ) return EINVAL;
+
+ for ( i=0; i<MAXKEYS && ctx->ltu_key[i].ltk_key; i++ ) {
+ if ( ctx->ltu_key[i].ltk_key == key ) {
+ *data = ctx->ltu_key[i].ltk_data;
+ if ( kfree ) *kfree = ctx->ltu_key[i].ltk_free;
+ return 0;
+ }
+ }
+ return ENOENT;
+}
+
+static void
+clear_key_idx( ldap_int_thread_userctx_t *ctx, int i )
+{
+ for ( ; i < MAXKEYS-1 && ctx->ltu_key[i+1].ltk_key; i++ )
+ ctx->ltu_key[i] = ctx->ltu_key[i+1];
+ ctx->ltu_key[i].ltk_key = NULL;
+}
+
+/*
+ * Set or remove data for the key in the given context.
+ * key can be any unique pointer.
+ * kfree() is an optional function to free the data (but not the key):
+ * pool_context_reset() and pool_purgekey() call kfree(key, data),
+ * but pool_setkey() does not. For pool_setkey() it is the caller's
+ * responsibility to free any existing data with the same key.
+ * kfree() must not call functions taking a tpool argument.
+ */
+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 )
+{
+ ldap_int_thread_userctx_t *ctx = xctx;
+ int i, found;
+
+ if ( !ctx || !key ) return EINVAL;
+
+ for ( i=found=0; i<MAXKEYS; i++ ) {
+ if ( ctx->ltu_key[i].ltk_key == key ) {
+ found = 1;
+ break;
+ } else if ( !ctx->ltu_key[i].ltk_key ) {
+ break;
+ }
+ }
+
+ if ( olddatap ) {
+ if ( found ) {
+ *olddatap = ctx->ltu_key[i].ltk_data;
+ } else {
+ *olddatap = NULL;
+ }
+ }
+
+ if ( oldkfreep ) {
+ if ( found ) {
+ *oldkfreep = ctx->ltu_key[i].ltk_free;
+ } else {
+ *oldkfreep = 0;
+ }
+ }
+
+ if ( data || kfree ) {
+ if ( i>=MAXKEYS )
+ return ENOMEM;
+ ctx->ltu_key[i].ltk_key = key;
+ ctx->ltu_key[i].ltk_data = data;
+ ctx->ltu_key[i].ltk_free = kfree;
+ } else if ( found ) {
+ clear_key_idx( ctx, i );
+ }
+
+ return 0;
+}
+
+/* Free all elements with this key, no matter which thread they're in.
+ * May only be called while the pool is paused.
+ */
+void ldap_pvt_thread_pool_purgekey( void *key )
+{
+ int i, j;
+ ldap_int_thread_userctx_t *ctx;
+
+ assert ( key != NULL );
+
+ for ( i=0; i<LDAP_MAXTHR; i++ ) {
+ ctx = thread_keys[i].ctx;
+ if ( ctx && ctx != DELETED_THREAD_CTX ) {
+ for ( j=0; j<MAXKEYS && ctx->ltu_key[j].ltk_key; j++ ) {
+ if ( ctx->ltu_key[j].ltk_key == key ) {
+ if (ctx->ltu_key[j].ltk_free)
+ ctx->ltu_key[j].ltk_free( ctx->ltu_key[j].ltk_key,
+ ctx->ltu_key[j].ltk_data );
+ clear_key_idx( ctx, j );
+ break;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Find the context of the current thread.
+ * This is necessary if the caller does not have access to the
+ * thread context handle (for example, a slapd plugin calling
+ * slapi_search_internal()). No doubt it is more efficient
+ * for the application to keep track of the thread context
+ * handles itself.
+ */
+void *ldap_pvt_thread_pool_context( )
+{
+ void *ctx = NULL;
+
+ ldap_pvt_thread_key_getdata( ldap_tpool_key, &ctx );
+ return ctx ? ctx : (void *) &ldap_int_main_thrctx;
+}
+
+/*
+ * Free the context's keys.
+ * Must not call functions taking a tpool argument (because this
+ * thread already holds ltp_mutex when called from pool_wrapper()).
+ */
+void ldap_pvt_thread_pool_context_reset( void *vctx )
+{
+ ldap_int_thread_userctx_t *ctx = vctx;
+ int i;
+
+ for ( i=MAXKEYS-1; i>=0; i--) {
+ if ( !ctx->ltu_key[i].ltk_key )
+ continue;
+ if ( ctx->ltu_key[i].ltk_free )
+ ctx->ltu_key[i].ltk_free( ctx->ltu_key[i].ltk_key,
+ ctx->ltu_key[i].ltk_data );
+ ctx->ltu_key[i].ltk_key = NULL;
+ }
+}
+
+ldap_pvt_thread_t ldap_pvt_thread_pool_tid( void *vctx )
+{
+ ldap_int_thread_userctx_t *ctx = vctx;
+
+ return ctx->ltu_id;
+}
+#endif /* LDAP_THREAD_HAVE_TPOOL */