]> git.sur5r.net Git - openldap/blob - libraries/libldap_r/tpool.c
ba44fb22cc7284241844ebaae83eaf6ce8af6f51
[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_concurrency,
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_concurrency;
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_backload ( ldap_pvt_thread_pool_t *tpool )
240 {
241         struct ldap_int_thread_pool_s *pool;
242         int count;
243
244         if (tpool == NULL)
245                 return(-1);
246
247         pool = *tpool;
248
249         if (pool == NULL)
250                 return(0);
251
252         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
253         count = pool->ltp_pending_count + pool->ltp_active_count;
254         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
255         return(count);
256 }
257
258 int
259 ldap_pvt_thread_pool_destroy ( ldap_pvt_thread_pool_t *tpool, int run_pending )
260 {
261         struct ldap_int_thread_pool_s *pool;
262         long waiting;
263         ldap_int_thread_ctx_t *ctx;
264
265         if (tpool == NULL)
266                 return(-1);
267
268         pool = *tpool;
269
270         if (pool == NULL) return(-1);
271
272         ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
273         pool = ldap_int_thread_delist(&ldap_int_thread_pool_list, pool);
274         ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
275
276         if (pool == NULL) return(-1);
277
278         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
279         pool->ltp_state = run_pending
280                 ? LDAP_INT_THREAD_POOL_FINISHING
281                 : LDAP_INT_THREAD_POOL_STOPPING;
282         waiting = pool->ltp_open_count;
283
284         /* broadcast could be used here, but only after
285          * it is fixed in the NT thread implementation
286          */
287         while (--waiting >= 0) {
288                 ldap_pvt_thread_cond_signal(&pool->ltp_cond);
289         }
290         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
291
292         do {
293                 ldap_pvt_thread_yield();
294                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
295                 waiting = pool->ltp_open_count;
296                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
297         } while (waiting > 0);
298
299         while (ctx = (ldap_int_thread_ctx_t *)ldap_int_thread_delist(
300                 &pool->ltp_pending_list, NULL))
301         {
302                 free(ctx);
303         }
304
305         ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
306         ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
307         free(pool);
308         return(0);
309 }
310
311 static void *
312 ldap_int_thread_pool_wrapper ( 
313         struct ldap_int_thread_pool_s *pool )
314 {
315         ldap_int_thread_ctx_t *ctx;
316
317         if (pool == NULL)
318                 return NULL;
319
320         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
321
322         while (pool->ltp_state != LDAP_INT_THREAD_POOL_STOPPING) {
323
324                 ctx = ldap_int_thread_delist(&pool->ltp_pending_list, NULL);
325                 if (ctx == NULL) {
326                         if (pool->ltp_state == LDAP_INT_THREAD_POOL_FINISHING)
327                                 break;
328                         /* we could check an idle timer here, and let the
329                          * thread die if it has been inactive for a while.
330                          * only die if there are other open threads (i.e.,
331                          * always have at least one thread open).  the check
332                          * should be like this:
333                          *   if (pool->ltp_open_count > 1 && pool->ltp_starting == 0)
334                          *       check timer, leave thread (break;)
335                          */
336
337                         if (pool->ltp_state == LDAP_INT_THREAD_POOL_RUNNING)
338                                 ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex);
339
340                         continue;
341                 }
342
343                 pool->ltp_pending_count--;
344                 pool->ltp_active_count++;
345                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
346
347                 (ctx->ltc_start_routine)(ctx->ltc_arg);
348                 free(ctx);
349                 ldap_pvt_thread_yield();
350
351                 /* if we use an idle timer, here's
352                  * a good place to update it
353                  */
354
355                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
356                 pool->ltp_active_count--;
357         }
358
359         pool->ltp_open_count--;
360         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
361
362         ldap_pvt_thread_exit(NULL);
363         return(NULL);
364 }
365
366 static void *
367 ldap_int_thread_enlist( ldap_int_thread_list_t *list, void *elem )
368 {
369         ldap_int_thread_list_element_t *prev;
370
371         if (elem == NULL) return(NULL);
372
373         ((ldap_int_thread_list_element_t *)elem)->next = NULL;
374         if (*list == NULL) {
375                 *list = elem;
376                 return(elem);
377         }
378
379         for (prev = *list ; prev->next != NULL; prev = prev->next) ;
380         prev->next = elem;
381         return(elem);
382 }
383
384 static void *
385 ldap_int_thread_delist( ldap_int_thread_list_t *list, void *elem )
386 {
387         ldap_int_thread_list_element_t *prev;
388
389         if (*list == NULL) return(NULL);
390
391         if (elem == NULL) elem = *list;
392
393         if (*list == elem) {
394                 *list = ((ldap_int_thread_list_element_t *)elem)->next;
395                 return(elem);
396         }
397
398         for (prev = *list ; prev->next != NULL; prev = prev->next) {
399                 if (prev->next == elem) {
400                         prev->next = ((ldap_int_thread_list_element_t *)elem)->next;
401                         return(elem);
402                 }
403         }
404         return(NULL);
405 }
406
407 static void *
408 ldap_int_thread_onlist( ldap_int_thread_list_t *list, void *elem )
409 {
410         ldap_int_thread_list_element_t *prev;
411
412         if (elem == NULL || *list == NULL) return(NULL);
413
414         for (prev = *list ; prev != NULL; prev = prev->next) {
415                 if (prev == elem)
416                         return(elem);
417         }
418
419         return(NULL);
420 }
421
422 #endif /* LDAP_HAVE_THREAD_POOL */