]> git.sur5r.net Git - openldap/blob - libraries/libldap_r/tpool.c
Reworked thread code to better support thread-library specific
[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 };
48
49 typedef struct ldap_int_thread_ctx_s {
50         struct ldap_int_thread_ctx_s *ltc_next;
51         void *(*ltc_start_routine)( void *);
52         void *ltc_arg;
53 } ldap_int_thread_ctx_t;
54
55 static ldap_int_thread_list_t ldap_int_thread_pool_list = NULL;
56 static ldap_pvt_thread_mutex_t ldap_pvt_thread_pool_mutex;
57
58 static void *ldap_int_thread_pool_wrapper(
59         struct ldap_int_thread_pool_s *pool );
60
61 static void *ldap_int_thread_enlist( ldap_int_thread_list_t *list, void *elem );
62 static void *ldap_int_thread_delist( ldap_int_thread_list_t *list, void *elem );
63 static void *ldap_int_thread_onlist( ldap_int_thread_list_t *list, void *elem );
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         while (ldap_int_thread_pool_list != NULL) {
75                 struct ldap_int_thread_pool_s *pool =
76                         (struct ldap_int_thread_pool_s *) ldap_int_thread_pool_list;
77
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_concurrency,
88         int max_pending )
89 {
90         int rc;
91         ldap_pvt_thread_pool_t pool;
92         ldap_pvt_thread_t thr;
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         ldap_pvt_thread_mutex_init(&pool->ltp_mutex);
101         ldap_pvt_thread_cond_init(&pool->ltp_cond);
102         pool->ltp_state = LDAP_INT_THREAD_POOL_RUNNING;
103         pool->ltp_max_count = max_concurrency;
104         pool->ltp_max_pending = max_pending;
105         ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
106         ldap_int_thread_enlist(&ldap_int_thread_pool_list, pool);
107         ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
108
109         /* start up one thread, just so there is one */
110         pool->ltp_open_count++;
111
112         rc = ldap_pvt_thread_create( &thr, 1,
113                 (void *) ldap_int_thread_pool_wrapper, pool );
114
115         if( rc != 0) {
116                 /* couldn't start one?  then don't start any */
117                 ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
118                 ldap_int_thread_delist(&ldap_int_thread_pool_list, pool);
119                 ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
120                 ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
121                 ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
122                 free(pool);
123                 return(-1);
124         }
125
126         *tpool = pool;
127         return(0);
128 }
129
130 int
131 ldap_pvt_thread_pool_submit (
132         ldap_pvt_thread_pool_t *tpool,
133         void *(*start_routine)( void * ), void *arg )
134 {
135         struct ldap_int_thread_pool_s *pool;
136         ldap_int_thread_ctx_t *ctx;
137         int need_thread = 0;
138         ldap_pvt_thread_t thr;
139
140         if (tpool == NULL)
141                 return(-1);
142
143         pool = *tpool;
144
145         if (pool == NULL)
146                 return(-1);
147
148         ctx = (ldap_int_thread_ctx_t *) LDAP_CALLOC(1,
149                 sizeof(ldap_int_thread_ctx_t));
150
151         if (ctx == NULL) return(-1);
152
153         ctx->ltc_start_routine = start_routine;
154         ctx->ltc_arg = arg;
155
156         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
157         if (pool->ltp_state != LDAP_INT_THREAD_POOL_RUNNING
158                 || (pool->ltp_max_pending > 0
159                         && pool->ltp_pending_count >= pool->ltp_max_pending))
160         {
161                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
162                 free(ctx);
163                 return(-1);
164         }
165         pool->ltp_pending_count++;
166         ldap_int_thread_enlist(&pool->ltp_pending_list, ctx);
167         ldap_pvt_thread_cond_signal(&pool->ltp_cond);
168         if ((pool->ltp_open_count <= 0
169                         || pool->ltp_pending_count > 1
170                         || pool->ltp_open_count == pool->ltp_active_count)
171                 && (pool->ltp_max_count <= 0
172                         || pool->ltp_open_count < pool->ltp_max_count))
173         {
174                 pool->ltp_open_count++;
175                 need_thread = 1;
176         }
177         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
178
179         if (need_thread) {
180                 int rc = ldap_pvt_thread_create( &thr, 1,
181                         (void *)ldap_int_thread_pool_wrapper, pool );
182                 if (rc != 0) {
183                         /* couldn't create thread.  back out of
184                          * ltp_open_count and check for even worse things.
185                          */
186                         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
187                         pool->ltp_open_count--;
188                         if (pool->ltp_open_count == 0) {
189                                 /* no open threads at all?!?  this will never happen
190                                  * because we always leave at least one thread open.
191                                  */
192                                 if (ldap_int_thread_delist(&pool->ltp_pending_list, ctx)) {
193                                         /* no open threads, context not handled, so
194                                          * back out of ltp_pending_count, free the context,
195                                          * report the error.
196                                          */
197                                         pool->ltp_pending_count++;
198                                         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
199                                         free(ctx);
200                                         return(-1);
201                                 }
202                         }
203                         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
204                         /* there is another open thread, so this
205                          * context will be handled eventually.
206                          * continue on and signal that the context
207                          * is waiting.
208                          */
209                 }
210         }
211
212         return(0);
213 }
214
215 int
216 ldap_pvt_thread_pool_backload ( ldap_pvt_thread_pool_t *tpool )
217 {
218         struct ldap_int_thread_pool_s *pool;
219         int count;
220
221         if (tpool == NULL)
222                 return(-1);
223
224         pool = *tpool;
225
226         if (pool == NULL)
227                 return(0);
228
229         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
230         count = pool->ltp_pending_count + pool->ltp_active_count;
231         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
232         return(count);
233 }
234
235 int
236 ldap_pvt_thread_pool_destroy ( ldap_pvt_thread_pool_t *tpool, int run_pending )
237 {
238         struct ldap_int_thread_pool_s *pool;
239         long waiting;
240         ldap_int_thread_ctx_t *ctx;
241
242         if (tpool == NULL)
243                 return(-1);
244
245         pool = *tpool;
246
247         if (pool == NULL) return(-1);
248
249         ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
250         pool = ldap_int_thread_delist(&ldap_int_thread_pool_list, pool);
251         ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
252
253         if (pool == NULL) return(-1);
254
255         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
256         pool->ltp_state = run_pending
257                 ? LDAP_INT_THREAD_POOL_FINISHING
258                 : LDAP_INT_THREAD_POOL_STOPPING;
259         waiting = pool->ltp_open_count;
260         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
261
262         /* broadcast could be used here, but only after
263          * it is fixed in the NT thread implementation
264          */
265         while (--waiting >= 0) {
266                 ldap_pvt_thread_cond_signal(&pool->ltp_cond);
267         }
268
269         do {
270                 ldap_pvt_thread_yield();
271                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
272                 waiting = pool->ltp_open_count;
273                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
274         } while (waiting > 0);
275
276         while (ctx = (ldap_int_thread_ctx_t *)ldap_int_thread_delist(
277                 &pool->ltp_pending_list, NULL))
278         {
279                 free(ctx);
280         }
281
282         ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
283         ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
284         free(pool);
285         return(0);
286 }
287
288 static void *
289 ldap_int_thread_pool_wrapper ( 
290         struct ldap_int_thread_pool_s *pool )
291 {
292         ldap_int_thread_ctx_t *ctx;
293
294         if (pool == NULL)
295                 return NULL;
296
297         ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
298
299         while (pool->ltp_state != LDAP_INT_THREAD_POOL_STOPPING) {
300
301                 ctx = ldap_int_thread_delist(&pool->ltp_pending_list, NULL);
302                 if (ctx == NULL) {
303                         if (pool->ltp_state == LDAP_INT_THREAD_POOL_FINISHING)
304                                 break;
305                         /* we could check an idle timer here, and let the
306                          * thread die if it has been inactive for a while.
307                          * only die if there are other open threads (i.e.,
308                          * always have at least one thread open).
309                          */
310
311                         if (pool->ltp_state == LDAP_INT_THREAD_POOL_RUNNING)
312                                 ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex);
313
314                         continue;
315                 }
316
317                 pool->ltp_pending_count--;
318                 pool->ltp_active_count++;
319                 ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
320
321                 (ctx->ltc_start_routine)(ctx->ltc_arg);
322                 free(ctx);
323                 ldap_pvt_thread_yield();
324
325                 /* if we use an idle timer, here's
326                  * a good place to update it
327                  */
328
329                 ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
330                 pool->ltp_active_count--;
331         }
332
333         pool->ltp_open_count--;
334         ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
335
336         ldap_pvt_thread_exit(NULL);
337         return(NULL);
338 }
339
340 static void *
341 ldap_int_thread_enlist( ldap_int_thread_list_t *list, void *elem )
342 {
343         ldap_int_thread_list_element_t *prev;
344
345         if (elem == NULL) return(NULL);
346
347         ((ldap_int_thread_list_element_t *)elem)->next = NULL;
348         if (*list == NULL) {
349                 *list = elem;
350                 return(elem);
351         }
352
353         for (prev = *list ; prev->next != NULL; prev = prev->next) ;
354         prev->next = elem;
355         return(elem);
356 }
357
358 static void *
359 ldap_int_thread_delist( ldap_int_thread_list_t *list, void *elem )
360 {
361         ldap_int_thread_list_element_t *prev;
362
363         if (*list == NULL) return(NULL);
364
365         if (elem == NULL) elem = *list;
366
367         if (*list == elem) {
368                 *list = ((ldap_int_thread_list_element_t *)elem)->next;
369                 return(elem);
370         }
371
372         for (prev = *list ; prev->next != NULL; prev = prev->next) {
373                 if (prev->next == elem) {
374                         prev->next = ((ldap_int_thread_list_element_t *)elem)->next;
375                         return(elem);
376                 }
377         }
378         return(NULL);
379 }
380
381 static void *
382 ldap_int_thread_onlist( ldap_int_thread_list_t *list, void *elem )
383 {
384         ldap_int_thread_list_element_t *prev;
385
386         if (elem == NULL || *list == NULL) return(NULL);
387
388         for (prev = *list ; prev != NULL; prev = prev->next) {
389                 if (prev == elem)
390                         return(elem);
391         }
392
393         return(NULL);
394 }
395
396 #endif /* LDAP_HAVE_THREAD_POOL */