]> git.sur5r.net Git - openldap/blob - libraries/libldap_r/tpool.c
623328026bf9177fe0cd877339bad24deac8a698
[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 "queue-compat.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         SLIST_ENTRY(ldap_int_thread_ctx_s) l;
36         STAILQ_ENTRY(ldap_int_thread_ctx_s) q;
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         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         STAILQ_HEAD(tcq, ldap_int_thread_ctx_s) ltp_pending_list;
47         long ltp_state;
48         long ltp_max_count;
49         long ltp_max_pending;
50         long ltp_pending_count;
51         long ltp_active_count;
52         long ltp_open_count;
53         long ltp_starting;
54 };
55
56 static STAILQ_HEAD(tpq, ldap_int_thread_pool_s)
57         ldap_int_thread_pool_list =
58         STAILQ_HEAD_INITIALIZER(ldap_int_thread_pool_list);
59
60 static SLIST_HEAD(tcl, ldap_int_thread_ctx_s)
61         ldap_int_ctx_free_list = 
62         SLIST_HEAD_INITIALIZER(ldap_int_ctx_free_list);
63
64 static ldap_pvt_thread_mutex_t ldap_pvt_thread_pool_mutex;
65 static ldap_pvt_thread_mutex_t ldap_pvt_ctx_free_mutex;
66
67 static void *ldap_int_thread_pool_wrapper(
68         struct ldap_int_thread_pool_s *pool );
69
70 int
71 ldap_int_thread_pool_startup ( void )
72 {
73         int rc = ldap_pvt_thread_mutex_init(&ldap_pvt_thread_pool_mutex);
74         if (rc == 0)
75                 rc = ldap_pvt_thread_mutex_init(&ldap_pvt_ctx_free_mutex);
76         return rc;
77 }
78
79 int
80 ldap_int_thread_pool_shutdown ( void )
81 {
82         ldap_int_thread_ctx_t *ctx;
83         struct ldap_int_thread_pool_s *pool;
84
85         while ((pool = STAILQ_FIRST(&ldap_int_thread_pool_list)) != NULL) {
86                 STAILQ_REMOVE_HEAD(&ldap_int_thread_pool_list, ltp_next);
87                 ldap_pvt_thread_pool_destroy( &pool, 0);
88         }
89         while ((ctx = SLIST_FIRST(&ldap_int_ctx_free_list))) {
90                 SLIST_REMOVE_HEAD(&ldap_int_ctx_free_list, ltc_next.l);
91                 free(ctx);
92         }
93         ldap_pvt_thread_mutex_destroy(&ldap_pvt_ctx_free_mutex);
94         ldap_pvt_thread_mutex_destroy(&ldap_pvt_thread_pool_mutex);
95         return(0);
96 }
97
98 int
99 ldap_pvt_thread_pool_init (
100         ldap_pvt_thread_pool_t *tpool,
101         int max_threads,
102         int max_pending )
103 {
104         ldap_pvt_thread_pool_t pool;
105         int rc;
106
107         *tpool = NULL;
108         pool = (ldap_pvt_thread_pool_t) LDAP_CALLOC(1,
109                 sizeof(struct ldap_int_thread_pool_s));
110
111         if (pool == NULL) return(-1);
112
113         rc = ldap_pvt_thread_mutex_init(&pool->ltp_mutex);
114         if (rc != 0)
115                 return(rc);
116         rc = ldap_pvt_thread_cond_init(&pool->ltp_cond);
117         if (rc != 0)
118                 return(rc);
119         pool->ltp_state = LDAP_INT_THREAD_POOL_RUNNING;
120         pool->ltp_max_count = max_threads;
121         pool->ltp_max_pending = max_pending;
122         STAILQ_INIT(&pool->ltp_pending_list);
123         ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
124         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,
146                 (void *) ldap_int_thread_pool_wrapper, pool );
147
148         if( rc != 0) {
149                 /* couldn't start one?  then don't start any */
150                 ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
151                 STAILQ_REMOVE(ldap_int_thread_pool_list, pool, 
152                         ldap_int_thread_element_s, ltp_next);
153                 ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
154                 ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
155                 ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
156                 free(pool);
157                 return(-1);
158         }
159 #endif
160
161         *tpool = pool;
162         return(0);
163 }
164
165 int
166 ldap_pvt_thread_pool_submit (
167         ldap_pvt_thread_pool_t *tpool,
168         void *(*start_routine)( void * ), void *arg )
169 {
170         struct ldap_int_thread_pool_s *pool;
171         ldap_int_thread_ctx_t *ctx;
172         int need_thread = 0;
173         ldap_pvt_thread_t thr;
174
175         if (tpool == NULL)
176                 return(-1);
177
178         pool = *tpool;
179
180         if (pool == NULL)
181                 return(-1);
182
183         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
184         if (pool->ltp_state != LDAP_INT_THREAD_POOL_RUNNING
185                 || (pool->ltp_max_pending > 0
186                         && pool->ltp_pending_count >= pool->ltp_max_pending))
187         {
188                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
189                 return(-1);
190         }
191         ldap_pvt_thread_mutex_lock(&ldap_pvt_ctx_free_mutex);
192         ctx = SLIST_FIRST(&ldap_int_ctx_free_list);
193         if (ctx) {
194                 SLIST_REMOVE_HEAD(&ldap_int_ctx_free_list, ltc_next.l);
195                 ldap_pvt_thread_mutex_unlock(&ldap_pvt_ctx_free_mutex);
196         } else {
197                 ldap_pvt_thread_mutex_unlock(&ldap_pvt_ctx_free_mutex);
198                 ctx = (ldap_int_thread_ctx_t *) LDAP_MALLOC(
199                         sizeof(ldap_int_thread_ctx_t));
200                 if (ctx == NULL) {
201                         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
202                         return(-1);
203                 }
204         }
205
206         ctx->ltc_start_routine = start_routine;
207         ctx->ltc_arg = arg;
208
209         pool->ltp_pending_count++;
210         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                         (void *)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                                 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                                         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                                         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 int
307 ldap_pvt_thread_pool_destroy ( ldap_pvt_thread_pool_t *tpool, int run_pending )
308 {
309         struct ldap_int_thread_pool_s *pool, *pptr;
310         long waiting;
311         ldap_int_thread_ctx_t *ctx;
312
313         if (tpool == NULL)
314                 return(-1);
315
316         pool = *tpool;
317
318         if (pool == NULL) return(-1);
319
320         ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
321         STAILQ_FOREACH(pptr, &ldap_int_thread_pool_list, ltp_next)
322                 if (pptr == pool) break;
323         if (pptr == pool)
324                 STAILQ_REMOVE(&ldap_int_thread_pool_list, pool,
325                         ldap_int_thread_pool_s, ltp_next);
326         ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
327
328         if (pool != pptr) return(-1);
329
330         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
331         pool->ltp_state = run_pending
332                 ? LDAP_INT_THREAD_POOL_FINISHING
333                 : LDAP_INT_THREAD_POOL_STOPPING;
334         waiting = pool->ltp_open_count;
335
336         /* broadcast could be used here, but only after
337          * it is fixed in the NT thread implementation
338          */
339         while (--waiting >= 0) {
340                 ldap_pvt_thread_cond_signal(&pool->ltp_cond);
341         }
342         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
343
344         do {
345                 ldap_pvt_thread_yield();
346                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
347                 waiting = pool->ltp_open_count;
348                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
349         } while (waiting > 0);
350
351         while ((ctx = STAILQ_FIRST(&pool->ltp_pending_list)) != NULL)
352         {
353                 STAILQ_REMOVE_HEAD(&pool->ltp_pending_list, ltc_next.q);
354                 free(ctx);
355         }
356
357         ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
358         ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
359         free(pool);
360         return(0);
361 }
362
363 static void *
364 ldap_int_thread_pool_wrapper ( 
365         struct ldap_int_thread_pool_s *pool )
366 {
367         ldap_int_thread_ctx_t *ctx;
368
369         if (pool == NULL)
370                 return NULL;
371
372         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
373
374         while (pool->ltp_state != LDAP_INT_THREAD_POOL_STOPPING) {
375                 ctx = STAILQ_FIRST(&pool->ltp_pending_list);
376                 if (ctx) {
377                         STAILQ_REMOVE_HEAD(&pool->ltp_pending_list, ltc_next.q);
378                 } else {
379                         if (pool->ltp_state == LDAP_INT_THREAD_POOL_FINISHING)
380                                 break;
381                         if (pool->ltp_max_count > 0
382                                 && pool->ltp_open_count > pool->ltp_max_count)
383                         {
384                                 /* too many threads running (can happen if the
385                                  * maximum threads value is set during ongoing
386                                  * operation using ldap_pvt_thread_pool_maxthreads)
387                                  * so let this thread die.
388                                  */
389                                 break;
390                         }
391
392                         /* we could check an idle timer here, and let the
393                          * thread die if it has been inactive for a while.
394                          * only die if there are other open threads (i.e.,
395                          * always have at least one thread open).  the check
396                          * should be like this:
397                          *   if (pool->ltp_open_count > 1 && pool->ltp_starting == 0)
398                          *       check timer, leave thread (break;)
399                          */
400
401                         if (pool->ltp_state == LDAP_INT_THREAD_POOL_RUNNING)
402                                 ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex);
403
404                         continue;
405                 }
406
407                 pool->ltp_pending_count--;
408                 pool->ltp_active_count++;
409                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
410
411                 (ctx->ltc_start_routine)(ctx->ltc_arg);
412                 ldap_pvt_thread_mutex_lock(&ldap_pvt_ctx_free_mutex);
413                 SLIST_INSERT_HEAD(&ldap_int_ctx_free_list, ctx, ltc_next.l);
414                 ldap_pvt_thread_mutex_unlock(&ldap_pvt_ctx_free_mutex);
415                 ldap_pvt_thread_yield();
416
417                 /* if we use an idle timer, here's
418                  * a good place to update it
419                  */
420
421                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
422                 pool->ltp_active_count--;
423         }
424
425         pool->ltp_open_count--;
426         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
427
428         ldap_pvt_thread_exit(NULL);
429         return(NULL);
430 }
431 #endif /* LDAP_HAVE_THREAD_POOL */