]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - contrib/bind9/lib/isc/task.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.git] / contrib / bind9 / lib / isc / task.c
1 /*
2  * Copyright (C) 2004-2011  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1998-2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15  * PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 /* $Id: task.c,v 1.115.14.2 2011-02-28 01:20:03 tbox Exp $ */
19
20 /*! \file
21  * \author Principal Author: Bob Halley
22  */
23
24 /*
25  * XXXRTH  Need to document the states a task can be in, and the rules
26  * for changing states.
27  */
28
29 #include <config.h>
30
31 #include <isc/condition.h>
32 #include <isc/event.h>
33 #include <isc/magic.h>
34 #include <isc/mem.h>
35 #include <isc/msgs.h>
36 #include <isc/platform.h>
37 #include <isc/string.h>
38 #include <isc/task.h>
39 #include <isc/thread.h>
40 #include <isc/util.h>
41 #include <isc/xml.h>
42
43 #ifdef OPENSSL_LEAKS
44 #include <openssl/err.h>
45 #endif
46
47 /*%
48  * For BIND9 internal applications:
49  * when built with threads we use multiple worker threads shared by the whole
50  * application.
51  * when built without threads we share a single global task manager and use
52  * an integrated event loop for socket, timer, and other generic task events.
53  * For generic library:
54  * we don't use either of them: an application can have multiple task managers
55  * whether or not it's threaded, and if the application is threaded each thread
56  * is expected to have a separate manager; no "worker threads" are shared by
57  * the application threads.
58  */
59 #ifdef BIND9
60 #ifdef ISC_PLATFORM_USETHREADS
61 #define USE_WORKER_THREADS
62 #else
63 #define USE_SHARED_MANAGER
64 #endif  /* ISC_PLATFORM_USETHREADS */
65 #endif  /* BIND9 */
66
67 #ifndef USE_WORKER_THREADS
68 #include "task_p.h"
69 #endif /* USE_WORKER_THREADS */
70
71 #ifdef ISC_TASK_TRACE
72 #define XTRACE(m)               fprintf(stderr, "task %p thread %lu: %s\n", \
73                                        task, isc_thread_self(), (m))
74 #define XTTRACE(t, m)           fprintf(stderr, "task %p thread %lu: %s\n", \
75                                        (t), isc_thread_self(), (m))
76 #define XTHREADTRACE(m)         fprintf(stderr, "thread %lu: %s\n", \
77                                        isc_thread_self(), (m))
78 #else
79 #define XTRACE(m)
80 #define XTTRACE(t, m)
81 #define XTHREADTRACE(m)
82 #endif
83
84 /***
85  *** Types.
86  ***/
87
88 typedef enum {
89         task_state_idle, task_state_ready, task_state_running,
90         task_state_done
91 } task_state_t;
92
93 #if defined(HAVE_LIBXML2) && defined(BIND9)
94 static const char *statenames[] = {
95         "idle", "ready", "running", "done",
96 };
97 #endif
98
99 #define TASK_MAGIC                      ISC_MAGIC('T', 'A', 'S', 'K')
100 #define VALID_TASK(t)                   ISC_MAGIC_VALID(t, TASK_MAGIC)
101
102 typedef struct isc__task isc__task_t;
103 typedef struct isc__taskmgr isc__taskmgr_t;
104
105 struct isc__task {
106         /* Not locked. */
107         isc_task_t                      common;
108         isc__taskmgr_t *                manager;
109         isc_mutex_t                     lock;
110         /* Locked by task lock. */
111         task_state_t                    state;
112         unsigned int                    references;
113         isc_eventlist_t                 events;
114         isc_eventlist_t                 on_shutdown;
115         unsigned int                    quantum;
116         unsigned int                    flags;
117         isc_stdtime_t                   now;
118         char                            name[16];
119         void *                          tag;
120         /* Locked by task manager lock. */
121         LINK(isc__task_t)               link;
122         LINK(isc__task_t)               ready_link;
123 };
124
125 #define TASK_F_SHUTTINGDOWN             0x01
126
127 #define TASK_SHUTTINGDOWN(t)            (((t)->flags & TASK_F_SHUTTINGDOWN) \
128                                          != 0)
129
130 #define TASK_MANAGER_MAGIC              ISC_MAGIC('T', 'S', 'K', 'M')
131 #define VALID_MANAGER(m)                ISC_MAGIC_VALID(m, TASK_MANAGER_MAGIC)
132
133 typedef ISC_LIST(isc__task_t)   isc__tasklist_t;
134
135 struct isc__taskmgr {
136         /* Not locked. */
137         isc_taskmgr_t                   common;
138         isc_mem_t *                     mctx;
139         isc_mutex_t                     lock;
140 #ifdef ISC_PLATFORM_USETHREADS
141         unsigned int                    workers;
142         isc_thread_t *                  threads;
143 #endif /* ISC_PLATFORM_USETHREADS */
144         /* Locked by task manager lock. */
145         unsigned int                    default_quantum;
146         LIST(isc__task_t)               tasks;
147         isc__tasklist_t                 ready_tasks;
148 #ifdef ISC_PLATFORM_USETHREADS
149         isc_condition_t                 work_available;
150         isc_condition_t                 exclusive_granted;
151 #endif /* ISC_PLATFORM_USETHREADS */
152         unsigned int                    tasks_running;
153         isc_boolean_t                   exclusive_requested;
154         isc_boolean_t                   exiting;
155 #ifdef USE_SHARED_MANAGER
156         unsigned int                    refs;
157 #endif /* ISC_PLATFORM_USETHREADS */
158 };
159
160 #define DEFAULT_TASKMGR_QUANTUM         10
161 #define DEFAULT_DEFAULT_QUANTUM         5
162 #define FINISHED(m)                     ((m)->exiting && EMPTY((m)->tasks))
163
164 #ifdef USE_SHARED_MANAGER
165 static isc__taskmgr_t *taskmgr = NULL;
166 #endif /* USE_SHARED_MANAGER */
167
168 /*%
169  * The following can be either static or public, depending on build environment.
170  */
171
172 #ifdef BIND9
173 #define ISC_TASKFUNC_SCOPE
174 #else
175 #define ISC_TASKFUNC_SCOPE static
176 #endif
177
178 ISC_TASKFUNC_SCOPE isc_result_t
179 isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum,
180                  isc_task_t **taskp);
181 ISC_TASKFUNC_SCOPE void
182 isc__task_attach(isc_task_t *source0, isc_task_t **targetp);
183 ISC_TASKFUNC_SCOPE void
184 isc__task_detach(isc_task_t **taskp);
185 ISC_TASKFUNC_SCOPE void
186 isc__task_send(isc_task_t *task0, isc_event_t **eventp);
187 ISC_TASKFUNC_SCOPE void
188 isc__task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp);
189 ISC_TASKFUNC_SCOPE unsigned int
190 isc__task_purgerange(isc_task_t *task0, void *sender, isc_eventtype_t first,
191                      isc_eventtype_t last, void *tag);
192 ISC_TASKFUNC_SCOPE unsigned int
193 isc__task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
194                 void *tag);
195 ISC_TASKFUNC_SCOPE isc_boolean_t
196 isc__task_purgeevent(isc_task_t *task0, isc_event_t *event);
197 ISC_TASKFUNC_SCOPE unsigned int
198 isc__task_unsendrange(isc_task_t *task, void *sender, isc_eventtype_t first,
199                       isc_eventtype_t last, void *tag,
200                       isc_eventlist_t *events);
201 ISC_TASKFUNC_SCOPE unsigned int
202 isc__task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type,
203                  void *tag, isc_eventlist_t *events);
204 ISC_TASKFUNC_SCOPE isc_result_t
205 isc__task_onshutdown(isc_task_t *task0, isc_taskaction_t action,
206                      const void *arg);
207 ISC_TASKFUNC_SCOPE void
208 isc__task_shutdown(isc_task_t *task0);
209 ISC_TASKFUNC_SCOPE void
210 isc__task_destroy(isc_task_t **taskp);
211 ISC_TASKFUNC_SCOPE void
212 isc__task_setname(isc_task_t *task0, const char *name, void *tag);
213 ISC_TASKFUNC_SCOPE const char *
214 isc__task_getname(isc_task_t *task0);
215 ISC_TASKFUNC_SCOPE void *
216 isc__task_gettag(isc_task_t *task0);
217 ISC_TASKFUNC_SCOPE void
218 isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t);
219 ISC_TASKFUNC_SCOPE isc_result_t
220 isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers,
221                     unsigned int default_quantum, isc_taskmgr_t **managerp);
222 ISC_TASKFUNC_SCOPE void
223 isc__taskmgr_destroy(isc_taskmgr_t **managerp);
224 ISC_TASKFUNC_SCOPE isc_result_t
225 isc__task_beginexclusive(isc_task_t *task);
226 ISC_TASKFUNC_SCOPE void
227 isc__task_endexclusive(isc_task_t *task0);
228
229 static struct isc__taskmethods {
230         isc_taskmethods_t methods;
231
232         /*%
233          * The following are defined just for avoiding unused static functions.
234          */
235 #ifndef BIND9
236         void *purgeevent, *unsendrange, *getname, *gettag, *getcurrenttime;
237 #endif
238 } taskmethods = {
239         {
240                 isc__task_attach,
241                 isc__task_detach,
242                 isc__task_destroy,
243                 isc__task_send,
244                 isc__task_sendanddetach,
245                 isc__task_unsend,
246                 isc__task_onshutdown,
247                 isc__task_shutdown,
248                 isc__task_setname,
249                 isc__task_purge,
250                 isc__task_purgerange,
251                 isc__task_beginexclusive,
252                 isc__task_endexclusive
253         }
254 #ifndef BIND9
255         ,
256         (void *)isc__task_purgeevent, (void *)isc__task_unsendrange,
257         (void *)isc__task_getname, (void *)isc__task_gettag,
258         (void *)isc__task_getcurrenttime
259 #endif
260 };
261
262 static isc_taskmgrmethods_t taskmgrmethods = {
263         isc__taskmgr_destroy,
264         isc__task_create
265 };
266
267 /***
268  *** Tasks.
269  ***/
270
271 static void
272 task_finished(isc__task_t *task) {
273         isc__taskmgr_t *manager = task->manager;
274
275         REQUIRE(EMPTY(task->events));
276         REQUIRE(EMPTY(task->on_shutdown));
277         REQUIRE(task->references == 0);
278         REQUIRE(task->state == task_state_done);
279
280         XTRACE("task_finished");
281
282         LOCK(&manager->lock);
283         UNLINK(manager->tasks, task, link);
284 #ifdef USE_WORKER_THREADS
285         if (FINISHED(manager)) {
286                 /*
287                  * All tasks have completed and the
288                  * task manager is exiting.  Wake up
289                  * any idle worker threads so they
290                  * can exit.
291                  */
292                 BROADCAST(&manager->work_available);
293         }
294 #endif /* USE_WORKER_THREADS */
295         UNLOCK(&manager->lock);
296
297         DESTROYLOCK(&task->lock);
298         task->common.impmagic = 0;
299         task->common.magic = 0;
300         isc_mem_put(manager->mctx, task, sizeof(*task));
301 }
302
303 ISC_TASKFUNC_SCOPE isc_result_t
304 isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum,
305                  isc_task_t **taskp)
306 {
307         isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0;
308         isc__task_t *task;
309         isc_boolean_t exiting;
310         isc_result_t result;
311
312         REQUIRE(VALID_MANAGER(manager));
313         REQUIRE(taskp != NULL && *taskp == NULL);
314
315         task = isc_mem_get(manager->mctx, sizeof(*task));
316         if (task == NULL)
317                 return (ISC_R_NOMEMORY);
318         XTRACE("isc_task_create");
319         task->manager = manager;
320         result = isc_mutex_init(&task->lock);
321         if (result != ISC_R_SUCCESS) {
322                 isc_mem_put(manager->mctx, task, sizeof(*task));
323                 return (result);
324         }
325         task->state = task_state_idle;
326         task->references = 1;
327         INIT_LIST(task->events);
328         INIT_LIST(task->on_shutdown);
329         task->quantum = quantum;
330         task->flags = 0;
331         task->now = 0;
332         memset(task->name, 0, sizeof(task->name));
333         task->tag = NULL;
334         INIT_LINK(task, link);
335         INIT_LINK(task, ready_link);
336
337         exiting = ISC_FALSE;
338         LOCK(&manager->lock);
339         if (!manager->exiting) {
340                 if (task->quantum == 0)
341                         task->quantum = manager->default_quantum;
342                 APPEND(manager->tasks, task, link);
343         } else
344                 exiting = ISC_TRUE;
345         UNLOCK(&manager->lock);
346
347         if (exiting) {
348                 DESTROYLOCK(&task->lock);
349                 isc_mem_put(manager->mctx, task, sizeof(*task));
350                 return (ISC_R_SHUTTINGDOWN);
351         }
352
353         task->common.methods = (isc_taskmethods_t *)&taskmethods;
354         task->common.magic = ISCAPI_TASK_MAGIC;
355         task->common.impmagic = TASK_MAGIC;
356         *taskp = (isc_task_t *)task;
357
358         return (ISC_R_SUCCESS);
359 }
360
361 ISC_TASKFUNC_SCOPE void
362 isc__task_attach(isc_task_t *source0, isc_task_t **targetp) {
363         isc__task_t *source = (isc__task_t *)source0;
364
365         /*
366          * Attach *targetp to source.
367          */
368
369         REQUIRE(VALID_TASK(source));
370         REQUIRE(targetp != NULL && *targetp == NULL);
371
372         XTTRACE(source, "isc_task_attach");
373
374         LOCK(&source->lock);
375         source->references++;
376         UNLOCK(&source->lock);
377
378         *targetp = (isc_task_t *)source;
379 }
380
381 static inline isc_boolean_t
382 task_shutdown(isc__task_t *task) {
383         isc_boolean_t was_idle = ISC_FALSE;
384         isc_event_t *event, *prev;
385
386         /*
387          * Caller must be holding the task's lock.
388          */
389
390         XTRACE("task_shutdown");
391
392         if (! TASK_SHUTTINGDOWN(task)) {
393                 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
394                                       ISC_MSG_SHUTTINGDOWN, "shutting down"));
395                 task->flags |= TASK_F_SHUTTINGDOWN;
396                 if (task->state == task_state_idle) {
397                         INSIST(EMPTY(task->events));
398                         task->state = task_state_ready;
399                         was_idle = ISC_TRUE;
400                 }
401                 INSIST(task->state == task_state_ready ||
402                        task->state == task_state_running);
403                 /*
404                  * Note that we post shutdown events LIFO.
405                  */
406                 for (event = TAIL(task->on_shutdown);
407                      event != NULL;
408                      event = prev) {
409                         prev = PREV(event, ev_link);
410                         DEQUEUE(task->on_shutdown, event, ev_link);
411                         ENQUEUE(task->events, event, ev_link);
412                 }
413         }
414
415         return (was_idle);
416 }
417
418 static inline void
419 task_ready(isc__task_t *task) {
420         isc__taskmgr_t *manager = task->manager;
421
422         REQUIRE(VALID_MANAGER(manager));
423         REQUIRE(task->state == task_state_ready);
424
425         XTRACE("task_ready");
426
427         LOCK(&manager->lock);
428
429         ENQUEUE(manager->ready_tasks, task, ready_link);
430 #ifdef USE_WORKER_THREADS
431         SIGNAL(&manager->work_available);
432 #endif /* USE_WORKER_THREADS */
433
434         UNLOCK(&manager->lock);
435 }
436
437 static inline isc_boolean_t
438 task_detach(isc__task_t *task) {
439
440         /*
441          * Caller must be holding the task lock.
442          */
443
444         REQUIRE(task->references > 0);
445
446         XTRACE("detach");
447
448         task->references--;
449         if (task->references == 0 && task->state == task_state_idle) {
450                 INSIST(EMPTY(task->events));
451                 /*
452                  * There are no references to this task, and no
453                  * pending events.  We could try to optimize and
454                  * either initiate shutdown or clean up the task,
455                  * depending on its state, but it's easier to just
456                  * make the task ready and allow run() or the event
457                  * loop to deal with shutting down and termination.
458                  */
459                 task->state = task_state_ready;
460                 return (ISC_TRUE);
461         }
462
463         return (ISC_FALSE);
464 }
465
466 ISC_TASKFUNC_SCOPE void
467 isc__task_detach(isc_task_t **taskp) {
468         isc__task_t *task;
469         isc_boolean_t was_idle;
470
471         /*
472          * Detach *taskp from its task.
473          */
474
475         REQUIRE(taskp != NULL);
476         task = (isc__task_t *)*taskp;
477         REQUIRE(VALID_TASK(task));
478
479         XTRACE("isc_task_detach");
480
481         LOCK(&task->lock);
482         was_idle = task_detach(task);
483         UNLOCK(&task->lock);
484
485         if (was_idle)
486                 task_ready(task);
487
488         *taskp = NULL;
489 }
490
491 static inline isc_boolean_t
492 task_send(isc__task_t *task, isc_event_t **eventp) {
493         isc_boolean_t was_idle = ISC_FALSE;
494         isc_event_t *event;
495
496         /*
497          * Caller must be holding the task lock.
498          */
499
500         REQUIRE(eventp != NULL);
501         event = *eventp;
502         REQUIRE(event != NULL);
503         REQUIRE(event->ev_type > 0);
504         REQUIRE(task->state != task_state_done);
505
506         XTRACE("task_send");
507
508         if (task->state == task_state_idle) {
509                 was_idle = ISC_TRUE;
510                 INSIST(EMPTY(task->events));
511                 task->state = task_state_ready;
512         }
513         INSIST(task->state == task_state_ready ||
514                task->state == task_state_running);
515         ENQUEUE(task->events, event, ev_link);
516         *eventp = NULL;
517
518         return (was_idle);
519 }
520
521 ISC_TASKFUNC_SCOPE void
522 isc__task_send(isc_task_t *task0, isc_event_t **eventp) {
523         isc__task_t *task = (isc__task_t *)task0;
524         isc_boolean_t was_idle;
525
526         /*
527          * Send '*event' to 'task'.
528          */
529
530         REQUIRE(VALID_TASK(task));
531
532         XTRACE("isc_task_send");
533
534         /*
535          * We're trying hard to hold locks for as short a time as possible.
536          * We're also trying to hold as few locks as possible.  This is why
537          * some processing is deferred until after the lock is released.
538          */
539         LOCK(&task->lock);
540         was_idle = task_send(task, eventp);
541         UNLOCK(&task->lock);
542
543         if (was_idle) {
544                 /*
545                  * We need to add this task to the ready queue.
546                  *
547                  * We've waited until now to do it because making a task
548                  * ready requires locking the manager.  If we tried to do
549                  * this while holding the task lock, we could deadlock.
550                  *
551                  * We've changed the state to ready, so no one else will
552                  * be trying to add this task to the ready queue.  The
553                  * only way to leave the ready state is by executing the
554                  * task.  It thus doesn't matter if events are added,
555                  * removed, or a shutdown is started in the interval
556                  * between the time we released the task lock, and the time
557                  * we add the task to the ready queue.
558                  */
559                 task_ready(task);
560         }
561 }
562
563 ISC_TASKFUNC_SCOPE void
564 isc__task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
565         isc_boolean_t idle1, idle2;
566         isc__task_t *task;
567
568         /*
569          * Send '*event' to '*taskp' and then detach '*taskp' from its
570          * task.
571          */
572
573         REQUIRE(taskp != NULL);
574         task = (isc__task_t *)*taskp;
575         REQUIRE(VALID_TASK(task));
576
577         XTRACE("isc_task_sendanddetach");
578
579         LOCK(&task->lock);
580         idle1 = task_send(task, eventp);
581         idle2 = task_detach(task);
582         UNLOCK(&task->lock);
583
584         /*
585          * If idle1, then idle2 shouldn't be true as well since we're holding
586          * the task lock, and thus the task cannot switch from ready back to
587          * idle.
588          */
589         INSIST(!(idle1 && idle2));
590
591         if (idle1 || idle2)
592                 task_ready(task);
593
594         *taskp = NULL;
595 }
596
597 #define PURGE_OK(event) (((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0)
598
599 static unsigned int
600 dequeue_events(isc__task_t *task, void *sender, isc_eventtype_t first,
601                isc_eventtype_t last, void *tag,
602                isc_eventlist_t *events, isc_boolean_t purging)
603 {
604         isc_event_t *event, *next_event;
605         unsigned int count = 0;
606
607         REQUIRE(VALID_TASK(task));
608         REQUIRE(last >= first);
609
610         XTRACE("dequeue_events");
611
612         /*
613          * Events matching 'sender', whose type is >= first and <= last, and
614          * whose tag is 'tag' will be dequeued.  If 'purging', matching events
615          * which are marked as unpurgable will not be dequeued.
616          *
617          * sender == NULL means "any sender", and tag == NULL means "any tag".
618          */
619
620         LOCK(&task->lock);
621
622         for (event = HEAD(task->events); event != NULL; event = next_event) {
623                 next_event = NEXT(event, ev_link);
624                 if (event->ev_type >= first && event->ev_type <= last &&
625                     (sender == NULL || event->ev_sender == sender) &&
626                     (tag == NULL || event->ev_tag == tag) &&
627                     (!purging || PURGE_OK(event))) {
628                         DEQUEUE(task->events, event, ev_link);
629                         ENQUEUE(*events, event, ev_link);
630                         count++;
631                 }
632         }
633
634         UNLOCK(&task->lock);
635
636         return (count);
637 }
638
639 ISC_TASKFUNC_SCOPE unsigned int
640 isc__task_purgerange(isc_task_t *task0, void *sender, isc_eventtype_t first,
641                      isc_eventtype_t last, void *tag)
642 {
643         isc__task_t *task = (isc__task_t *)task0;
644         unsigned int count;
645         isc_eventlist_t events;
646         isc_event_t *event, *next_event;
647
648         /*
649          * Purge events from a task's event queue.
650          */
651
652         XTRACE("isc_task_purgerange");
653
654         ISC_LIST_INIT(events);
655
656         count = dequeue_events(task, sender, first, last, tag, &events,
657                                ISC_TRUE);
658
659         for (event = HEAD(events); event != NULL; event = next_event) {
660                 next_event = NEXT(event, ev_link);
661                 isc_event_free(&event);
662         }
663
664         /*
665          * Note that purging never changes the state of the task.
666          */
667
668         return (count);
669 }
670
671 ISC_TASKFUNC_SCOPE unsigned int
672 isc__task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
673                 void *tag)
674 {
675         /*
676          * Purge events from a task's event queue.
677          */
678
679         XTRACE("isc_task_purge");
680
681         return (isc__task_purgerange(task, sender, type, type, tag));
682 }
683
684 ISC_TASKFUNC_SCOPE isc_boolean_t
685 isc__task_purgeevent(isc_task_t *task0, isc_event_t *event) {
686         isc__task_t *task = (isc__task_t *)task0;
687         isc_event_t *curr_event, *next_event;
688
689         /*
690          * Purge 'event' from a task's event queue.
691          *
692          * XXXRTH:  WARNING:  This method may be removed before beta.
693          */
694
695         REQUIRE(VALID_TASK(task));
696
697         /*
698          * If 'event' is on the task's event queue, it will be purged,
699          * unless it is marked as unpurgeable.  'event' does not have to be
700          * on the task's event queue; in fact, it can even be an invalid
701          * pointer.  Purging only occurs if the event is actually on the task's
702          * event queue.
703          *
704          * Purging never changes the state of the task.
705          */
706
707         LOCK(&task->lock);
708         for (curr_event = HEAD(task->events);
709              curr_event != NULL;
710              curr_event = next_event) {
711                 next_event = NEXT(curr_event, ev_link);
712                 if (curr_event == event && PURGE_OK(event)) {
713                         DEQUEUE(task->events, curr_event, ev_link);
714                         break;
715                 }
716         }
717         UNLOCK(&task->lock);
718
719         if (curr_event == NULL)
720                 return (ISC_FALSE);
721
722         isc_event_free(&curr_event);
723
724         return (ISC_TRUE);
725 }
726
727 ISC_TASKFUNC_SCOPE unsigned int
728 isc__task_unsendrange(isc_task_t *task, void *sender, isc_eventtype_t first,
729                       isc_eventtype_t last, void *tag,
730                       isc_eventlist_t *events)
731 {
732         /*
733          * Remove events from a task's event queue.
734          */
735
736         XTRACE("isc_task_unsendrange");
737
738         return (dequeue_events((isc__task_t *)task, sender, first,
739                                last, tag, events, ISC_FALSE));
740 }
741
742 ISC_TASKFUNC_SCOPE unsigned int
743 isc__task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type,
744                  void *tag, isc_eventlist_t *events)
745 {
746         /*
747          * Remove events from a task's event queue.
748          */
749
750         XTRACE("isc_task_unsend");
751
752         return (dequeue_events((isc__task_t *)task, sender, type,
753                                type, tag, events, ISC_FALSE));
754 }
755
756 ISC_TASKFUNC_SCOPE isc_result_t
757 isc__task_onshutdown(isc_task_t *task0, isc_taskaction_t action,
758                      const void *arg)
759 {
760         isc__task_t *task = (isc__task_t *)task0;
761         isc_boolean_t disallowed = ISC_FALSE;
762         isc_result_t result = ISC_R_SUCCESS;
763         isc_event_t *event;
764
765         /*
766          * Send a shutdown event with action 'action' and argument 'arg' when
767          * 'task' is shutdown.
768          */
769
770         REQUIRE(VALID_TASK(task));
771         REQUIRE(action != NULL);
772
773         event = isc_event_allocate(task->manager->mctx,
774                                    NULL,
775                                    ISC_TASKEVENT_SHUTDOWN,
776                                    action,
777                                    arg,
778                                    sizeof(*event));
779         if (event == NULL)
780                 return (ISC_R_NOMEMORY);
781
782         LOCK(&task->lock);
783         if (TASK_SHUTTINGDOWN(task)) {
784                 disallowed = ISC_TRUE;
785                 result = ISC_R_SHUTTINGDOWN;
786         } else
787                 ENQUEUE(task->on_shutdown, event, ev_link);
788         UNLOCK(&task->lock);
789
790         if (disallowed)
791                 isc_mem_put(task->manager->mctx, event, sizeof(*event));
792
793         return (result);
794 }
795
796 ISC_TASKFUNC_SCOPE void
797 isc__task_shutdown(isc_task_t *task0) {
798         isc__task_t *task = (isc__task_t *)task0;
799         isc_boolean_t was_idle;
800
801         /*
802          * Shutdown 'task'.
803          */
804
805         REQUIRE(VALID_TASK(task));
806
807         LOCK(&task->lock);
808         was_idle = task_shutdown(task);
809         UNLOCK(&task->lock);
810
811         if (was_idle)
812                 task_ready(task);
813 }
814
815 ISC_TASKFUNC_SCOPE void
816 isc__task_destroy(isc_task_t **taskp) {
817
818         /*
819          * Destroy '*taskp'.
820          */
821
822         REQUIRE(taskp != NULL);
823
824         isc_task_shutdown(*taskp);
825         isc_task_detach(taskp);
826 }
827
828 ISC_TASKFUNC_SCOPE void
829 isc__task_setname(isc_task_t *task0, const char *name, void *tag) {
830         isc__task_t *task = (isc__task_t *)task0;
831
832         /*
833          * Name 'task'.
834          */
835
836         REQUIRE(VALID_TASK(task));
837
838         LOCK(&task->lock);
839         memset(task->name, 0, sizeof(task->name));
840         strncpy(task->name, name, sizeof(task->name) - 1);
841         task->tag = tag;
842         UNLOCK(&task->lock);
843 }
844
845 ISC_TASKFUNC_SCOPE const char *
846 isc__task_getname(isc_task_t *task0) {
847         isc__task_t *task = (isc__task_t *)task0;
848
849         REQUIRE(VALID_TASK(task));
850
851         return (task->name);
852 }
853
854 ISC_TASKFUNC_SCOPE void *
855 isc__task_gettag(isc_task_t *task0) {
856         isc__task_t *task = (isc__task_t *)task0;
857
858         REQUIRE(VALID_TASK(task));
859
860         return (task->tag);
861 }
862
863 ISC_TASKFUNC_SCOPE void
864 isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t) {
865         isc__task_t *task = (isc__task_t *)task0;
866
867         REQUIRE(VALID_TASK(task));
868         REQUIRE(t != NULL);
869
870         LOCK(&task->lock);
871
872         *t = task->now;
873
874         UNLOCK(&task->lock);
875 }
876
877 /***
878  *** Task Manager.
879  ***/
880 static void
881 dispatch(isc__taskmgr_t *manager) {
882         isc__task_t *task;
883 #ifndef USE_WORKER_THREADS
884         unsigned int total_dispatch_count = 0;
885         isc__tasklist_t ready_tasks;
886 #endif /* USE_WORKER_THREADS */
887
888         REQUIRE(VALID_MANAGER(manager));
889
890         /*
891          * Again we're trying to hold the lock for as short a time as possible
892          * and to do as little locking and unlocking as possible.
893          *
894          * In both while loops, the appropriate lock must be held before the
895          * while body starts.  Code which acquired the lock at the top of
896          * the loop would be more readable, but would result in a lot of
897          * extra locking.  Compare:
898          *
899          * Straightforward:
900          *
901          *      LOCK();
902          *      ...
903          *      UNLOCK();
904          *      while (expression) {
905          *              LOCK();
906          *              ...
907          *              UNLOCK();
908          *
909          *              Unlocked part here...
910          *
911          *              LOCK();
912          *              ...
913          *              UNLOCK();
914          *      }
915          *
916          * Note how if the loop continues we unlock and then immediately lock.
917          * For N iterations of the loop, this code does 2N+1 locks and 2N+1
918          * unlocks.  Also note that the lock is not held when the while
919          * condition is tested, which may or may not be important, depending
920          * on the expression.
921          *
922          * As written:
923          *
924          *      LOCK();
925          *      while (expression) {
926          *              ...
927          *              UNLOCK();
928          *
929          *              Unlocked part here...
930          *
931          *              LOCK();
932          *              ...
933          *      }
934          *      UNLOCK();
935          *
936          * For N iterations of the loop, this code does N+1 locks and N+1
937          * unlocks.  The while expression is always protected by the lock.
938          */
939
940 #ifndef USE_WORKER_THREADS
941         ISC_LIST_INIT(ready_tasks);
942 #endif
943         LOCK(&manager->lock);
944         while (!FINISHED(manager)) {
945 #ifdef USE_WORKER_THREADS
946                 /*
947                  * For reasons similar to those given in the comment in
948                  * isc_task_send() above, it is safe for us to dequeue
949                  * the task while only holding the manager lock, and then
950                  * change the task to running state while only holding the
951                  * task lock.
952                  */
953                 while ((EMPTY(manager->ready_tasks) ||
954                         manager->exclusive_requested) &&
955                         !FINISHED(manager))
956                 {
957                         XTHREADTRACE(isc_msgcat_get(isc_msgcat,
958                                                     ISC_MSGSET_GENERAL,
959                                                     ISC_MSG_WAIT, "wait"));
960                         WAIT(&manager->work_available, &manager->lock);
961                         XTHREADTRACE(isc_msgcat_get(isc_msgcat,
962                                                     ISC_MSGSET_TASK,
963                                                     ISC_MSG_AWAKE, "awake"));
964                 }
965 #else /* USE_WORKER_THREADS */
966                 if (total_dispatch_count >= DEFAULT_TASKMGR_QUANTUM ||
967                     EMPTY(manager->ready_tasks))
968                         break;
969 #endif /* USE_WORKER_THREADS */
970                 XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK,
971                                             ISC_MSG_WORKING, "working"));
972
973                 task = HEAD(manager->ready_tasks);
974                 if (task != NULL) {
975                         unsigned int dispatch_count = 0;
976                         isc_boolean_t done = ISC_FALSE;
977                         isc_boolean_t requeue = ISC_FALSE;
978                         isc_boolean_t finished = ISC_FALSE;
979                         isc_event_t *event;
980
981                         INSIST(VALID_TASK(task));
982
983                         /*
984                          * Note we only unlock the manager lock if we actually
985                          * have a task to do.  We must reacquire the manager
986                          * lock before exiting the 'if (task != NULL)' block.
987                          */
988                         DEQUEUE(manager->ready_tasks, task, ready_link);
989                         manager->tasks_running++;
990                         UNLOCK(&manager->lock);
991
992                         LOCK(&task->lock);
993                         INSIST(task->state == task_state_ready);
994                         task->state = task_state_running;
995                         XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
996                                               ISC_MSG_RUNNING, "running"));
997                         isc_stdtime_get(&task->now);
998                         do {
999                                 if (!EMPTY(task->events)) {
1000                                         event = HEAD(task->events);
1001                                         DEQUEUE(task->events, event, ev_link);
1002
1003                                         /*
1004                                          * Execute the event action.
1005                                          */
1006                                         XTRACE(isc_msgcat_get(isc_msgcat,
1007                                                             ISC_MSGSET_TASK,
1008                                                             ISC_MSG_EXECUTE,
1009                                                             "execute action"));
1010                                         if (event->ev_action != NULL) {
1011                                                 UNLOCK(&task->lock);
1012                                                 (event->ev_action)(
1013                                                         (isc_task_t *)task,
1014                                                         event);
1015                                                 LOCK(&task->lock);
1016                                         }
1017                                         dispatch_count++;
1018 #ifndef USE_WORKER_THREADS
1019                                         total_dispatch_count++;
1020 #endif /* USE_WORKER_THREADS */
1021                                 }
1022
1023                                 if (task->references == 0 &&
1024                                     EMPTY(task->events) &&
1025                                     !TASK_SHUTTINGDOWN(task)) {
1026                                         isc_boolean_t was_idle;
1027
1028                                         /*
1029                                          * There are no references and no
1030                                          * pending events for this task,
1031                                          * which means it will not become
1032                                          * runnable again via an external
1033                                          * action (such as sending an event
1034                                          * or detaching).
1035                                          *
1036                                          * We initiate shutdown to prevent
1037                                          * it from becoming a zombie.
1038                                          *
1039                                          * We do this here instead of in
1040                                          * the "if EMPTY(task->events)" block
1041                                          * below because:
1042                                          *
1043                                          *      If we post no shutdown events,
1044                                          *      we want the task to finish.
1045                                          *
1046                                          *      If we did post shutdown events,
1047                                          *      will still want the task's
1048                                          *      quantum to be applied.
1049                                          */
1050                                         was_idle = task_shutdown(task);
1051                                         INSIST(!was_idle);
1052                                 }
1053
1054                                 if (EMPTY(task->events)) {
1055                                         /*
1056                                          * Nothing else to do for this task
1057                                          * right now.
1058                                          */
1059                                         XTRACE(isc_msgcat_get(isc_msgcat,
1060                                                               ISC_MSGSET_TASK,
1061                                                               ISC_MSG_EMPTY,
1062                                                               "empty"));
1063                                         if (task->references == 0 &&
1064                                             TASK_SHUTTINGDOWN(task)) {
1065                                                 /*
1066                                                  * The task is done.
1067                                                  */
1068                                                 XTRACE(isc_msgcat_get(
1069                                                                isc_msgcat,
1070                                                                ISC_MSGSET_TASK,
1071                                                                ISC_MSG_DONE,
1072                                                                "done"));
1073                                                 finished = ISC_TRUE;
1074                                                 task->state = task_state_done;
1075                                         } else
1076                                                 task->state = task_state_idle;
1077                                         done = ISC_TRUE;
1078                                 } else if (dispatch_count >= task->quantum) {
1079                                         /*
1080                                          * Our quantum has expired, but
1081                                          * there is more work to be done.
1082                                          * We'll requeue it to the ready
1083                                          * queue later.
1084                                          *
1085                                          * We don't check quantum until
1086                                          * dispatching at least one event,
1087                                          * so the minimum quantum is one.
1088                                          */
1089                                         XTRACE(isc_msgcat_get(isc_msgcat,
1090                                                               ISC_MSGSET_TASK,
1091                                                               ISC_MSG_QUANTUM,
1092                                                               "quantum"));
1093                                         task->state = task_state_ready;
1094                                         requeue = ISC_TRUE;
1095                                         done = ISC_TRUE;
1096                                 }
1097                         } while (!done);
1098                         UNLOCK(&task->lock);
1099
1100                         if (finished)
1101                                 task_finished(task);
1102
1103                         LOCK(&manager->lock);
1104                         manager->tasks_running--;
1105 #ifdef USE_WORKER_THREADS
1106                         if (manager->exclusive_requested &&
1107                             manager->tasks_running == 1) {
1108                                 SIGNAL(&manager->exclusive_granted);
1109                         }
1110 #endif /* USE_WORKER_THREADS */
1111                         if (requeue) {
1112                                 /*
1113                                  * We know we're awake, so we don't have
1114                                  * to wakeup any sleeping threads if the
1115                                  * ready queue is empty before we requeue.
1116                                  *
1117                                  * A possible optimization if the queue is
1118                                  * empty is to 'goto' the 'if (task != NULL)'
1119                                  * block, avoiding the ENQUEUE of the task
1120                                  * and the subsequent immediate DEQUEUE
1121                                  * (since it is the only executable task).
1122                                  * We don't do this because then we'd be
1123                                  * skipping the exit_requested check.  The
1124                                  * cost of ENQUEUE is low anyway, especially
1125                                  * when you consider that we'd have to do
1126                                  * an extra EMPTY check to see if we could
1127                                  * do the optimization.  If the ready queue
1128                                  * were usually nonempty, the 'optimization'
1129                                  * might even hurt rather than help.
1130                                  */
1131 #ifdef USE_WORKER_THREADS
1132                                 ENQUEUE(manager->ready_tasks, task,
1133                                         ready_link);
1134 #else
1135                                 ENQUEUE(ready_tasks, task, ready_link);
1136 #endif
1137                         }
1138                 }
1139         }
1140 #ifndef USE_WORKER_THREADS
1141         ISC_LIST_APPENDLIST(manager->ready_tasks, ready_tasks, ready_link);
1142 #endif
1143         UNLOCK(&manager->lock);
1144 }
1145
1146 #ifdef USE_WORKER_THREADS
1147 static isc_threadresult_t
1148 #ifdef _WIN32
1149 WINAPI
1150 #endif
1151 run(void *uap) {
1152         isc__taskmgr_t *manager = uap;
1153
1154         XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
1155                                     ISC_MSG_STARTING, "starting"));
1156
1157         dispatch(manager);
1158
1159         XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
1160                                     ISC_MSG_EXITING, "exiting"));
1161
1162 #ifdef OPENSSL_LEAKS
1163         ERR_remove_state(0);
1164 #endif
1165
1166         return ((isc_threadresult_t)0);
1167 }
1168 #endif /* USE_WORKER_THREADS */
1169
1170 static void
1171 manager_free(isc__taskmgr_t *manager) {
1172         isc_mem_t *mctx;
1173
1174 #ifdef USE_WORKER_THREADS
1175         (void)isc_condition_destroy(&manager->exclusive_granted);
1176         (void)isc_condition_destroy(&manager->work_available);
1177         isc_mem_free(manager->mctx, manager->threads);
1178 #endif /* USE_WORKER_THREADS */
1179         DESTROYLOCK(&manager->lock);
1180         manager->common.impmagic = 0;
1181         manager->common.magic = 0;
1182         mctx = manager->mctx;
1183         isc_mem_put(mctx, manager, sizeof(*manager));
1184         isc_mem_detach(&mctx);
1185
1186 #ifdef USE_SHARED_MANAGER
1187         taskmgr = NULL;
1188 #endif  /* USE_SHARED_MANAGER */
1189 }
1190
1191 ISC_TASKFUNC_SCOPE isc_result_t
1192 isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers,
1193                     unsigned int default_quantum, isc_taskmgr_t **managerp)
1194 {
1195         isc_result_t result;
1196         unsigned int i, started = 0;
1197         isc__taskmgr_t *manager;
1198
1199         /*
1200          * Create a new task manager.
1201          */
1202
1203         REQUIRE(workers > 0);
1204         REQUIRE(managerp != NULL && *managerp == NULL);
1205
1206 #ifndef USE_WORKER_THREADS
1207         UNUSED(i);
1208         UNUSED(started);
1209 #endif
1210
1211 #ifdef USE_SHARED_MANAGER
1212         if (taskmgr != NULL) {
1213                 if (taskmgr->refs == 0)
1214                         return (ISC_R_SHUTTINGDOWN);
1215                 taskmgr->refs++;
1216                 *managerp = (isc_taskmgr_t *)taskmgr;
1217                 return (ISC_R_SUCCESS);
1218         }
1219 #endif /* USE_SHARED_MANAGER */
1220
1221         manager = isc_mem_get(mctx, sizeof(*manager));
1222         if (manager == NULL)
1223                 return (ISC_R_NOMEMORY);
1224         manager->common.methods = &taskmgrmethods;
1225         manager->common.impmagic = TASK_MANAGER_MAGIC;
1226         manager->common.magic = ISCAPI_TASKMGR_MAGIC;
1227         manager->mctx = NULL;
1228         result = isc_mutex_init(&manager->lock);
1229         if (result != ISC_R_SUCCESS)
1230                 goto cleanup_mgr;
1231
1232 #ifdef USE_WORKER_THREADS
1233         manager->workers = 0;
1234         manager->threads = isc_mem_allocate(mctx,
1235                                             workers * sizeof(isc_thread_t));
1236         if (manager->threads == NULL) {
1237                 result = ISC_R_NOMEMORY;
1238                 goto cleanup_lock;
1239         }
1240         if (isc_condition_init(&manager->work_available) != ISC_R_SUCCESS) {
1241                 UNEXPECTED_ERROR(__FILE__, __LINE__,
1242                                  "isc_condition_init() %s",
1243                                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
1244                                                 ISC_MSG_FAILED, "failed"));
1245                 result = ISC_R_UNEXPECTED;
1246                 goto cleanup_threads;
1247         }
1248         if (isc_condition_init(&manager->exclusive_granted) != ISC_R_SUCCESS) {
1249                 UNEXPECTED_ERROR(__FILE__, __LINE__,
1250                                  "isc_condition_init() %s",
1251                                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
1252                                                 ISC_MSG_FAILED, "failed"));
1253                 result = ISC_R_UNEXPECTED;
1254                 goto cleanup_workavailable;
1255         }
1256 #endif /* USE_WORKER_THREADS */
1257         if (default_quantum == 0)
1258                 default_quantum = DEFAULT_DEFAULT_QUANTUM;
1259         manager->default_quantum = default_quantum;
1260         INIT_LIST(manager->tasks);
1261         INIT_LIST(manager->ready_tasks);
1262         manager->tasks_running = 0;
1263         manager->exclusive_requested = ISC_FALSE;
1264         manager->exiting = ISC_FALSE;
1265
1266         isc_mem_attach(mctx, &manager->mctx);
1267
1268 #ifdef USE_WORKER_THREADS
1269         LOCK(&manager->lock);
1270         /*
1271          * Start workers.
1272          */
1273         for (i = 0; i < workers; i++) {
1274                 if (isc_thread_create(run, manager,
1275                                       &manager->threads[manager->workers]) ==
1276                     ISC_R_SUCCESS) {
1277                         manager->workers++;
1278                         started++;
1279                 }
1280         }
1281         UNLOCK(&manager->lock);
1282
1283         if (started == 0) {
1284                 manager_free(manager);
1285                 return (ISC_R_NOTHREADS);
1286         }
1287         isc_thread_setconcurrency(workers);
1288 #endif /* USE_WORKER_THREADS */
1289 #ifdef USE_SHARED_MANAGER
1290         manager->refs = 1;
1291         taskmgr = manager;
1292 #endif /* USE_SHARED_MANAGER */
1293
1294         *managerp = (isc_taskmgr_t *)manager;
1295
1296         return (ISC_R_SUCCESS);
1297
1298 #ifdef USE_WORKER_THREADS
1299  cleanup_workavailable:
1300         (void)isc_condition_destroy(&manager->work_available);
1301  cleanup_threads:
1302         isc_mem_free(mctx, manager->threads);
1303  cleanup_lock:
1304         DESTROYLOCK(&manager->lock);
1305 #endif
1306  cleanup_mgr:
1307         isc_mem_put(mctx, manager, sizeof(*manager));
1308         return (result);
1309 }
1310
1311 ISC_TASKFUNC_SCOPE void
1312 isc__taskmgr_destroy(isc_taskmgr_t **managerp) {
1313         isc__taskmgr_t *manager;
1314         isc__task_t *task;
1315         unsigned int i;
1316
1317         /*
1318          * Destroy '*managerp'.
1319          */
1320
1321         REQUIRE(managerp != NULL);
1322         manager = (isc__taskmgr_t *)*managerp;
1323         REQUIRE(VALID_MANAGER(manager));
1324
1325 #ifndef USE_WORKER_THREADS
1326         UNUSED(i);
1327 #endif /* USE_WORKER_THREADS */
1328
1329 #ifdef USE_SHARED_MANAGER
1330         manager->refs--;
1331         if (manager->refs > 0) {
1332                 *managerp = NULL;
1333                 return;
1334         }
1335 #endif
1336
1337         XTHREADTRACE("isc_taskmgr_destroy");
1338         /*
1339          * Only one non-worker thread may ever call this routine.
1340          * If a worker thread wants to initiate shutdown of the
1341          * task manager, it should ask some non-worker thread to call
1342          * isc_taskmgr_destroy(), e.g. by signalling a condition variable
1343          * that the startup thread is sleeping on.
1344          */
1345
1346         /*
1347          * Unlike elsewhere, we're going to hold this lock a long time.
1348          * We need to do so, because otherwise the list of tasks could
1349          * change while we were traversing it.
1350          *
1351          * This is also the only function where we will hold both the
1352          * task manager lock and a task lock at the same time.
1353          */
1354
1355         LOCK(&manager->lock);
1356
1357         /*
1358          * Make sure we only get called once.
1359          */
1360         INSIST(!manager->exiting);
1361         manager->exiting = ISC_TRUE;
1362
1363         /*
1364          * Post shutdown event(s) to every task (if they haven't already been
1365          * posted).
1366          */
1367         for (task = HEAD(manager->tasks);
1368              task != NULL;
1369              task = NEXT(task, link)) {
1370                 LOCK(&task->lock);
1371                 if (task_shutdown(task))
1372                         ENQUEUE(manager->ready_tasks, task, ready_link);
1373                 UNLOCK(&task->lock);
1374         }
1375 #ifdef USE_WORKER_THREADS
1376         /*
1377          * Wake up any sleeping workers.  This ensures we get work done if
1378          * there's work left to do, and if there are already no tasks left
1379          * it will cause the workers to see manager->exiting.
1380          */
1381         BROADCAST(&manager->work_available);
1382         UNLOCK(&manager->lock);
1383
1384         /*
1385          * Wait for all the worker threads to exit.
1386          */
1387         for (i = 0; i < manager->workers; i++)
1388                 (void)isc_thread_join(manager->threads[i], NULL);
1389 #else /* USE_WORKER_THREADS */
1390         /*
1391          * Dispatch the shutdown events.
1392          */
1393         UNLOCK(&manager->lock);
1394         while (isc__taskmgr_ready((isc_taskmgr_t *)manager))
1395                 (void)isc__taskmgr_dispatch((isc_taskmgr_t *)manager);
1396 #ifdef BIND9
1397         if (!ISC_LIST_EMPTY(manager->tasks))
1398                 isc_mem_printallactive(stderr);
1399 #endif
1400         INSIST(ISC_LIST_EMPTY(manager->tasks));
1401 #ifdef USE_SHARED_MANAGER
1402         taskmgr = NULL;
1403 #endif
1404 #endif /* USE_WORKER_THREADS */
1405
1406         manager_free(manager);
1407
1408         *managerp = NULL;
1409 }
1410
1411 #ifndef USE_WORKER_THREADS
1412 isc_boolean_t
1413 isc__taskmgr_ready(isc_taskmgr_t *manager0) {
1414         isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0;
1415
1416 #ifdef USE_SHARED_MANAGER
1417         if (manager == NULL)
1418                 manager = taskmgr;
1419 #endif
1420         if (manager == NULL)
1421                 return (ISC_FALSE);
1422         return (ISC_TF(!ISC_LIST_EMPTY(manager->ready_tasks)));
1423 }
1424
1425 isc_result_t
1426 isc__taskmgr_dispatch(isc_taskmgr_t *manager0) {
1427         isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0;
1428
1429 #ifdef USE_SHARED_MANAGER
1430         if (manager == NULL)
1431                 manager = taskmgr;
1432 #endif
1433         if (manager == NULL)
1434                 return (ISC_R_NOTFOUND);
1435
1436         dispatch(manager);
1437
1438         return (ISC_R_SUCCESS);
1439 }
1440
1441 #endif /* USE_WORKER_THREADS */
1442
1443 ISC_TASKFUNC_SCOPE isc_result_t
1444 isc__task_beginexclusive(isc_task_t *task0) {
1445 #ifdef USE_WORKER_THREADS
1446         isc__task_t *task = (isc__task_t *)task0;
1447         isc__taskmgr_t *manager = task->manager;
1448         REQUIRE(task->state == task_state_running);
1449         LOCK(&manager->lock);
1450         if (manager->exclusive_requested) {
1451                 UNLOCK(&manager->lock);
1452                 return (ISC_R_LOCKBUSY);
1453         }
1454         manager->exclusive_requested = ISC_TRUE;
1455         while (manager->tasks_running > 1) {
1456                 WAIT(&manager->exclusive_granted, &manager->lock);
1457         }
1458         UNLOCK(&manager->lock);
1459 #else
1460         UNUSED(task0);
1461 #endif
1462         return (ISC_R_SUCCESS);
1463 }
1464
1465 ISC_TASKFUNC_SCOPE void
1466 isc__task_endexclusive(isc_task_t *task0) {
1467 #ifdef USE_WORKER_THREADS
1468         isc__task_t *task = (isc__task_t *)task0;
1469         isc__taskmgr_t *manager = task->manager;
1470
1471         REQUIRE(task->state == task_state_running);
1472         LOCK(&manager->lock);
1473         REQUIRE(manager->exclusive_requested);
1474         manager->exclusive_requested = ISC_FALSE;
1475         BROADCAST(&manager->work_available);
1476         UNLOCK(&manager->lock);
1477 #else
1478         UNUSED(task0);
1479 #endif
1480 }
1481
1482 #ifdef USE_SOCKETIMPREGISTER
1483 isc_result_t
1484 isc__task_register() {
1485         return (isc_task_register(isc__taskmgr_create));
1486 }
1487 #endif
1488
1489 isc_boolean_t
1490 isc_task_exiting(isc_task_t *t) {
1491         isc__task_t *task = (isc__task_t *)t;
1492
1493         REQUIRE(VALID_TASK(task));
1494         return (TASK_SHUTTINGDOWN(task));
1495 }
1496
1497
1498 #if defined(HAVE_LIBXML2) && defined(BIND9)
1499 void
1500 isc_taskmgr_renderxml(isc_taskmgr_t *mgr0, xmlTextWriterPtr writer) {
1501         isc__taskmgr_t *mgr = (isc__taskmgr_t *)mgr0;
1502         isc__task_t *task;
1503
1504         LOCK(&mgr->lock);
1505
1506         /*
1507          * Write out the thread-model, and some details about each depending
1508          * on which type is enabled.
1509          */
1510         xmlTextWriterStartElement(writer, ISC_XMLCHAR "thread-model");
1511 #ifdef ISC_PLATFORM_USETHREADS
1512         xmlTextWriterStartElement(writer, ISC_XMLCHAR "type");
1513         xmlTextWriterWriteString(writer, ISC_XMLCHAR "threaded");
1514         xmlTextWriterEndElement(writer); /* type */
1515
1516         xmlTextWriterStartElement(writer, ISC_XMLCHAR "worker-threads");
1517         xmlTextWriterWriteFormatString(writer, "%d", mgr->workers);
1518         xmlTextWriterEndElement(writer); /* worker-threads */
1519 #else /* ISC_PLATFORM_USETHREADS */
1520         xmlTextWriterStartElement(writer, ISC_XMLCHAR "type");
1521         xmlTextWriterWriteString(writer, ISC_XMLCHAR "non-threaded");
1522         xmlTextWriterEndElement(writer); /* type */
1523
1524         xmlTextWriterStartElement(writer, ISC_XMLCHAR "references");
1525         xmlTextWriterWriteFormatString(writer, "%d", mgr->refs);
1526         xmlTextWriterEndElement(writer); /* references */
1527 #endif /* ISC_PLATFORM_USETHREADS */
1528
1529         xmlTextWriterStartElement(writer, ISC_XMLCHAR "default-quantum");
1530         xmlTextWriterWriteFormatString(writer, "%d", mgr->default_quantum);
1531         xmlTextWriterEndElement(writer); /* default-quantum */
1532
1533         xmlTextWriterStartElement(writer, ISC_XMLCHAR "tasks-running");
1534         xmlTextWriterWriteFormatString(writer, "%d", mgr->tasks_running);
1535         xmlTextWriterEndElement(writer); /* tasks-running */
1536
1537         xmlTextWriterEndElement(writer); /* thread-model */
1538
1539         xmlTextWriterStartElement(writer, ISC_XMLCHAR "tasks");
1540         task = ISC_LIST_HEAD(mgr->tasks);
1541         while (task != NULL) {
1542                 LOCK(&task->lock);
1543                 xmlTextWriterStartElement(writer, ISC_XMLCHAR "task");
1544
1545                 if (task->name[0] != 0) {
1546                         xmlTextWriterStartElement(writer, ISC_XMLCHAR "name");
1547                         xmlTextWriterWriteFormatString(writer, "%s",
1548                                                        task->name);
1549                         xmlTextWriterEndElement(writer); /* name */
1550                 }
1551
1552                 xmlTextWriterStartElement(writer, ISC_XMLCHAR "references");
1553                 xmlTextWriterWriteFormatString(writer, "%d", task->references);
1554                 xmlTextWriterEndElement(writer); /* references */
1555
1556                 xmlTextWriterStartElement(writer, ISC_XMLCHAR "id");
1557                 xmlTextWriterWriteFormatString(writer, "%p", task);
1558                 xmlTextWriterEndElement(writer); /* id */
1559
1560                 xmlTextWriterStartElement(writer, ISC_XMLCHAR "state");
1561                 xmlTextWriterWriteFormatString(writer, "%s",
1562                                                statenames[task->state]);
1563                 xmlTextWriterEndElement(writer); /* state */
1564
1565                 xmlTextWriterStartElement(writer, ISC_XMLCHAR "quantum");
1566                 xmlTextWriterWriteFormatString(writer, "%d", task->quantum);
1567                 xmlTextWriterEndElement(writer); /* quantum */
1568
1569                 xmlTextWriterEndElement(writer);
1570
1571                 UNLOCK(&task->lock);
1572                 task = ISC_LIST_NEXT(task, link);
1573         }
1574         xmlTextWriterEndElement(writer); /* tasks */
1575
1576         UNLOCK(&mgr->lock);
1577 }
1578 #endif /* HAVE_LIBXML2 && BIND9 */