]> git.sur5r.net Git - openldap/blob - libraries/libldap_r/tpool.c
Added "threads" config to slapd.conf.
[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 #include <stdarg.h>
16
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
24 #ifndef LDAP_THREAD_HAVE_TPOOL
25
26 enum ldap_int_thread_pool_state {
27         LDAP_INT_THREAD_POOL_RUNNING,
28         LDAP_INT_THREAD_POOL_FINISHING,
29         LDAP_INT_THREAD_POOL_STOPPING
30 };
31
32 typedef struct ldap_int_thread_list_element_s {
33         struct ldap_int_thread_list_element_s *next;
34 } ldap_int_thread_list_element_t, *ldap_int_thread_list_t;
35
36 struct ldap_int_thread_pool_s {
37         struct ldap_int_thread_pool_s *ltp_next;
38         ldap_pvt_thread_mutex_t ltp_mutex;
39         ldap_pvt_thread_cond_t ltp_cond;
40         ldap_int_thread_list_t ltp_pending_list;
41         long ltp_state;
42         long ltp_max_count;
43         long ltp_max_pending;
44         long ltp_pending_count;
45         long ltp_active_count;
46         long ltp_open_count;
47         long ltp_starting;
48 };
49
50 typedef struct ldap_int_thread_ctx_s {
51         struct ldap_int_thread_ctx_s *ltc_next;
52         void *(*ltc_start_routine)( void *);
53         void *ltc_arg;
54 } ldap_int_thread_ctx_t;
55
56 static ldap_int_thread_list_t ldap_int_thread_pool_list = NULL;
57 static ldap_pvt_thread_mutex_t ldap_pvt_thread_pool_mutex;
58
59 static void *ldap_int_thread_pool_wrapper(
60         struct ldap_int_thread_pool_s *pool );
61
62 static void *ldap_int_thread_enlist( ldap_int_thread_list_t *list, void *elem );
63 static void *ldap_int_thread_delist( ldap_int_thread_list_t *list, void *elem );
64 static void *ldap_int_thread_onlist( ldap_int_thread_list_t *list, void *elem );
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         while (ldap_int_thread_pool_list != NULL) {
76                 struct ldap_int_thread_pool_s *pool =
77                         (struct ldap_int_thread_pool_s *) ldap_int_thread_pool_list;
78
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_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
110         ldap_int_thread_enlist(&ldap_int_thread_pool_list, pool);
111         ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
112
113 #if 0
114         /* THIS WILL NOT WORK on some systems.  If the process
115          * forks after starting a thread, there is no guarantee
116          * that the thread will survive the fork.  For example,
117          * slapd forks in order to daemonize, and does so after
118          * calling ldap_pvt_thread_pool_init.  On some systems,
119          * this initial thread does not run in the child process,
120          * but ltp_open_count == 1, so two things happen: 
121          * 1) the first client connection fails, and 2) when
122          * slapd is kill'ed, it never terminates since it waits
123          * for all worker threads to exit.
124
125         /* start up one thread, just so there is one. no need to
126          * lock the mutex right now, since no threads are running.
127          */
128         pool->ltp_open_count++;
129
130         ldap_pvt_thread_t thr;
131         rc = ldap_pvt_thread_create( &thr, 1,
132                 (void *) ldap_int_thread_pool_wrapper, pool );
133
134         if( rc != 0) {
135                 /* couldn't start one?  then don't start any */
136                 ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
137                 ldap_int_thread_delist(&ldap_int_thread_pool_list, pool);
138                 ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
139                 ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
140                 ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
141                 free(pool);
142                 return(-1);
143         }
144 #endif
145
146         *tpool = pool;
147         return(0);
148 }
149
150 int
151 ldap_pvt_thread_pool_submit (
152         ldap_pvt_thread_pool_t *tpool,
153         void *(*start_routine)( void * ), void *arg )
154 {
155         struct ldap_int_thread_pool_s *pool;
156         ldap_int_thread_ctx_t *ctx;
157         int need_thread = 0;
158         ldap_pvt_thread_t thr;
159
160         if (tpool == NULL)
161                 return(-1);
162
163         pool = *tpool;
164
165         if (pool == NULL)
166                 return(-1);
167
168         ctx = (ldap_int_thread_ctx_t *) LDAP_CALLOC(1,
169                 sizeof(ldap_int_thread_ctx_t));
170
171         if (ctx == NULL) return(-1);
172
173         ctx->ltc_start_routine = start_routine;
174         ctx->ltc_arg = arg;
175
176         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
177         if (pool->ltp_state != LDAP_INT_THREAD_POOL_RUNNING
178                 || (pool->ltp_max_pending > 0
179                         && pool->ltp_pending_count >= pool->ltp_max_pending))
180         {
181                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
182                 free(ctx);
183                 return(-1);
184         }
185         pool->ltp_pending_count++;
186         ldap_int_thread_enlist(&pool->ltp_pending_list, ctx);
187         ldap_pvt_thread_cond_signal(&pool->ltp_cond);
188         if ((pool->ltp_open_count <= 0
189                         || pool->ltp_pending_count > 1
190                         || pool->ltp_open_count == pool->ltp_active_count)
191                 && (pool->ltp_max_count <= 0
192                         || pool->ltp_open_count < pool->ltp_max_count))
193         {
194                 pool->ltp_open_count++;
195                 pool->ltp_starting++;
196                 need_thread = 1;
197         }
198         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
199
200         if (need_thread) {
201                 int rc = ldap_pvt_thread_create( &thr, 1,
202                         (void *)ldap_int_thread_pool_wrapper, pool );
203                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
204                 if (rc == 0) {
205                         pool->ltp_starting--;
206                 } else {
207                         /* couldn't create thread.  back out of
208                          * ltp_open_count and check for even worse things.
209                          */
210                         pool->ltp_open_count--;
211                         pool->ltp_starting--;
212                         if (pool->ltp_open_count == 0) {
213                                 /* no open threads at all?!?
214                                  */
215                                 if (ldap_int_thread_delist(&pool->ltp_pending_list, ctx)) {
216                                         /* no open threads, context not handled, so
217                                          * back out of ltp_pending_count, free the context,
218                                          * report the error.
219                                          */
220                                         pool->ltp_pending_count++;
221                                         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
222                                         free(ctx);
223                                         return(-1);
224                                 }
225                         }
226                         /* there is another open thread, so this
227                          * context will be handled eventually.
228                          * continue on and signal that the context
229                          * is waiting.
230                          */
231                 }
232                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
233         }
234
235         return(0);
236 }
237
238 int
239 ldap_pvt_thread_pool_maxthreads ( ldap_pvt_thread_pool_t *tpool, int max_threads )
240 {
241         struct ldap_int_thread_pool_s *pool;
242
243         if (tpool == NULL)
244                 return(-1);
245
246         pool = *tpool;
247
248         if (pool == NULL)
249                 return(-1);
250
251         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
252         pool->ltp_max_count = max_threads;
253         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
254         return(0);
255 }
256
257 int
258 ldap_pvt_thread_pool_backload ( ldap_pvt_thread_pool_t *tpool )
259 {
260         struct ldap_int_thread_pool_s *pool;
261         int count;
262
263         if (tpool == NULL)
264                 return(-1);
265
266         pool = *tpool;
267
268         if (pool == NULL)
269                 return(0);
270
271         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
272         count = pool->ltp_pending_count + pool->ltp_active_count;
273         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
274         return(count);
275 }
276
277 int
278 ldap_pvt_thread_pool_destroy ( ldap_pvt_thread_pool_t *tpool, int run_pending )
279 {
280         struct ldap_int_thread_pool_s *pool;
281         long waiting;
282         ldap_int_thread_ctx_t *ctx;
283
284         if (tpool == NULL)
285                 return(-1);
286
287         pool = *tpool;
288
289         if (pool == NULL) return(-1);
290
291         ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
292         pool = ldap_int_thread_delist(&ldap_int_thread_pool_list, pool);
293         ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
294
295         if (pool == NULL) return(-1);
296
297         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
298         pool->ltp_state = run_pending
299                 ? LDAP_INT_THREAD_POOL_FINISHING
300                 : LDAP_INT_THREAD_POOL_STOPPING;
301         waiting = pool->ltp_open_count;
302
303         /* broadcast could be used here, but only after
304          * it is fixed in the NT thread implementation
305          */
306         while (--waiting >= 0) {
307                 ldap_pvt_thread_cond_signal(&pool->ltp_cond);
308         }
309         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
310
311         do {
312                 ldap_pvt_thread_yield();
313                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
314                 waiting = pool->ltp_open_count;
315                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
316         } while (waiting > 0);
317
318         while (ctx = (ldap_int_thread_ctx_t *)ldap_int_thread_delist(
319                 &pool->ltp_pending_list, NULL))
320         {
321                 free(ctx);
322         }
323
324         ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
325         ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
326         free(pool);
327         return(0);
328 }
329
330 static void *
331 ldap_int_thread_pool_wrapper ( 
332         struct ldap_int_thread_pool_s *pool )
333 {
334         ldap_int_thread_ctx_t *ctx;
335
336         if (pool == NULL)
337                 return NULL;
338
339         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
340
341         while (pool->ltp_state != LDAP_INT_THREAD_POOL_STOPPING) {
342
343                 ctx = ldap_int_thread_delist(&pool->ltp_pending_list, NULL);
344                 if (ctx == NULL) {
345                         if (pool->ltp_state == LDAP_INT_THREAD_POOL_FINISHING)
346                                 break;
347                         if (pool->ltp_max_count > 0
348                                 && pool->ltp_open_count > pool->ltp_max_count)
349                         {
350                                 /* too many threads running (can happen if the
351                                  * maximum threads value is set during ongoing
352                                  * operation using ldap_pvt_thread_pool_maxthreads)
353                                  * so let this thread die.
354                                  */
355                                 break;
356                         }
357
358                         /* we could check an idle timer here, and let the
359                          * thread die if it has been inactive for a while.
360                          * only die if there are other open threads (i.e.,
361                          * always have at least one thread open).  the check
362                          * should be like this:
363                          *   if (pool->ltp_open_count > 1 && pool->ltp_starting == 0)
364                          *       check timer, leave thread (break;)
365                          */
366
367                         if (pool->ltp_state == LDAP_INT_THREAD_POOL_RUNNING)
368                                 ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex);
369
370                         continue;
371                 }
372
373                 pool->ltp_pending_count--;
374                 pool->ltp_active_count++;
375                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
376
377                 (ctx->ltc_start_routine)(ctx->ltc_arg);
378                 free(ctx);
379                 ldap_pvt_thread_yield();
380
381                 /* if we use an idle timer, here's
382                  * a good place to update it
383                  */
384
385                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
386                 pool->ltp_active_count--;
387         }
388
389         pool->ltp_open_count--;
390         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
391
392         ldap_pvt_thread_exit(NULL);
393         return(NULL);
394 }
395
396 static void *
397 ldap_int_thread_enlist( ldap_int_thread_list_t *list, void *elem )
398 {
399         ldap_int_thread_list_element_t *prev;
400
401         if (elem == NULL) return(NULL);
402
403         ((ldap_int_thread_list_element_t *)elem)->next = NULL;
404         if (*list == NULL) {
405                 *list = elem;
406                 return(elem);
407         }
408
409         for (prev = *list ; prev->next != NULL; prev = prev->next) ;
410         prev->next = elem;
411         return(elem);
412 }
413
414 static void *
415 ldap_int_thread_delist( ldap_int_thread_list_t *list, void *elem )
416 {
417         ldap_int_thread_list_element_t *prev;
418
419         if (*list == NULL) return(NULL);
420
421         if (elem == NULL) elem = *list;
422
423         if (*list == elem) {
424                 *list = ((ldap_int_thread_list_element_t *)elem)->next;
425                 return(elem);
426         }
427
428         for (prev = *list ; prev->next != NULL; prev = prev->next) {
429                 if (prev->next == elem) {
430                         prev->next = ((ldap_int_thread_list_element_t *)elem)->next;
431                         return(elem);
432                 }
433         }
434         return(NULL);
435 }
436
437 static void *
438 ldap_int_thread_onlist( ldap_int_thread_list_t *list, void *elem )
439 {
440         ldap_int_thread_list_element_t *prev;
441
442         if (elem == NULL || *list == NULL) return(NULL);
443
444         for (prev = *list ; prev != NULL; prev = prev->next) {
445                 if (prev == elem)
446                         return(elem);
447         }
448
449         return(NULL);
450 }
451
452 #endif /* LDAP_HAVE_THREAD_POOL */