]> git.sur5r.net Git - openldap/blob - libraries/libldap_r/tpool.c
Avoid premature free
[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 #if 0
65 static void *ldap_int_thread_onlist( ldap_int_thread_list_t *list, void *elem );
66 #endif
67
68 int
69 ldap_int_thread_pool_startup ( void )
70 {
71         return ldap_pvt_thread_mutex_init(&ldap_pvt_thread_pool_mutex);
72 }
73
74 int
75 ldap_int_thread_pool_shutdown ( void )
76 {
77         while (ldap_int_thread_pool_list != NULL) {
78                 struct ldap_int_thread_pool_s *pool =
79                         (struct ldap_int_thread_pool_s *) ldap_int_thread_pool_list;
80
81                 ldap_pvt_thread_pool_destroy( &pool, 0);
82         }
83         ldap_pvt_thread_mutex_destroy(&ldap_pvt_thread_pool_mutex);
84         return(0);
85 }
86
87 int
88 ldap_pvt_thread_pool_init (
89         ldap_pvt_thread_pool_t *tpool,
90         int max_threads,
91         int max_pending )
92 {
93         ldap_pvt_thread_pool_t pool;
94         int rc;
95
96         *tpool = NULL;
97         pool = (ldap_pvt_thread_pool_t) LDAP_CALLOC(1,
98                 sizeof(struct ldap_int_thread_pool_s));
99
100         if (pool == NULL) return(-1);
101
102         rc = ldap_pvt_thread_mutex_init(&pool->ltp_mutex);
103         if (rc != 0)
104                 return(rc);
105         rc = ldap_pvt_thread_cond_init(&pool->ltp_cond);
106         if (rc != 0)
107                 return(rc);
108         pool->ltp_state = LDAP_INT_THREAD_POOL_RUNNING;
109         pool->ltp_max_count = max_threads;
110         pool->ltp_max_pending = max_pending;
111         ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
112         ldap_int_thread_enlist(&ldap_int_thread_pool_list, pool);
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_int_thread_delist(&ldap_int_thread_pool_list, pool);
140                 ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
141                 ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
142                 ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
143                 free(pool);
144                 return(-1);
145         }
146 #endif
147
148         *tpool = pool;
149         return(0);
150 }
151
152 int
153 ldap_pvt_thread_pool_submit (
154         ldap_pvt_thread_pool_t *tpool,
155         void *(*start_routine)( void * ), void *arg )
156 {
157         struct ldap_int_thread_pool_s *pool;
158         ldap_int_thread_ctx_t *ctx;
159         int need_thread = 0;
160         ldap_pvt_thread_t thr;
161
162         if (tpool == NULL)
163                 return(-1);
164
165         pool = *tpool;
166
167         if (pool == NULL)
168                 return(-1);
169
170         ctx = (ldap_int_thread_ctx_t *) LDAP_CALLOC(1,
171                 sizeof(ldap_int_thread_ctx_t));
172
173         if (ctx == NULL) return(-1);
174
175         ctx->ltc_start_routine = start_routine;
176         ctx->ltc_arg = arg;
177
178         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
179         if (pool->ltp_state != LDAP_INT_THREAD_POOL_RUNNING
180                 || (pool->ltp_max_pending > 0
181                         && pool->ltp_pending_count >= pool->ltp_max_pending))
182         {
183                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
184                 free(ctx);
185                 return(-1);
186         }
187         pool->ltp_pending_count++;
188         ldap_int_thread_enlist(&pool->ltp_pending_list, ctx);
189         ldap_pvt_thread_cond_signal(&pool->ltp_cond);
190         if ((pool->ltp_open_count <= 0
191                         || pool->ltp_pending_count > 1
192                         || pool->ltp_open_count == pool->ltp_active_count)
193                 && (pool->ltp_max_count <= 0
194                         || pool->ltp_open_count < pool->ltp_max_count))
195         {
196                 pool->ltp_open_count++;
197                 pool->ltp_starting++;
198                 need_thread = 1;
199         }
200         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
201
202         if (need_thread) {
203                 int rc = ldap_pvt_thread_create( &thr, 1,
204                         (void *)ldap_int_thread_pool_wrapper, pool );
205                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
206                 if (rc == 0) {
207                         pool->ltp_starting--;
208                 } else {
209                         /* couldn't create thread.  back out of
210                          * ltp_open_count and check for even worse things.
211                          */
212                         pool->ltp_open_count--;
213                         pool->ltp_starting--;
214                         if (pool->ltp_open_count == 0) {
215                                 /* no open threads at all?!?
216                                  */
217                                 if (ldap_int_thread_delist(&pool->ltp_pending_list, ctx)) {
218                                         /* no open threads, context not handled, so
219                                          * back out of ltp_pending_count, free the context,
220                                          * report the error.
221                                          */
222                                         pool->ltp_pending_count++;
223                                         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
224                                         free(ctx);
225                                         return(-1);
226                                 }
227                         }
228                         /* there is another open thread, so this
229                          * context will be handled eventually.
230                          * continue on and signal that the context
231                          * is waiting.
232                          */
233                 }
234                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
235         }
236
237         return(0);
238 }
239
240 int
241 ldap_pvt_thread_pool_maxthreads ( ldap_pvt_thread_pool_t *tpool, int max_threads )
242 {
243         struct ldap_int_thread_pool_s *pool;
244
245         if (tpool == NULL)
246                 return(-1);
247
248         pool = *tpool;
249
250         if (pool == NULL)
251                 return(-1);
252
253         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
254         pool->ltp_max_count = max_threads;
255         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
256         return(0);
257 }
258
259 int
260 ldap_pvt_thread_pool_backload ( ldap_pvt_thread_pool_t *tpool )
261 {
262         struct ldap_int_thread_pool_s *pool;
263         int count;
264
265         if (tpool == NULL)
266                 return(-1);
267
268         pool = *tpool;
269
270         if (pool == NULL)
271                 return(0);
272
273         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
274         count = pool->ltp_pending_count + pool->ltp_active_count;
275         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
276         return(count);
277 }
278
279 int
280 ldap_pvt_thread_pool_destroy ( ldap_pvt_thread_pool_t *tpool, int run_pending )
281 {
282         struct ldap_int_thread_pool_s *pool;
283         long waiting;
284         ldap_int_thread_ctx_t *ctx;
285
286         if (tpool == NULL)
287                 return(-1);
288
289         pool = *tpool;
290
291         if (pool == NULL) return(-1);
292
293         ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
294         pool = ldap_int_thread_delist(&ldap_int_thread_pool_list, pool);
295         ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
296
297         if (pool == NULL) return(-1);
298
299         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
300         pool->ltp_state = run_pending
301                 ? LDAP_INT_THREAD_POOL_FINISHING
302                 : LDAP_INT_THREAD_POOL_STOPPING;
303         waiting = pool->ltp_open_count;
304
305         /* broadcast could be used here, but only after
306          * it is fixed in the NT thread implementation
307          */
308         while (--waiting >= 0) {
309                 ldap_pvt_thread_cond_signal(&pool->ltp_cond);
310         }
311         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
312
313         do {
314                 ldap_pvt_thread_yield();
315                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
316                 waiting = pool->ltp_open_count;
317                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
318         } while (waiting > 0);
319
320         while ((ctx = (ldap_int_thread_ctx_t *)ldap_int_thread_delist(
321                 &pool->ltp_pending_list, NULL)) != NULL)
322         {
323                 free(ctx);
324         }
325
326         ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
327         ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
328         free(pool);
329         return(0);
330 }
331
332 static void *
333 ldap_int_thread_pool_wrapper ( 
334         struct ldap_int_thread_pool_s *pool )
335 {
336         ldap_int_thread_ctx_t *ctx;
337
338         if (pool == NULL)
339                 return NULL;
340
341         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
342
343         while (pool->ltp_state != LDAP_INT_THREAD_POOL_STOPPING) {
344
345                 ctx = ldap_int_thread_delist(&pool->ltp_pending_list, NULL);
346                 if (ctx == NULL) {
347                         if (pool->ltp_state == LDAP_INT_THREAD_POOL_FINISHING)
348                                 break;
349                         if (pool->ltp_max_count > 0
350                                 && pool->ltp_open_count > pool->ltp_max_count)
351                         {
352                                 /* too many threads running (can happen if the
353                                  * maximum threads value is set during ongoing
354                                  * operation using ldap_pvt_thread_pool_maxthreads)
355                                  * so let this thread die.
356                                  */
357                                 break;
358                         }
359
360                         /* we could check an idle timer here, and let the
361                          * thread die if it has been inactive for a while.
362                          * only die if there are other open threads (i.e.,
363                          * always have at least one thread open).  the check
364                          * should be like this:
365                          *   if (pool->ltp_open_count > 1 && pool->ltp_starting == 0)
366                          *       check timer, leave thread (break;)
367                          */
368
369                         if (pool->ltp_state == LDAP_INT_THREAD_POOL_RUNNING)
370                                 ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex);
371
372                         continue;
373                 }
374
375                 pool->ltp_pending_count--;
376                 pool->ltp_active_count++;
377                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
378
379                 (ctx->ltc_start_routine)(ctx->ltc_arg);
380                 free(ctx);
381                 ldap_pvt_thread_yield();
382
383                 /* if we use an idle timer, here's
384                  * a good place to update it
385                  */
386
387                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
388                 pool->ltp_active_count--;
389         }
390
391         pool->ltp_open_count--;
392         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
393
394         ldap_pvt_thread_exit(NULL);
395         return(NULL);
396 }
397
398 static void *
399 ldap_int_thread_enlist( ldap_int_thread_list_t *list, void *elem )
400 {
401         ldap_int_thread_list_element_t *prev;
402
403         if (elem == NULL) return(NULL);
404
405         ((ldap_int_thread_list_element_t *)elem)->next = NULL;
406         if (*list == NULL) {
407                 *list = elem;
408                 return(elem);
409         }
410
411         for (prev = *list ; prev->next != NULL; prev = prev->next) ;
412         prev->next = elem;
413         return(elem);
414 }
415
416 static void *
417 ldap_int_thread_delist( ldap_int_thread_list_t *list, void *elem )
418 {
419         ldap_int_thread_list_element_t *prev;
420
421         if (*list == NULL) return(NULL);
422
423         if (elem == NULL) elem = *list;
424
425         if (*list == elem) {
426                 *list = ((ldap_int_thread_list_element_t *)elem)->next;
427                 return(elem);
428         }
429
430         for (prev = *list ; prev->next != NULL; prev = prev->next) {
431                 if (prev->next == elem) {
432                         prev->next = ((ldap_int_thread_list_element_t *)elem)->next;
433                         return(elem);
434                 }
435         }
436         return(NULL);
437 }
438 #if 0
439 static void *
440 ldap_int_thread_onlist( ldap_int_thread_list_t *list, void *elem )
441 {
442         ldap_int_thread_list_element_t *prev;
443
444         if (elem == NULL || *list == NULL) return(NULL);
445
446         for (prev = *list ; prev != NULL; prev = prev->next) {
447                 if (prev == elem)
448                         return(elem);
449         }
450
451         return(NULL);
452 }
453 #endif
454 #endif /* LDAP_HAVE_THREAD_POOL */