]> git.sur5r.net Git - openldap/blobdiff - libraries/libldap_r/tpool.c
Patch: ucdata 2.4 bugs (ITS#1751)
[openldap] / libraries / libldap_r / tpool.c
index af0c4f739d63069105f6118fd9cc70fe59f8bef9..923a1b9ce4e23c94e543c87b1e8fab17288e8744 100644 (file)
@@ -1,6 +1,6 @@
 /* $OpenLDAP$ */
 /*
- * Copyright 1998-2000 The OpenLDAP Foundation, Redwood City, California, USA
+ * Copyright 1998-2002 The OpenLDAP Foundation, Redwood City, California, USA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms are permitted only
 #include "portable.h"
 
 #include <stdio.h>
-#include <stdarg.h>
 
+#include <ac/stdarg.h>
 #include <ac/stdlib.h>
 #include <ac/string.h>
 #include <ac/time.h>
 
 #include "ldap-int.h"
 #include "ldap_pvt_thread.h"
+#include "ldap_queue.h"
 
 #ifndef LDAP_THREAD_HAVE_TPOOL
 
@@ -29,38 +30,37 @@ enum ldap_int_thread_pool_state {
        LDAP_INT_THREAD_POOL_STOPPING
 };
 
-typedef struct ldap_int_thread_list_element_s {
-       struct ldap_int_thread_list_element_s *next;
-} ldap_int_thread_list_element_t, *ldap_int_thread_list_t;
+typedef struct ldap_int_thread_ctx_s {
+       union {
+       LDAP_STAILQ_ENTRY(ldap_int_thread_ctx_s) q;
+       LDAP_SLIST_ENTRY(ldap_int_thread_ctx_s) l;
+       } ltc_next;
+       void *(*ltc_start_routine)( void *);
+       void *ltc_arg;
+} ldap_int_thread_ctx_t;
 
 struct ldap_int_thread_pool_s {
-       struct ldap_int_thread_pool_s *ltp_next;
+       LDAP_STAILQ_ENTRY(ldap_int_thread_pool_s) ltp_next;
        ldap_pvt_thread_mutex_t ltp_mutex;
        ldap_pvt_thread_cond_t ltp_cond;
-       ldap_int_thread_list_t ltp_pending_list;
+       LDAP_STAILQ_HEAD(tcq, ldap_int_thread_ctx_s) ltp_pending_list;
+       LDAP_SLIST_HEAD(tcl, ldap_int_thread_ctx_s) ltp_free_list;
        long ltp_state;
        long ltp_max_count;
        long ltp_max_pending;
        long ltp_pending_count;
        long ltp_active_count;
        long ltp_open_count;
+       long ltp_starting;
 };
 
-typedef struct ldap_int_thread_ctx_s {
-       struct ldap_int_thread_ctx_s *ltc_next;
-       void *(*ltc_start_routine)( void *);
-       void *ltc_arg;
-} ldap_int_thread_ctx_t;
+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_int_thread_list_t ldap_int_thread_pool_list = NULL;
 static ldap_pvt_thread_mutex_t ldap_pvt_thread_pool_mutex;
 
-static void *ldap_int_thread_pool_wrapper(
-       struct ldap_int_thread_pool_s *pool );
-
-static void *ldap_int_thread_enlist( ldap_int_thread_list_t *list, void *elem );
-static void *ldap_int_thread_delist( ldap_int_thread_list_t *list, void *elem );
-static void *ldap_int_thread_onlist( ldap_int_thread_list_t *list, void *elem );
+static void *ldap_int_thread_pool_wrapper( void *pool );
 
 int
 ldap_int_thread_pool_startup ( void )
@@ -71,10 +71,10 @@ ldap_int_thread_pool_startup ( void )
 int
 ldap_int_thread_pool_shutdown ( void )
 {
-       while (ldap_int_thread_pool_list != NULL) {
-               struct ldap_int_thread_pool_s *pool =
-                       (struct ldap_int_thread_pool_s *) ldap_int_thread_pool_list;
+       struct ldap_int_thread_pool_s *pool;
 
+       while ((pool = LDAP_STAILQ_FIRST(&ldap_int_thread_pool_list)) != NULL) {
+               LDAP_STAILQ_REMOVE_HEAD(&ldap_int_thread_pool_list, ltp_next);
                ldap_pvt_thread_pool_destroy( &pool, 0);
        }
        ldap_pvt_thread_mutex_destroy(&ldap_pvt_thread_pool_mutex);
@@ -84,12 +84,11 @@ ldap_int_thread_pool_shutdown ( void )
 int
 ldap_pvt_thread_pool_init (
        ldap_pvt_thread_pool_t *tpool,
-       int max_concurrency,
+       int max_threads,
        int max_pending )
 {
-       int rc;
        ldap_pvt_thread_pool_t pool;
-       ldap_pvt_thread_t thr;
+       int rc;
 
        *tpool = NULL;
        pool = (ldap_pvt_thread_pool_t) LDAP_CALLOC(1,
@@ -97,31 +96,53 @@ ldap_pvt_thread_pool_init (
 
        if (pool == NULL) return(-1);
 
-       ldap_pvt_thread_mutex_init(&pool->ltp_mutex);
-       ldap_pvt_thread_cond_init(&pool->ltp_cond);
+       rc = ldap_pvt_thread_mutex_init(&pool->ltp_mutex);
+       if (rc != 0)
+               return(rc);
+       rc = ldap_pvt_thread_cond_init(&pool->ltp_cond);
+       if (rc != 0)
+               return(rc);
        pool->ltp_state = LDAP_INT_THREAD_POOL_RUNNING;
-       pool->ltp_max_count = max_concurrency;
+       pool->ltp_max_count = max_threads;
        pool->ltp_max_pending = max_pending;
+       LDAP_STAILQ_INIT(&pool->ltp_pending_list);
+       LDAP_SLIST_INIT(&pool->ltp_free_list);
        ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
-       ldap_int_thread_enlist(&ldap_int_thread_pool_list, pool);
+       LDAP_STAILQ_INSERT_TAIL(&ldap_int_thread_pool_list, pool, ltp_next);
        ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
 
-       /* start up one thread, just so there is one */
+#if 0
+       /* THIS WILL NOT WORK on some systems.  If the process
+        * forks after starting a thread, there is no guarantee
+        * that the thread will survive the fork.  For example,
+        * slapd forks in order to daemonize, and does so after
+        * calling ldap_pvt_thread_pool_init.  On some systems,
+        * this initial thread does not run in the child process,
+        * but ltp_open_count == 1, so two things happen: 
+        * 1) the first client connection fails, and 2) when
+        * slapd is kill'ed, it never terminates since it waits
+        * for all worker threads to exit. */
+
+       /* start up one thread, just so there is one. no need to
+        * lock the mutex right now, since no threads are running.
+        */
        pool->ltp_open_count++;
 
-       rc = ldap_pvt_thread_create( &thr, 1,
-               (void *) ldap_int_thread_pool_wrapper, pool );
+       ldap_pvt_thread_t thr;
+       rc = ldap_pvt_thread_create( &thr, 1, ldap_int_thread_pool_wrapper, pool );
 
        if( rc != 0) {
                /* couldn't start one?  then don't start any */
                ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
-               ldap_int_thread_delist(&ldap_int_thread_pool_list, 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);
                ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
                ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
-               free(pool);
+               LDAP_FREE(pool);
                return(-1);
        }
+#endif
 
        *tpool = pool;
        return(0);
@@ -145,25 +166,31 @@ ldap_pvt_thread_pool_submit (
        if (pool == NULL)
                return(-1);
 
-       ctx = (ldap_int_thread_ctx_t *) LDAP_CALLOC(1,
-               sizeof(ldap_int_thread_ctx_t));
-
-       if (ctx == NULL) return(-1);
-
-       ctx->ltc_start_routine = start_routine;
-       ctx->ltc_arg = arg;
-
        ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
        if (pool->ltp_state != LDAP_INT_THREAD_POOL_RUNNING
                || (pool->ltp_max_pending > 0
                        && pool->ltp_pending_count >= pool->ltp_max_pending))
        {
                ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
-               free(ctx);
                return(-1);
        }
+       ctx = LDAP_SLIST_FIRST(&pool->ltp_free_list);
+       if (ctx) {
+               LDAP_SLIST_REMOVE_HEAD(&pool->ltp_free_list, ltc_next.l);
+       } else {
+               ctx = (ldap_int_thread_ctx_t *) LDAP_MALLOC(
+                       sizeof(ldap_int_thread_ctx_t));
+               if (ctx == NULL) {
+                       ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+                       return(-1);
+               }
+       }
+
+       ctx->ltc_start_routine = start_routine;
+       ctx->ltc_arg = arg;
+
        pool->ltp_pending_count++;
-       ldap_int_thread_enlist(&pool->ltp_pending_list, ctx);
+       LDAP_STAILQ_INSERT_TAIL(&pool->ltp_pending_list, ctx, ltc_next.q);
        ldap_pvt_thread_cond_signal(&pool->ltp_cond);
        if ((pool->ltp_open_count <= 0
                        || pool->ltp_pending_count > 1
@@ -172,46 +199,73 @@ ldap_pvt_thread_pool_submit (
                        || pool->ltp_open_count < pool->ltp_max_count))
        {
                pool->ltp_open_count++;
+               pool->ltp_starting++;
                need_thread = 1;
        }
        ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
 
        if (need_thread) {
                int rc = ldap_pvt_thread_create( &thr, 1,
-                       (void *)ldap_int_thread_pool_wrapper, pool );
-               if (rc != 0) {
+                       ldap_int_thread_pool_wrapper, pool );
+               ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+               if (rc == 0) {
+                       pool->ltp_starting--;
+               } else {
                        /* couldn't create thread.  back out of
                         * ltp_open_count and check for even worse things.
                         */
-                       ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
                        pool->ltp_open_count--;
+                       pool->ltp_starting--;
                        if (pool->ltp_open_count == 0) {
-                               /* no open threads at all?!?  this will never happen
-                                * because we always leave at least one thread open.
+                               /* no open threads at all?!?
                                 */
-                               if (ldap_int_thread_delist(&pool->ltp_pending_list, ctx)) {
+                               ldap_int_thread_ctx_t *ptr;
+                               LDAP_STAILQ_FOREACH(ptr, &pool->ltp_pending_list, ltc_next.q)
+                                       if (ptr == ctx) break;
+                               if (ptr == ctx) {
                                        /* no open threads, context not handled, so
                                         * back out of ltp_pending_count, free the context,
                                         * report the error.
                                         */
+                                       LDAP_STAILQ_REMOVE(&pool->ltp_pending_list, ctx, 
+                                               ldap_int_thread_ctx_s, ltc_next.q);
                                        pool->ltp_pending_count++;
                                        ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
-                                       free(ctx);
+                                       LDAP_FREE(ctx);
                                        return(-1);
                                }
                        }
-                       ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
                        /* there is another open thread, so this
                         * context will be handled eventually.
                         * continue on and signal that the context
                         * is waiting.
                         */
                }
+               ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
        }
 
        return(0);
 }
 
+int
+ldap_pvt_thread_pool_maxthreads ( ldap_pvt_thread_pool_t *tpool, int max_threads )
+{
+       struct ldap_int_thread_pool_s *pool;
+
+       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;
+       ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+       return(0);
+}
+
 int
 ldap_pvt_thread_pool_backload ( ldap_pvt_thread_pool_t *tpool )
 {
@@ -235,7 +289,7 @@ ldap_pvt_thread_pool_backload ( ldap_pvt_thread_pool_t *tpool )
 int
 ldap_pvt_thread_pool_destroy ( ldap_pvt_thread_pool_t *tpool, int run_pending )
 {
-       struct ldap_int_thread_pool_s *pool;
+       struct ldap_int_thread_pool_s *pool, *pptr;
        long waiting;
        ldap_int_thread_ctx_t *ctx;
 
@@ -247,17 +301,20 @@ ldap_pvt_thread_pool_destroy ( ldap_pvt_thread_pool_t *tpool, int run_pending )
        if (pool == NULL) return(-1);
 
        ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
-       pool = ldap_int_thread_delist(&ldap_int_thread_pool_list, pool);
+       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 == NULL) return(-1);
+       if (pool != pptr) return(-1);
 
        ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
        pool->ltp_state = run_pending
                ? LDAP_INT_THREAD_POOL_FINISHING
                : LDAP_INT_THREAD_POOL_STOPPING;
        waiting = pool->ltp_open_count;
-       ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
 
        /* broadcast could be used here, but only after
         * it is fixed in the NT thread implementation
@@ -265,6 +322,7 @@ ldap_pvt_thread_pool_destroy ( ldap_pvt_thread_pool_t *tpool, int run_pending )
        while (--waiting >= 0) {
                ldap_pvt_thread_cond_signal(&pool->ltp_cond);
        }
+       ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
 
        do {
                ldap_pvt_thread_yield();
@@ -273,22 +331,29 @@ ldap_pvt_thread_pool_destroy ( ldap_pvt_thread_pool_t *tpool, int run_pending )
                ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
        } while (waiting > 0);
 
-       while (ctx = (ldap_int_thread_ctx_t *)ldap_int_thread_delist(
-               &pool->ltp_pending_list, NULL))
+       while ((ctx = LDAP_STAILQ_FIRST(&pool->ltp_pending_list)) != NULL)
        {
-               free(ctx);
+               LDAP_STAILQ_REMOVE_HEAD(&pool->ltp_pending_list, ltc_next.q);
+               LDAP_FREE(ctx);
+       }
+
+       while ((ctx = LDAP_SLIST_FIRST(&pool->ltp_free_list)) != NULL)
+       {
+               LDAP_SLIST_REMOVE_HEAD(&pool->ltp_free_list, ltc_next.l);
+               LDAP_FREE(ctx);
        }
 
        ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
        ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
-       free(pool);
+       LDAP_FREE(pool);
        return(0);
 }
 
 static void *
 ldap_int_thread_pool_wrapper ( 
-       struct ldap_int_thread_pool_s *pool )
+       void *xpool )
 {
+       struct ldap_int_thread_pool_s *pool = xpool;
        ldap_int_thread_ctx_t *ctx;
 
        if (pool == NULL)
@@ -297,15 +362,30 @@ ldap_int_thread_pool_wrapper (
        ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
 
        while (pool->ltp_state != LDAP_INT_THREAD_POOL_STOPPING) {
-
-               ctx = ldap_int_thread_delist(&pool->ltp_pending_list, NULL);
-               if (ctx == NULL) {
+               ctx = LDAP_STAILQ_FIRST(&pool->ltp_pending_list);
+               if (ctx) {
+                       LDAP_STAILQ_REMOVE_HEAD(&pool->ltp_pending_list, ltc_next.q);
+               } else {
                        if (pool->ltp_state == LDAP_INT_THREAD_POOL_FINISHING)
                                break;
+                       if (pool->ltp_max_count > 0
+                               && pool->ltp_open_count > pool->ltp_max_count)
+                       {
+                               /* too many threads running (can happen if the
+                                * maximum threads value is set during ongoing
+                                * operation using ldap_pvt_thread_pool_maxthreads)
+                                * so let this thread die.
+                                */
+                               break;
+                       }
+
                        /* 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).
+                        * 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, leave thread (break;)
                         */
 
                        if (pool->ltp_state == LDAP_INT_THREAD_POOL_RUNNING)
@@ -319,7 +399,7 @@ ldap_int_thread_pool_wrapper (
                ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
 
                (ctx->ltc_start_routine)(ctx->ltc_arg);
-               free(ctx);
+               LDAP_SLIST_INSERT_HEAD(&pool->ltp_free_list, ctx, ltc_next.l);
                ldap_pvt_thread_yield();
 
                /* if we use an idle timer, here's
@@ -336,61 +416,4 @@ ldap_int_thread_pool_wrapper (
        ldap_pvt_thread_exit(NULL);
        return(NULL);
 }
-
-static void *
-ldap_int_thread_enlist( ldap_int_thread_list_t *list, void *elem )
-{
-       ldap_int_thread_list_element_t *prev;
-
-       if (elem == NULL) return(NULL);
-
-       ((ldap_int_thread_list_element_t *)elem)->next = NULL;
-       if (*list == NULL) {
-               *list = elem;
-               return(elem);
-       }
-
-       for (prev = *list ; prev->next != NULL; prev = prev->next) ;
-       prev->next = elem;
-       return(elem);
-}
-
-static void *
-ldap_int_thread_delist( ldap_int_thread_list_t *list, void *elem )
-{
-       ldap_int_thread_list_element_t *prev;
-
-       if (*list == NULL) return(NULL);
-
-       if (elem == NULL) elem = *list;
-
-       if (*list == elem) {
-               *list = ((ldap_int_thread_list_element_t *)elem)->next;
-               return(elem);
-       }
-
-       for (prev = *list ; prev->next != NULL; prev = prev->next) {
-               if (prev->next == elem) {
-                       prev->next = ((ldap_int_thread_list_element_t *)elem)->next;
-                       return(elem);
-               }
-       }
-       return(NULL);
-}
-
-static void *
-ldap_int_thread_onlist( ldap_int_thread_list_t *list, void *elem )
-{
-       ldap_int_thread_list_element_t *prev;
-
-       if (elem == NULL || *list == NULL) return(NULL);
-
-       for (prev = *list ; prev != NULL; prev = prev->next) {
-               if (prev == elem)
-                       return(elem);
-       }
-
-       return(NULL);
-}
-
 #endif /* LDAP_HAVE_THREAD_POOL */