]> git.sur5r.net Git - openldap/blob - libldap_r/tpool.c
Do not require ac/string.h for lber_pvt.h
[openldap] / libldap_r / tpool.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2002 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 #include <ac/errno.h>
21
22 #include "ldap-int.h"
23 #include "ldap_pvt_thread.h"
24 #include "ldap_queue.h"
25
26 #ifndef LDAP_THREAD_HAVE_TPOOL
27
28 enum ldap_int_thread_pool_state {
29         LDAP_INT_THREAD_POOL_RUNNING,
30         LDAP_INT_THREAD_POOL_FINISHING,
31         LDAP_INT_THREAD_POOL_STOPPING
32 };
33
34 typedef struct ldap_int_thread_key_s {
35         void *ltk_key;
36         void *ltk_data;
37         ldap_pvt_thread_pool_keyfree_t *ltk_free;
38 } ldap_int_thread_key_t;
39
40 /* Max number of thread-specific keys we store per thread.
41  * We don't expect to use many...
42  */
43 #define MAXKEYS 32
44
45 typedef struct ldap_int_thread_ctx_s {
46         union {
47         LDAP_STAILQ_ENTRY(ldap_int_thread_ctx_s) q;
48         LDAP_SLIST_ENTRY(ldap_int_thread_ctx_s) l;
49         } ltc_next;
50         ldap_pvt_thread_start_t *ltc_start_routine;
51         void *ltc_arg;
52         ldap_int_thread_key_t ltc_key[MAXKEYS];
53 } ldap_int_thread_ctx_t;
54
55 struct ldap_int_thread_pool_s {
56         LDAP_STAILQ_ENTRY(ldap_int_thread_pool_s) ltp_next;
57         ldap_pvt_thread_mutex_t ltp_mutex;
58         ldap_pvt_thread_cond_t ltp_cond;
59         LDAP_STAILQ_HEAD(tcq, ldap_int_thread_ctx_s) ltp_pending_list;
60         LDAP_SLIST_HEAD(tcl, ldap_int_thread_ctx_s) ltp_free_list;
61         long ltp_state;
62         long ltp_max_count;
63         long ltp_max_pending;
64         long ltp_pending_count;
65         long ltp_active_count;
66         long ltp_open_count;
67         long ltp_starting;
68 };
69
70 static LDAP_STAILQ_HEAD(tpq, ldap_int_thread_pool_s)
71         ldap_int_thread_pool_list =
72         LDAP_STAILQ_HEAD_INITIALIZER(ldap_int_thread_pool_list);
73
74 static ldap_pvt_thread_mutex_t ldap_pvt_thread_pool_mutex;
75
76 static void *ldap_int_thread_pool_wrapper( void *pool );
77
78 int
79 ldap_int_thread_pool_startup ( void )
80 {
81         return ldap_pvt_thread_mutex_init(&ldap_pvt_thread_pool_mutex);
82 }
83
84 int
85 ldap_int_thread_pool_shutdown ( void )
86 {
87         struct ldap_int_thread_pool_s *pool;
88
89         while ((pool = LDAP_STAILQ_FIRST(&ldap_int_thread_pool_list)) != NULL) {
90                 LDAP_STAILQ_REMOVE_HEAD(&ldap_int_thread_pool_list, ltp_next);
91                 ldap_pvt_thread_pool_destroy( &pool, 0);
92         }
93         ldap_pvt_thread_mutex_destroy(&ldap_pvt_thread_pool_mutex);
94         return(0);
95 }
96
97 int
98 ldap_pvt_thread_pool_init (
99         ldap_pvt_thread_pool_t *tpool,
100         int max_threads,
101         int max_pending )
102 {
103         ldap_pvt_thread_pool_t pool;
104         int rc;
105
106         *tpool = NULL;
107         pool = (ldap_pvt_thread_pool_t) LDAP_CALLOC(1,
108                 sizeof(struct ldap_int_thread_pool_s));
109
110         if (pool == NULL) return(-1);
111
112         rc = ldap_pvt_thread_mutex_init(&pool->ltp_mutex);
113         if (rc != 0)
114                 return(rc);
115         rc = ldap_pvt_thread_cond_init(&pool->ltp_cond);
116         if (rc != 0)
117                 return(rc);
118         pool->ltp_state = LDAP_INT_THREAD_POOL_RUNNING;
119         pool->ltp_max_count = max_threads;
120         pool->ltp_max_pending = max_pending;
121         LDAP_STAILQ_INIT(&pool->ltp_pending_list);
122         LDAP_SLIST_INIT(&pool->ltp_free_list);
123         ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
124         LDAP_STAILQ_INSERT_TAIL(&ldap_int_thread_pool_list, pool, ltp_next);
125         ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
126
127 #if 0
128         /* THIS WILL NOT WORK on some systems.  If the process
129          * forks after starting a thread, there is no guarantee
130          * that the thread will survive the fork.  For example,
131          * slapd forks in order to daemonize, and does so after
132          * calling ldap_pvt_thread_pool_init.  On some systems,
133          * this initial thread does not run in the child process,
134          * but ltp_open_count == 1, so two things happen: 
135          * 1) the first client connection fails, and 2) when
136          * slapd is kill'ed, it never terminates since it waits
137          * for all worker threads to exit. */
138
139         /* start up one thread, just so there is one. no need to
140          * lock the mutex right now, since no threads are running.
141          */
142         pool->ltp_open_count++;
143
144         ldap_pvt_thread_t thr;
145         rc = ldap_pvt_thread_create( &thr, 1, ldap_int_thread_pool_wrapper, pool );
146
147         if( rc != 0) {
148                 /* couldn't start one?  then don't start any */
149                 ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
150                 LDAP_STAILQ_REMOVE(ldap_int_thread_pool_list, pool, 
151                         ldap_int_thread_pool_s, ltp_next);
152                 ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
153                 ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
154                 ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
155                 LDAP_FREE(pool);
156                 return(-1);
157         }
158 #endif
159
160         *tpool = pool;
161         return(0);
162 }
163
164 int
165 ldap_pvt_thread_pool_submit (
166         ldap_pvt_thread_pool_t *tpool,
167         ldap_pvt_thread_start_t *start_routine, void *arg )
168 {
169         struct ldap_int_thread_pool_s *pool;
170         ldap_int_thread_ctx_t *ctx;
171         int need_thread = 0;
172         ldap_pvt_thread_t thr;
173
174         if (tpool == NULL)
175                 return(-1);
176
177         pool = *tpool;
178
179         if (pool == NULL)
180                 return(-1);
181
182         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
183         if (pool->ltp_state != LDAP_INT_THREAD_POOL_RUNNING
184                 || (pool->ltp_max_pending > 0
185                         && pool->ltp_pending_count >= pool->ltp_max_pending))
186         {
187                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
188                 return(-1);
189         }
190         ctx = LDAP_SLIST_FIRST(&pool->ltp_free_list);
191         if (ctx) {
192                 LDAP_SLIST_REMOVE_HEAD(&pool->ltp_free_list, ltc_next.l);
193         } else {
194                 int i;
195                 ctx = (ldap_int_thread_ctx_t *) LDAP_MALLOC(
196                         sizeof(ldap_int_thread_ctx_t));
197                 if (ctx == NULL) {
198                         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
199                         return(-1);
200                 }
201                 for ( i=0; i<MAXKEYS; i++ ) {
202                         ctx->ltc_key[i].ltk_key = NULL;
203                 }
204         }
205
206         ctx->ltc_start_routine = start_routine;
207         ctx->ltc_arg = arg;
208
209         pool->ltp_pending_count++;
210         LDAP_STAILQ_INSERT_TAIL(&pool->ltp_pending_list, ctx, ltc_next.q);
211         ldap_pvt_thread_cond_signal(&pool->ltp_cond);
212         if ((pool->ltp_open_count <= 0
213                         || pool->ltp_pending_count > 1
214                         || pool->ltp_open_count == pool->ltp_active_count)
215                 && (pool->ltp_max_count <= 0
216                         || pool->ltp_open_count < pool->ltp_max_count))
217         {
218                 pool->ltp_open_count++;
219                 pool->ltp_starting++;
220                 need_thread = 1;
221         }
222         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
223
224         if (need_thread) {
225                 int rc = ldap_pvt_thread_create( &thr, 1,
226                         ldap_int_thread_pool_wrapper, pool );
227                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
228                 if (rc == 0) {
229                         pool->ltp_starting--;
230                 } else {
231                         /* couldn't create thread.  back out of
232                          * ltp_open_count and check for even worse things.
233                          */
234                         pool->ltp_open_count--;
235                         pool->ltp_starting--;
236                         if (pool->ltp_open_count == 0) {
237                                 /* no open threads at all?!?
238                                  */
239                                 ldap_int_thread_ctx_t *ptr;
240                                 LDAP_STAILQ_FOREACH(ptr, &pool->ltp_pending_list, ltc_next.q)
241                                         if (ptr == ctx) break;
242                                 if (ptr == ctx) {
243                                         /* no open threads, context not handled, so
244                                          * back out of ltp_pending_count, free the context,
245                                          * report the error.
246                                          */
247                                         LDAP_STAILQ_REMOVE(&pool->ltp_pending_list, ctx, 
248                                                 ldap_int_thread_ctx_s, ltc_next.q);
249                                         pool->ltp_pending_count++;
250                                         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
251                                         LDAP_FREE(ctx);
252                                         return(-1);
253                                 }
254                         }
255                         /* there is another open thread, so this
256                          * context will be handled eventually.
257                          * continue on and signal that the context
258                          * is waiting.
259                          */
260                 }
261                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
262         }
263
264         return(0);
265 }
266
267 int
268 ldap_pvt_thread_pool_maxthreads ( ldap_pvt_thread_pool_t *tpool, int max_threads )
269 {
270         struct ldap_int_thread_pool_s *pool;
271
272         if (tpool == NULL)
273                 return(-1);
274
275         pool = *tpool;
276
277         if (pool == NULL)
278                 return(-1);
279
280         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
281         pool->ltp_max_count = max_threads;
282         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
283         return(0);
284 }
285
286 int
287 ldap_pvt_thread_pool_backload ( ldap_pvt_thread_pool_t *tpool )
288 {
289         struct ldap_int_thread_pool_s *pool;
290         int count;
291
292         if (tpool == NULL)
293                 return(-1);
294
295         pool = *tpool;
296
297         if (pool == NULL)
298                 return(0);
299
300         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
301         count = pool->ltp_pending_count + pool->ltp_active_count;
302         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
303         return(count);
304 }
305
306 static void ldap_int_thread_pool_keyfree( ldap_int_thread_ctx_t *ctx )
307 {
308         int i;
309         for ( i=0; i<MAXKEYS && ctx->ltc_key[i].ltk_key; i++ ) {
310                 if (ctx->ltc_key[i].ltk_free)
311                         ctx->ltc_key[i].ltk_free(
312                                 ctx->ltc_key[i].ltk_key,
313                                 ctx->ltc_key[i].ltk_data );
314         }
315 }
316
317 int
318 ldap_pvt_thread_pool_destroy ( ldap_pvt_thread_pool_t *tpool, int run_pending )
319 {
320         struct ldap_int_thread_pool_s *pool, *pptr;
321         long waiting;
322         ldap_int_thread_ctx_t *ctx;
323
324         if (tpool == NULL)
325                 return(-1);
326
327         pool = *tpool;
328
329         if (pool == NULL) return(-1);
330
331         ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
332         LDAP_STAILQ_FOREACH(pptr, &ldap_int_thread_pool_list, ltp_next)
333                 if (pptr == pool) break;
334         if (pptr == pool)
335                 LDAP_STAILQ_REMOVE(&ldap_int_thread_pool_list, pool,
336                         ldap_int_thread_pool_s, ltp_next);
337         ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
338
339         if (pool != pptr) return(-1);
340
341         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
342         pool->ltp_state = run_pending
343                 ? LDAP_INT_THREAD_POOL_FINISHING
344                 : LDAP_INT_THREAD_POOL_STOPPING;
345         waiting = pool->ltp_open_count;
346
347         /* broadcast could be used here, but only after
348          * it is fixed in the NT thread implementation
349          */
350         while (--waiting >= 0) {
351                 ldap_pvt_thread_cond_signal(&pool->ltp_cond);
352         }
353         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
354
355         do {
356                 ldap_pvt_thread_yield();
357                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
358                 waiting = pool->ltp_open_count;
359                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
360         } while (waiting > 0);
361
362         while ((ctx = LDAP_STAILQ_FIRST(&pool->ltp_pending_list)) != NULL)
363         {
364                 LDAP_STAILQ_REMOVE_HEAD(&pool->ltp_pending_list, ltc_next.q);
365                 ldap_int_thread_pool_keyfree( ctx );
366                 LDAP_FREE(ctx);
367         }
368
369         while ((ctx = LDAP_SLIST_FIRST(&pool->ltp_free_list)) != NULL)
370         {
371                 LDAP_SLIST_REMOVE_HEAD(&pool->ltp_free_list, ltc_next.l);
372                 ldap_int_thread_pool_keyfree( ctx );
373                 LDAP_FREE(ctx);
374         }
375
376         ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
377         ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
378         LDAP_FREE(pool);
379         return(0);
380 }
381
382 static void *
383 ldap_int_thread_pool_wrapper ( 
384         void *xpool )
385 {
386         struct ldap_int_thread_pool_s *pool = xpool;
387         ldap_int_thread_ctx_t *ctx;
388
389         if (pool == NULL)
390                 return NULL;
391
392         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
393
394         while (pool->ltp_state != LDAP_INT_THREAD_POOL_STOPPING) {
395                 ctx = LDAP_STAILQ_FIRST(&pool->ltp_pending_list);
396                 if (ctx) {
397                         LDAP_STAILQ_REMOVE_HEAD(&pool->ltp_pending_list, ltc_next.q);
398                 } else {
399                         if (pool->ltp_state == LDAP_INT_THREAD_POOL_FINISHING)
400                                 break;
401                         if (pool->ltp_max_count > 0
402                                 && pool->ltp_open_count > pool->ltp_max_count)
403                         {
404                                 /* too many threads running (can happen if the
405                                  * maximum threads value is set during ongoing
406                                  * operation using ldap_pvt_thread_pool_maxthreads)
407                                  * so let this thread die.
408                                  */
409                                 break;
410                         }
411
412                         /* we could check an idle timer here, and let the
413                          * thread die if it has been inactive for a while.
414                          * only die if there are other open threads (i.e.,
415                          * always have at least one thread open).  the check
416                          * should be like this:
417                          *   if (pool->ltp_open_count > 1 && pool->ltp_starting == 0)
418                          *       check timer, leave thread (break;)
419                          */
420
421                         if (pool->ltp_state == LDAP_INT_THREAD_POOL_RUNNING)
422                                 ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex);
423
424                         continue;
425                 }
426
427                 pool->ltp_pending_count--;
428                 pool->ltp_active_count++;
429                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
430
431                 ctx->ltc_start_routine(ctx, ctx->ltc_arg);
432
433                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
434                 LDAP_SLIST_INSERT_HEAD(&pool->ltp_free_list, ctx, ltc_next.l);
435                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
436
437                 ldap_pvt_thread_yield();
438
439                 /* if we use an idle timer, here's
440                  * a good place to update it
441                  */
442
443                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
444                 pool->ltp_active_count--;
445         }
446
447         pool->ltp_open_count--;
448         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
449
450
451         ldap_pvt_thread_exit(NULL);
452         return(NULL);
453 }
454
455 int ldap_pvt_thread_pool_getkey(
456         void *xctx,
457         void *key,
458         void **data,
459         ldap_pvt_thread_pool_keyfree_t **kfree )
460 {
461         ldap_int_thread_ctx_t *ctx = xctx;
462         int i;
463
464         if ( !ctx || !data ) return EINVAL;
465
466         for ( i=0; i<MAXKEYS && ctx->ltc_key[i].ltk_key; i++ ) {
467                 if ( ctx->ltc_key[i].ltk_key == key ) {
468                         *data = ctx->ltc_key[i].ltk_data;
469                         if ( kfree ) *kfree = ctx->ltc_key[i].ltk_free;
470                         return 0;
471                 }
472         }
473         return ENOENT;
474 }
475
476 int ldap_pvt_thread_pool_setkey(
477         void *xctx,
478         void *key,
479         void *data,
480         ldap_pvt_thread_pool_keyfree_t *kfree )
481 {
482         ldap_int_thread_ctx_t *ctx = xctx;
483         int i;
484
485         if ( !ctx || !key ) return EINVAL;
486
487         for ( i=0; i<MAXKEYS; i++ ) {
488                 if ( !ctx->ltc_key[i].ltk_key || ctx->ltc_key[i].ltk_key == key ) {
489                         ctx->ltc_key[i].ltk_key = key;
490                         ctx->ltc_key[i].ltk_data = data;
491                         ctx->ltc_key[i].ltk_free = kfree;
492                         return 0;
493                 }
494         }
495         return ENOMEM;
496 }
497 #endif /* LDAP_HAVE_THREAD_POOL */