]> git.sur5r.net Git - openldap/blob - libraries/libldap_r/tpool.c
ldap_int_thread_pool_wrapper was called with wrong prototype (ITS#1673)
[openldap] / libraries / 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
21 #include "ldap-int.h"
22 #include "ldap_pvt_thread.h"
23 #include "ldap_queue.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         LDAP_STAILQ_ENTRY(ldap_int_thread_ctx_s) q;
36         LDAP_SLIST_ENTRY(ldap_int_thread_ctx_s) l;
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         LDAP_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         LDAP_STAILQ_HEAD(tcq, ldap_int_thread_ctx_s) ltp_pending_list;
47         LDAP_SLIST_HEAD(tcl, ldap_int_thread_ctx_s) ltp_free_list;
48         long ltp_state;
49         long ltp_max_count;
50         long ltp_max_pending;
51         long ltp_pending_count;
52         long ltp_active_count;
53         long ltp_open_count;
54         long ltp_starting;
55 };
56
57 static LDAP_STAILQ_HEAD(tpq, ldap_int_thread_pool_s)
58         ldap_int_thread_pool_list =
59         LDAP_STAILQ_HEAD_INITIALIZER(ldap_int_thread_pool_list);
60
61 static ldap_pvt_thread_mutex_t ldap_pvt_thread_pool_mutex;
62
63 static void *ldap_int_thread_pool_wrapper( void *pool );
64
65 int
66 ldap_int_thread_pool_startup ( void )
67 {
68         return ldap_pvt_thread_mutex_init(&ldap_pvt_thread_pool_mutex);
69 }
70
71 int
72 ldap_int_thread_pool_shutdown ( void )
73 {
74         struct ldap_int_thread_pool_s *pool;
75
76         while ((pool = LDAP_STAILQ_FIRST(&ldap_int_thread_pool_list)) != NULL) {
77                 LDAP_STAILQ_REMOVE_HEAD(&ldap_int_thread_pool_list, ltp_next);
78                 ldap_pvt_thread_pool_destroy( &pool, 0);
79         }
80         ldap_pvt_thread_mutex_destroy(&ldap_pvt_thread_pool_mutex);
81         return(0);
82 }
83
84 int
85 ldap_pvt_thread_pool_init (
86         ldap_pvt_thread_pool_t *tpool,
87         int max_threads,
88         int max_pending )
89 {
90         ldap_pvt_thread_pool_t pool;
91         int rc;
92
93         *tpool = NULL;
94         pool = (ldap_pvt_thread_pool_t) LDAP_CALLOC(1,
95                 sizeof(struct ldap_int_thread_pool_s));
96
97         if (pool == NULL) return(-1);
98
99         rc = ldap_pvt_thread_mutex_init(&pool->ltp_mutex);
100         if (rc != 0)
101                 return(rc);
102         rc = ldap_pvt_thread_cond_init(&pool->ltp_cond);
103         if (rc != 0)
104                 return(rc);
105         pool->ltp_state = LDAP_INT_THREAD_POOL_RUNNING;
106         pool->ltp_max_count = max_threads;
107         pool->ltp_max_pending = max_pending;
108         LDAP_STAILQ_INIT(&pool->ltp_pending_list);
109         LDAP_SLIST_INIT(&pool->ltp_free_list);
110         ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
111         LDAP_STAILQ_INSERT_TAIL(&ldap_int_thread_pool_list, pool, ltp_next);
112         ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
113
114 #if 0
115         /* THIS WILL NOT WORK on some systems.  If the process
116          * forks after starting a thread, there is no guarantee
117          * that the thread will survive the fork.  For example,
118          * slapd forks in order to daemonize, and does so after
119          * calling ldap_pvt_thread_pool_init.  On some systems,
120          * this initial thread does not run in the child process,
121          * but ltp_open_count == 1, so two things happen: 
122          * 1) the first client connection fails, and 2) when
123          * slapd is kill'ed, it never terminates since it waits
124          * for all worker threads to exit. */
125
126         /* start up one thread, just so there is one. no need to
127          * lock the mutex right now, since no threads are running.
128          */
129         pool->ltp_open_count++;
130
131         ldap_pvt_thread_t thr;
132         rc = ldap_pvt_thread_create( &thr, 1, 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_STAILQ_REMOVE(ldap_int_thread_pool_list, pool, 
138                         ldap_int_thread_pool_s, ltp_next);
139                 ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
140                 ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
141                 ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
142                 LDAP_FREE(pool);
143                 return(-1);
144         }
145 #endif
146
147         *tpool = pool;
148         return(0);
149 }
150
151 int
152 ldap_pvt_thread_pool_submit (
153         ldap_pvt_thread_pool_t *tpool,
154         void *(*start_routine)( void * ), void *arg )
155 {
156         struct ldap_int_thread_pool_s *pool;
157         ldap_int_thread_ctx_t *ctx;
158         int need_thread = 0;
159         ldap_pvt_thread_t thr;
160
161         if (tpool == NULL)
162                 return(-1);
163
164         pool = *tpool;
165
166         if (pool == NULL)
167                 return(-1);
168
169         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
170         if (pool->ltp_state != LDAP_INT_THREAD_POOL_RUNNING
171                 || (pool->ltp_max_pending > 0
172                         && pool->ltp_pending_count >= pool->ltp_max_pending))
173         {
174                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
175                 return(-1);
176         }
177         ctx = LDAP_SLIST_FIRST(&pool->ltp_free_list);
178         if (ctx) {
179                 LDAP_SLIST_REMOVE_HEAD(&pool->ltp_free_list, ltc_next.l);
180         } else {
181                 ctx = (ldap_int_thread_ctx_t *) LDAP_MALLOC(
182                         sizeof(ldap_int_thread_ctx_t));
183                 if (ctx == NULL) {
184                         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
185                         return(-1);
186                 }
187         }
188
189         ctx->ltc_start_routine = start_routine;
190         ctx->ltc_arg = arg;
191
192         pool->ltp_pending_count++;
193         LDAP_STAILQ_INSERT_TAIL(&pool->ltp_pending_list, ctx, ltc_next.q);
194         ldap_pvt_thread_cond_signal(&pool->ltp_cond);
195         if ((pool->ltp_open_count <= 0
196                         || pool->ltp_pending_count > 1
197                         || pool->ltp_open_count == pool->ltp_active_count)
198                 && (pool->ltp_max_count <= 0
199                         || pool->ltp_open_count < pool->ltp_max_count))
200         {
201                 pool->ltp_open_count++;
202                 pool->ltp_starting++;
203                 need_thread = 1;
204         }
205         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
206
207         if (need_thread) {
208                 int rc = ldap_pvt_thread_create( &thr, 1,
209                         ldap_int_thread_pool_wrapper, pool );
210                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
211                 if (rc == 0) {
212                         pool->ltp_starting--;
213                 } else {
214                         /* couldn't create thread.  back out of
215                          * ltp_open_count and check for even worse things.
216                          */
217                         pool->ltp_open_count--;
218                         pool->ltp_starting--;
219                         if (pool->ltp_open_count == 0) {
220                                 /* no open threads at all?!?
221                                  */
222                                 ldap_int_thread_ctx_t *ptr;
223                                 LDAP_STAILQ_FOREACH(ptr, &pool->ltp_pending_list, ltc_next.q)
224                                         if (ptr == ctx) break;
225                                 if (ptr == ctx) {
226                                         /* no open threads, context not handled, so
227                                          * back out of ltp_pending_count, free the context,
228                                          * report the error.
229                                          */
230                                         LDAP_STAILQ_REMOVE(&pool->ltp_pending_list, ctx, 
231                                                 ldap_int_thread_ctx_s, ltc_next.q);
232                                         pool->ltp_pending_count++;
233                                         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
234                                         LDAP_FREE(ctx);
235                                         return(-1);
236                                 }
237                         }
238                         /* there is another open thread, so this
239                          * context will be handled eventually.
240                          * continue on and signal that the context
241                          * is waiting.
242                          */
243                 }
244                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
245         }
246
247         return(0);
248 }
249
250 int
251 ldap_pvt_thread_pool_maxthreads ( ldap_pvt_thread_pool_t *tpool, int max_threads )
252 {
253         struct ldap_int_thread_pool_s *pool;
254
255         if (tpool == NULL)
256                 return(-1);
257
258         pool = *tpool;
259
260         if (pool == NULL)
261                 return(-1);
262
263         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
264         pool->ltp_max_count = max_threads;
265         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
266         return(0);
267 }
268
269 int
270 ldap_pvt_thread_pool_backload ( ldap_pvt_thread_pool_t *tpool )
271 {
272         struct ldap_int_thread_pool_s *pool;
273         int count;
274
275         if (tpool == NULL)
276                 return(-1);
277
278         pool = *tpool;
279
280         if (pool == NULL)
281                 return(0);
282
283         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
284         count = pool->ltp_pending_count + pool->ltp_active_count;
285         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
286         return(count);
287 }
288
289 int
290 ldap_pvt_thread_pool_destroy ( ldap_pvt_thread_pool_t *tpool, int run_pending )
291 {
292         struct ldap_int_thread_pool_s *pool, *pptr;
293         long waiting;
294         ldap_int_thread_ctx_t *ctx;
295
296         if (tpool == NULL)
297                 return(-1);
298
299         pool = *tpool;
300
301         if (pool == NULL) return(-1);
302
303         ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
304         LDAP_STAILQ_FOREACH(pptr, &ldap_int_thread_pool_list, ltp_next)
305                 if (pptr == pool) break;
306         if (pptr == pool)
307                 LDAP_STAILQ_REMOVE(&ldap_int_thread_pool_list, pool,
308                         ldap_int_thread_pool_s, ltp_next);
309         ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
310
311         if (pool != pptr) return(-1);
312
313         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
314         pool->ltp_state = run_pending
315                 ? LDAP_INT_THREAD_POOL_FINISHING
316                 : LDAP_INT_THREAD_POOL_STOPPING;
317         waiting = pool->ltp_open_count;
318
319         /* broadcast could be used here, but only after
320          * it is fixed in the NT thread implementation
321          */
322         while (--waiting >= 0) {
323                 ldap_pvt_thread_cond_signal(&pool->ltp_cond);
324         }
325         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
326
327         do {
328                 ldap_pvt_thread_yield();
329                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
330                 waiting = pool->ltp_open_count;
331                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
332         } while (waiting > 0);
333
334         while ((ctx = LDAP_STAILQ_FIRST(&pool->ltp_pending_list)) != NULL)
335         {
336                 LDAP_STAILQ_REMOVE_HEAD(&pool->ltp_pending_list, ltc_next.q);
337                 LDAP_FREE(ctx);
338         }
339
340         while ((ctx = LDAP_SLIST_FIRST(&pool->ltp_free_list)) != NULL)
341         {
342                 LDAP_SLIST_REMOVE_HEAD(&pool->ltp_free_list, ltc_next.l);
343                 LDAP_FREE(ctx);
344         }
345
346         ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
347         ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
348         LDAP_FREE(pool);
349         return(0);
350 }
351
352 static void *
353 ldap_int_thread_pool_wrapper ( 
354         void *xpool )
355 {
356         struct ldap_int_thread_pool_s *pool = xpool;
357         ldap_int_thread_ctx_t *ctx;
358
359         if (pool == NULL)
360                 return NULL;
361
362         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
363
364         while (pool->ltp_state != LDAP_INT_THREAD_POOL_STOPPING) {
365                 ctx = LDAP_STAILQ_FIRST(&pool->ltp_pending_list);
366                 if (ctx) {
367                         LDAP_STAILQ_REMOVE_HEAD(&pool->ltp_pending_list, ltc_next.q);
368                 } else {
369                         if (pool->ltp_state == LDAP_INT_THREAD_POOL_FINISHING)
370                                 break;
371                         if (pool->ltp_max_count > 0
372                                 && pool->ltp_open_count > pool->ltp_max_count)
373                         {
374                                 /* too many threads running (can happen if the
375                                  * maximum threads value is set during ongoing
376                                  * operation using ldap_pvt_thread_pool_maxthreads)
377                                  * so let this thread die.
378                                  */
379                                 break;
380                         }
381
382                         /* we could check an idle timer here, and let the
383                          * thread die if it has been inactive for a while.
384                          * only die if there are other open threads (i.e.,
385                          * always have at least one thread open).  the check
386                          * should be like this:
387                          *   if (pool->ltp_open_count > 1 && pool->ltp_starting == 0)
388                          *       check timer, leave thread (break;)
389                          */
390
391                         if (pool->ltp_state == LDAP_INT_THREAD_POOL_RUNNING)
392                                 ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex);
393
394                         continue;
395                 }
396
397                 pool->ltp_pending_count--;
398                 pool->ltp_active_count++;
399                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
400
401                 (ctx->ltc_start_routine)(ctx->ltc_arg);
402                 LDAP_SLIST_INSERT_HEAD(&pool->ltp_free_list, ctx, ltc_next.l);
403                 ldap_pvt_thread_yield();
404
405                 /* if we use an idle timer, here's
406                  * a good place to update it
407                  */
408
409                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
410                 pool->ltp_active_count--;
411         }
412
413         pool->ltp_open_count--;
414         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
415
416         ldap_pvt_thread_exit(NULL);
417         return(NULL);
418 }
419 #endif /* LDAP_HAVE_THREAD_POOL */