]> git.sur5r.net Git - openldap/blob - libraries/libldap_r/tpool.c
fa159b3df0421b4653cf9df6e25ff2186944f8aa
[openldap] / libraries / libldap_r / tpool.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2000 The OpenLDAP Foundation, Redwood City, California, USA
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms are permitted only
7  * as authorized by the OpenLDAP Public License.  A copy of this
8  * license is available at http://www.OpenLDAP.org/license.html or
9  * in file LICENSE in the top-level directory of the distribution.
10  */
11
12 #include "portable.h"
13
14 #include <stdio.h>
15
16 #include <ac/stdarg.h>
17 #include <ac/stdlib.h>
18 #include <ac/string.h>
19 #include <ac/time.h>
20
21 #include "ldap-int.h"
22 #include "ldap_pvt_thread.h"
23 #include "ldap_queue.h"
24
25 #ifndef LDAP_THREAD_HAVE_TPOOL
26
27 enum ldap_int_thread_pool_state {
28         LDAP_INT_THREAD_POOL_RUNNING,
29         LDAP_INT_THREAD_POOL_FINISHING,
30         LDAP_INT_THREAD_POOL_STOPPING
31 };
32
33 typedef struct ldap_int_thread_ctx_s {
34         union {
35         LDAP_STAILQ_ENTRY(ldap_int_thread_ctx_s) q;
36         LDAP_SLIST_ENTRY(ldap_int_thread_ctx_s) l;
37         } ltc_next;
38         void *(*ltc_start_routine)( void *);
39         void *ltc_arg;
40 } ldap_int_thread_ctx_t;
41
42 struct ldap_int_thread_pool_s {
43         LDAP_STAILQ_ENTRY(ldap_int_thread_pool_s) ltp_next;
44         ldap_pvt_thread_mutex_t ltp_mutex;
45         ldap_pvt_thread_cond_t ltp_cond;
46         LDAP_STAILQ_HEAD(tcq, ldap_int_thread_ctx_s) ltp_pending_list;
47         LDAP_SLIST_HEAD(tcl, ldap_int_thread_ctx_s) ltp_free_list;
48         long ltp_state;
49         long ltp_max_count;
50         long ltp_max_pending;
51         long ltp_pending_count;
52         long ltp_active_count;
53         long ltp_open_count;
54         long ltp_starting;
55 };
56
57 static LDAP_STAILQ_HEAD(tpq, ldap_int_thread_pool_s)
58         ldap_int_thread_pool_list =
59         LDAP_STAILQ_HEAD_INITIALIZER(ldap_int_thread_pool_list);
60
61 static ldap_pvt_thread_mutex_t ldap_pvt_thread_pool_mutex;
62
63 static void *ldap_int_thread_pool_wrapper(
64         struct ldap_int_thread_pool_s *pool );
65
66 int
67 ldap_int_thread_pool_startup ( void )
68 {
69         return ldap_pvt_thread_mutex_init(&ldap_pvt_thread_pool_mutex);
70 }
71
72 int
73 ldap_int_thread_pool_shutdown ( void )
74 {
75         struct ldap_int_thread_pool_s *pool;
76
77         while ((pool = LDAP_STAILQ_FIRST(&ldap_int_thread_pool_list)) != NULL) {
78                 LDAP_STAILQ_REMOVE_HEAD(&ldap_int_thread_pool_list, ltp_next);
79                 ldap_pvt_thread_pool_destroy( &pool, 0);
80         }
81         ldap_pvt_thread_mutex_destroy(&ldap_pvt_thread_pool_mutex);
82         return(0);
83 }
84
85 int
86 ldap_pvt_thread_pool_init (
87         ldap_pvt_thread_pool_t *tpool,
88         int max_threads,
89         int max_pending )
90 {
91         ldap_pvt_thread_pool_t pool;
92         int rc;
93
94         *tpool = NULL;
95         pool = (ldap_pvt_thread_pool_t) LDAP_CALLOC(1,
96                 sizeof(struct ldap_int_thread_pool_s));
97
98         if (pool == NULL) return(-1);
99
100         rc = ldap_pvt_thread_mutex_init(&pool->ltp_mutex);
101         if (rc != 0)
102                 return(rc);
103         rc = ldap_pvt_thread_cond_init(&pool->ltp_cond);
104         if (rc != 0)
105                 return(rc);
106         pool->ltp_state = LDAP_INT_THREAD_POOL_RUNNING;
107         pool->ltp_max_count = max_threads;
108         pool->ltp_max_pending = max_pending;
109         LDAP_STAILQ_INIT(&pool->ltp_pending_list);
110         LDAP_SLIST_INIT(&pool->ltp_free_list);
111         ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
112         LDAP_STAILQ_INSERT_TAIL(&ldap_int_thread_pool_list, pool, ltp_next);
113         ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
114
115 #if 0
116         /* THIS WILL NOT WORK on some systems.  If the process
117          * forks after starting a thread, there is no guarantee
118          * that the thread will survive the fork.  For example,
119          * slapd forks in order to daemonize, and does so after
120          * calling ldap_pvt_thread_pool_init.  On some systems,
121          * this initial thread does not run in the child process,
122          * but ltp_open_count == 1, so two things happen: 
123          * 1) the first client connection fails, and 2) when
124          * slapd is kill'ed, it never terminates since it waits
125          * for all worker threads to exit. */
126
127         /* start up one thread, just so there is one. no need to
128          * lock the mutex right now, since no threads are running.
129          */
130         pool->ltp_open_count++;
131
132         ldap_pvt_thread_t thr;
133         rc = ldap_pvt_thread_create( &thr, 1,
134                 (void *) ldap_int_thread_pool_wrapper, pool );
135
136         if( rc != 0) {
137                 /* couldn't start one?  then don't start any */
138                 ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
139                 LDAP_STAILQ_REMOVE(ldap_int_thread_pool_list, pool, 
140                         ldap_int_thread_pool_s, ltp_next);
141                 ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
142                 ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
143                 ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
144                 free(pool);
145                 return(-1);
146         }
147 #endif
148
149         *tpool = pool;
150         return(0);
151 }
152
153 int
154 ldap_pvt_thread_pool_submit (
155         ldap_pvt_thread_pool_t *tpool,
156         void *(*start_routine)( void * ), void *arg )
157 {
158         struct ldap_int_thread_pool_s *pool;
159         ldap_int_thread_ctx_t *ctx;
160         int need_thread = 0;
161         ldap_pvt_thread_t thr;
162
163         if (tpool == NULL)
164                 return(-1);
165
166         pool = *tpool;
167
168         if (pool == NULL)
169                 return(-1);
170
171         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
172         if (pool->ltp_state != LDAP_INT_THREAD_POOL_RUNNING
173                 || (pool->ltp_max_pending > 0
174                         && pool->ltp_pending_count >= pool->ltp_max_pending))
175         {
176                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
177                 return(-1);
178         }
179         ctx = LDAP_SLIST_FIRST(&pool->ltp_free_list);
180         if (ctx) {
181                 LDAP_SLIST_REMOVE_HEAD(&pool->ltp_free_list, ltc_next.l);
182         } else {
183                 ctx = (ldap_int_thread_ctx_t *) LDAP_MALLOC(
184                         sizeof(ldap_int_thread_ctx_t));
185                 if (ctx == NULL) {
186                         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
187                         return(-1);
188                 }
189         }
190
191         ctx->ltc_start_routine = start_routine;
192         ctx->ltc_arg = arg;
193
194         pool->ltp_pending_count++;
195         LDAP_STAILQ_INSERT_TAIL(&pool->ltp_pending_list, ctx, ltc_next.q);
196         ldap_pvt_thread_cond_signal(&pool->ltp_cond);
197         if ((pool->ltp_open_count <= 0
198                         || pool->ltp_pending_count > 1
199                         || pool->ltp_open_count == pool->ltp_active_count)
200                 && (pool->ltp_max_count <= 0
201                         || pool->ltp_open_count < pool->ltp_max_count))
202         {
203                 pool->ltp_open_count++;
204                 pool->ltp_starting++;
205                 need_thread = 1;
206         }
207         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
208
209         if (need_thread) {
210                 int rc = ldap_pvt_thread_create( &thr, 1,
211                         (void *)ldap_int_thread_pool_wrapper, pool );
212                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
213                 if (rc == 0) {
214                         pool->ltp_starting--;
215                 } else {
216                         /* couldn't create thread.  back out of
217                          * ltp_open_count and check for even worse things.
218                          */
219                         pool->ltp_open_count--;
220                         pool->ltp_starting--;
221                         if (pool->ltp_open_count == 0) {
222                                 /* no open threads at all?!?
223                                  */
224                                 ldap_int_thread_ctx_t *ptr;
225                                 LDAP_STAILQ_FOREACH(ptr, &pool->ltp_pending_list, ltc_next.q)
226                                         if (ptr == ctx) break;
227                                 if (ptr == ctx) {
228                                         /* no open threads, context not handled, so
229                                          * back out of ltp_pending_count, free the context,
230                                          * report the error.
231                                          */
232                                         LDAP_STAILQ_REMOVE(&pool->ltp_pending_list, ctx, 
233                                                 ldap_int_thread_ctx_s, ltc_next.q);
234                                         pool->ltp_pending_count++;
235                                         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
236                                         free(ctx);
237                                         return(-1);
238                                 }
239                         }
240                         /* there is another open thread, so this
241                          * context will be handled eventually.
242                          * continue on and signal that the context
243                          * is waiting.
244                          */
245                 }
246                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
247         }
248
249         return(0);
250 }
251
252 int
253 ldap_pvt_thread_pool_maxthreads ( ldap_pvt_thread_pool_t *tpool, int max_threads )
254 {
255         struct ldap_int_thread_pool_s *pool;
256
257         if (tpool == NULL)
258                 return(-1);
259
260         pool = *tpool;
261
262         if (pool == NULL)
263                 return(-1);
264
265         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
266         pool->ltp_max_count = max_threads;
267         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
268         return(0);
269 }
270
271 int
272 ldap_pvt_thread_pool_backload ( ldap_pvt_thread_pool_t *tpool )
273 {
274         struct ldap_int_thread_pool_s *pool;
275         int count;
276
277         if (tpool == NULL)
278                 return(-1);
279
280         pool = *tpool;
281
282         if (pool == NULL)
283                 return(0);
284
285         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
286         count = pool->ltp_pending_count + pool->ltp_active_count;
287         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
288         return(count);
289 }
290
291 int
292 ldap_pvt_thread_pool_destroy ( ldap_pvt_thread_pool_t *tpool, int run_pending )
293 {
294         struct ldap_int_thread_pool_s *pool, *pptr;
295         long waiting;
296         ldap_int_thread_ctx_t *ctx;
297
298         if (tpool == NULL)
299                 return(-1);
300
301         pool = *tpool;
302
303         if (pool == NULL) return(-1);
304
305         ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
306         LDAP_STAILQ_FOREACH(pptr, &ldap_int_thread_pool_list, ltp_next)
307                 if (pptr == pool) break;
308         if (pptr == pool)
309                 LDAP_STAILQ_REMOVE(&ldap_int_thread_pool_list, pool,
310                         ldap_int_thread_pool_s, ltp_next);
311         ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
312
313         if (pool != pptr) return(-1);
314
315         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
316         pool->ltp_state = run_pending
317                 ? LDAP_INT_THREAD_POOL_FINISHING
318                 : LDAP_INT_THREAD_POOL_STOPPING;
319         waiting = pool->ltp_open_count;
320
321         /* broadcast could be used here, but only after
322          * it is fixed in the NT thread implementation
323          */
324         while (--waiting >= 0) {
325                 ldap_pvt_thread_cond_signal(&pool->ltp_cond);
326         }
327         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
328
329         do {
330                 ldap_pvt_thread_yield();
331                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
332                 waiting = pool->ltp_open_count;
333                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
334         } while (waiting > 0);
335
336         while ((ctx = LDAP_STAILQ_FIRST(&pool->ltp_pending_list)) != NULL)
337         {
338                 LDAP_STAILQ_REMOVE_HEAD(&pool->ltp_pending_list, ltc_next.q);
339                 free(ctx);
340         }
341
342         while ((ctx = LDAP_SLIST_FIRST(&pool->ltp_free_list)) != NULL)
343         {
344                 LDAP_SLIST_REMOVE_HEAD(&pool->ltp_free_list, ltc_next.l);
345                 free(ctx);
346         }
347
348         ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
349         ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
350         free(pool);
351         return(0);
352 }
353
354 static void *
355 ldap_int_thread_pool_wrapper ( 
356         struct ldap_int_thread_pool_s *pool )
357 {
358         ldap_int_thread_ctx_t *ctx;
359
360         if (pool == NULL)
361                 return NULL;
362
363         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
364
365         while (pool->ltp_state != LDAP_INT_THREAD_POOL_STOPPING) {
366                 ctx = LDAP_STAILQ_FIRST(&pool->ltp_pending_list);
367                 if (ctx) {
368                         LDAP_STAILQ_REMOVE_HEAD(&pool->ltp_pending_list, ltc_next.q);
369                 } else {
370                         if (pool->ltp_state == LDAP_INT_THREAD_POOL_FINISHING)
371                                 break;
372                         if (pool->ltp_max_count > 0
373                                 && pool->ltp_open_count > pool->ltp_max_count)
374                         {
375                                 /* too many threads running (can happen if the
376                                  * maximum threads value is set during ongoing
377                                  * operation using ldap_pvt_thread_pool_maxthreads)
378                                  * so let this thread die.
379                                  */
380                                 break;
381                         }
382
383                         /* we could check an idle timer here, and let the
384                          * thread die if it has been inactive for a while.
385                          * only die if there are other open threads (i.e.,
386                          * always have at least one thread open).  the check
387                          * should be like this:
388                          *   if (pool->ltp_open_count > 1 && pool->ltp_starting == 0)
389                          *       check timer, leave thread (break;)
390                          */
391
392                         if (pool->ltp_state == LDAP_INT_THREAD_POOL_RUNNING)
393                                 ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex);
394
395                         continue;
396                 }
397
398                 pool->ltp_pending_count--;
399                 pool->ltp_active_count++;
400                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
401
402                 (ctx->ltc_start_routine)(ctx->ltc_arg);
403                 LDAP_SLIST_INSERT_HEAD(&pool->ltp_free_list, ctx, ltc_next.l);
404                 ldap_pvt_thread_yield();
405
406                 /* if we use an idle timer, here's
407                  * a good place to update it
408                  */
409
410                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
411                 pool->ltp_active_count--;
412         }
413
414         pool->ltp_open_count--;
415         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
416
417         ldap_pvt_thread_exit(NULL);
418         return(NULL);
419 }
420 #endif /* LDAP_HAVE_THREAD_POOL */