Merge from vendor branch READLINE:
[dragonfly.git] / contrib / bind-9.2.4rc7 / lib / isc / task.c
1 /*
2  * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1998-2002  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and 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.85.2.5 2004/03/09 06:11:52 marka Exp $ */
19
20 /*
21  * 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
42 #ifndef ISC_PLATFORM_USETHREADS
43 #include "task_p.h"
44 #endif /* ISC_PLATFORM_USETHREADS */
45
46 #define ISC_TASK_NAMES 1
47
48 #ifdef ISC_TASK_TRACE
49 #define XTRACE(m)               fprintf(stderr, "task %p thread %lu: %s\n", \
50                                        task, isc_thread_self(), (m))
51 #define XTTRACE(t, m)           fprintf(stderr, "task %p thread %lu: %s\n", \
52                                        (t), isc_thread_self(), (m))
53 #define XTHREADTRACE(m)         fprintf(stderr, "thread %lu: %s\n", \
54                                        isc_thread_self(), (m))
55 #else
56 #define XTRACE(m)
57 #define XTTRACE(t, m)
58 #define XTHREADTRACE(m)
59 #endif
60
61 /***
62  *** Types.
63  ***/
64
65 typedef enum {
66         task_state_idle, task_state_ready, task_state_running,
67         task_state_done
68 } task_state_t;
69
70 #define TASK_MAGIC                      ISC_MAGIC('T', 'A', 'S', 'K')
71 #define VALID_TASK(t)                   ISC_MAGIC_VALID(t, TASK_MAGIC)
72
73 struct isc_task {
74         /* Not locked. */
75         unsigned int                    magic;
76         isc_taskmgr_t *                 manager;
77         isc_mutex_t                     lock;
78         /* Locked by task lock. */
79         task_state_t                    state;
80         unsigned int                    references;
81         isc_eventlist_t                 events;
82         isc_eventlist_t                 on_shutdown;
83         unsigned int                    quantum;
84         unsigned int                    flags;
85 #ifdef ISC_TASK_NAMES
86         char                            name[16];
87         void *                          tag;
88 #endif
89         /* Locked by task manager lock. */
90         LINK(isc_task_t)                link;
91         LINK(isc_task_t)                ready_link;
92 };
93
94 #define TASK_F_SHUTTINGDOWN             0x01
95
96 #define TASK_SHUTTINGDOWN(t)            (((t)->flags & TASK_F_SHUTTINGDOWN) \
97                                          != 0)
98
99 #define TASK_MANAGER_MAGIC              ISC_MAGIC('T', 'S', 'K', 'M')
100 #define VALID_MANAGER(m)                ISC_MAGIC_VALID(m, TASK_MANAGER_MAGIC)
101
102 struct isc_taskmgr {
103         /* Not locked. */
104         unsigned int                    magic;
105         isc_mem_t *                     mctx;
106         isc_mutex_t                     lock;
107         unsigned int                    workers;
108 #ifdef ISC_PLATFORM_USETHREADS
109         isc_thread_t *                  threads;
110 #endif /* ISC_PLATFORM_USETHREADS */
111         /* Locked by task manager lock. */
112         unsigned int                    default_quantum;
113         LIST(isc_task_t)                tasks;
114         isc_tasklist_t                  ready_tasks;
115 #ifdef ISC_PLATFORM_USETHREADS
116         isc_condition_t                 work_available;
117         isc_condition_t                 exclusive_granted;
118 #endif /* ISC_PLATFORM_USETHREADS */
119         unsigned int                    tasks_running;
120         isc_boolean_t                   exclusive_requested;
121         isc_boolean_t                   exiting;
122 #ifndef ISC_PLATFORM_USETHREADS
123         unsigned int                    refs;
124 #endif /* ISC_PLATFORM_USETHREADS */
125 };
126
127 #define DEFAULT_TASKMGR_QUANTUM         10
128 #define DEFAULT_DEFAULT_QUANTUM         5
129 #define FINISHED(m)                     ((m)->exiting && EMPTY((m)->tasks))
130
131 #ifndef ISC_PLATFORM_USETHREADS
132 static isc_taskmgr_t *taskmgr = NULL;
133 #endif /* ISC_PLATFORM_USETHREADS */
134
135 /***
136  *** Tasks.
137  ***/
138
139 static void
140 task_finished(isc_task_t *task) {
141         isc_taskmgr_t *manager = task->manager;
142
143         REQUIRE(EMPTY(task->events));
144         REQUIRE(EMPTY(task->on_shutdown));
145         REQUIRE(task->references == 0);
146         REQUIRE(task->state == task_state_done);
147
148         XTRACE("task_finished");
149
150         LOCK(&manager->lock);
151         UNLINK(manager->tasks, task, link);
152 #ifdef ISC_PLATFORM_USETHREADS
153         if (FINISHED(manager)) {
154                 /*
155                  * All tasks have completed and the
156                  * task manager is exiting.  Wake up
157                  * any idle worker threads so they
158                  * can exit.
159                  */
160                 BROADCAST(&manager->work_available);
161         }
162 #endif /* ISC_PLATFORM_USETHREADS */
163         UNLOCK(&manager->lock);
164
165         DESTROYLOCK(&task->lock);
166         task->magic = 0;
167         isc_mem_put(manager->mctx, task, sizeof *task);
168 }
169
170 isc_result_t
171 isc_task_create(isc_taskmgr_t *manager, unsigned int quantum,
172                 isc_task_t **taskp)
173 {
174         isc_task_t *task;
175         isc_boolean_t exiting;
176
177         REQUIRE(VALID_MANAGER(manager));
178         REQUIRE(taskp != NULL && *taskp == NULL);
179
180         task = isc_mem_get(manager->mctx, sizeof *task);
181         if (task == NULL)
182                 return (ISC_R_NOMEMORY);
183         XTRACE("isc_task_create");
184         task->manager = manager;
185         if (isc_mutex_init(&task->lock) != ISC_R_SUCCESS) {
186                 isc_mem_put(manager->mctx, task, sizeof *task);
187                 UNEXPECTED_ERROR(__FILE__, __LINE__,
188                                  "isc_mutex_init() %s",
189                                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
190                                                 ISC_MSG_FAILED, "failed"));
191                 return (ISC_R_UNEXPECTED);
192         }
193         task->state = task_state_idle;
194         task->references = 1;
195         INIT_LIST(task->events);
196         INIT_LIST(task->on_shutdown);
197         task->quantum = quantum;
198         task->flags = 0;
199 #ifdef ISC_TASK_NAMES
200         memset(task->name, 0, sizeof task->name);
201         task->tag = NULL;
202 #endif
203         INIT_LINK(task, link);
204         INIT_LINK(task, ready_link);
205
206         exiting = ISC_FALSE;
207         LOCK(&manager->lock);
208         if (!manager->exiting) {
209                 if (task->quantum == 0)
210                         task->quantum = manager->default_quantum;
211                 APPEND(manager->tasks, task, link);
212         } else
213                 exiting = ISC_TRUE;
214         UNLOCK(&manager->lock);
215
216         if (exiting) {
217                 DESTROYLOCK(&task->lock);
218                 isc_mem_put(manager->mctx, task, sizeof *task);
219                 return (ISC_R_SHUTTINGDOWN);
220         }
221
222         task->magic = TASK_MAGIC;
223         *taskp = task;
224
225         return (ISC_R_SUCCESS);
226 }
227
228 void
229 isc_task_attach(isc_task_t *source, isc_task_t **targetp) {
230
231         /*
232          * Attach *targetp to source.
233          */
234
235         REQUIRE(VALID_TASK(source));
236         REQUIRE(targetp != NULL && *targetp == NULL);
237
238         XTTRACE(source, "isc_task_attach");
239
240         LOCK(&source->lock);
241         source->references++;
242         UNLOCK(&source->lock);
243
244         *targetp = source;
245 }
246
247 static inline isc_boolean_t
248 task_shutdown(isc_task_t *task) {
249         isc_boolean_t was_idle = ISC_FALSE;
250         isc_event_t *event, *prev;
251
252         /*
253          * Caller must be holding the task's lock.
254          */
255
256         XTRACE("task_shutdown");
257
258         if (! TASK_SHUTTINGDOWN(task)) {
259                 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
260                                       ISC_MSG_SHUTTINGDOWN, "shutting down"));
261                 task->flags |= TASK_F_SHUTTINGDOWN;
262                 if (task->state == task_state_idle) {
263                         INSIST(EMPTY(task->events));
264                         task->state = task_state_ready;
265                         was_idle = ISC_TRUE;
266                 }
267                 INSIST(task->state == task_state_ready ||
268                        task->state == task_state_running);
269                 /*
270                  * Note that we post shutdown events LIFO.
271                  */
272                 for (event = TAIL(task->on_shutdown);
273                      event != NULL;
274                      event = prev) {
275                         prev = PREV(event, ev_link);
276                         DEQUEUE(task->on_shutdown, event, ev_link);
277                         ENQUEUE(task->events, event, ev_link);
278                 }
279         }
280
281         return (was_idle);
282 }
283
284 static inline void
285 task_ready(isc_task_t *task) {
286         isc_taskmgr_t *manager = task->manager;
287
288         REQUIRE(VALID_MANAGER(manager));
289         REQUIRE(task->state == task_state_ready);
290
291         XTRACE("task_ready");
292
293         LOCK(&manager->lock);
294
295         ENQUEUE(manager->ready_tasks, task, ready_link);
296 #ifdef ISC_PLATFORM_USETHREADS
297         SIGNAL(&manager->work_available);
298 #endif /* ISC_PLATFORM_USETHREADS */
299
300         UNLOCK(&manager->lock);
301 }
302
303 static inline isc_boolean_t
304 task_detach(isc_task_t *task) {
305
306         /*
307          * Caller must be holding the task lock.
308          */
309
310         REQUIRE(task->references > 0);
311
312         XTRACE("detach");
313
314         task->references--;
315         if (task->references == 0 && task->state == task_state_idle) {
316                 INSIST(EMPTY(task->events));
317                 /*
318                  * There are no references to this task, and no
319                  * pending events.  We could try to optimize and
320                  * either initiate shutdown or clean up the task,
321                  * depending on its state, but it's easier to just
322                  * make the task ready and allow run() or the event
323                  * loop to deal with shutting down and termination.
324                  */
325                 task->state = task_state_ready;
326                 return (ISC_TRUE);
327         }
328
329         return (ISC_FALSE);
330 }
331
332 void
333 isc_task_detach(isc_task_t **taskp) {
334         isc_task_t *task;
335         isc_boolean_t was_idle;
336
337         /*
338          * Detach *taskp from its task.
339          */
340
341         REQUIRE(taskp != NULL);
342         task = *taskp;
343         REQUIRE(VALID_TASK(task));
344
345         XTRACE("isc_task_detach");
346
347         LOCK(&task->lock);
348         was_idle = task_detach(task);
349         UNLOCK(&task->lock);
350
351         if (was_idle)
352                 task_ready(task);
353
354         *taskp = NULL;
355 }
356
357 static inline isc_boolean_t
358 task_send(isc_task_t *task, isc_event_t **eventp) {
359         isc_boolean_t was_idle = ISC_FALSE;
360         isc_event_t *event;
361
362         /*
363          * Caller must be holding the task lock.
364          */
365
366         REQUIRE(eventp != NULL);
367         event = *eventp;
368         REQUIRE(event != NULL);
369         REQUIRE(event->ev_type > 0);
370         REQUIRE(task->state != task_state_done);
371
372         XTRACE("task_send");
373
374         if (task->state == task_state_idle) {
375                 was_idle = ISC_TRUE;
376                 INSIST(EMPTY(task->events));
377                 task->state = task_state_ready;
378         }
379         INSIST(task->state == task_state_ready ||
380                task->state == task_state_running);
381         ENQUEUE(task->events, event, ev_link);
382         *eventp = NULL;
383
384         return (was_idle);
385 }
386
387 void
388 isc_task_send(isc_task_t *task, isc_event_t **eventp) {
389         isc_boolean_t was_idle;
390
391         /*
392          * Send '*event' to 'task'.
393          */
394
395         REQUIRE(VALID_TASK(task));
396
397         XTRACE("isc_task_send");
398
399         /*
400          * We're trying hard to hold locks for as short a time as possible.
401          * We're also trying to hold as few locks as possible.  This is why
402          * some processing is deferred until after the lock is released.
403          */
404         LOCK(&task->lock);
405         was_idle = task_send(task, eventp);
406         UNLOCK(&task->lock);
407
408         if (was_idle) {
409                 /*
410                  * We need to add this task to the ready queue.
411                  *
412                  * We've waited until now to do it because making a task
413                  * ready requires locking the manager.  If we tried to do
414                  * this while holding the task lock, we could deadlock.
415                  *
416                  * We've changed the state to ready, so no one else will
417                  * be trying to add this task to the ready queue.  The
418                  * only way to leave the ready state is by executing the
419                  * task.  It thus doesn't matter if events are added,
420                  * removed, or a shutdown is started in the interval
421                  * between the time we released the task lock, and the time
422                  * we add the task to the ready queue.
423                  */
424                 task_ready(task);
425         }
426 }
427
428 void
429 isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
430         isc_boolean_t idle1, idle2;
431         isc_task_t *task;
432
433         /*
434          * Send '*event' to '*taskp' and then detach '*taskp' from its
435          * task.
436          */
437
438         REQUIRE(taskp != NULL);
439         task = *taskp;
440         REQUIRE(VALID_TASK(task));
441
442         XTRACE("isc_task_sendanddetach");
443
444         LOCK(&task->lock);
445         idle1 = task_send(task, eventp);
446         idle2 = task_detach(task);
447         UNLOCK(&task->lock);
448
449         /*
450          * If idle1, then idle2 shouldn't be true as well since we're holding
451          * the task lock, and thus the task cannot switch from ready back to
452          * idle.
453          */
454         INSIST(!(idle1 && idle2));
455
456         if (idle1 || idle2)
457                 task_ready(task);
458
459         *taskp = NULL;
460 }
461
462 #define PURGE_OK(event) (((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0)
463
464 static unsigned int
465 dequeue_events(isc_task_t *task, void *sender, isc_eventtype_t first,
466                isc_eventtype_t last, void *tag,
467                isc_eventlist_t *events, isc_boolean_t purging)
468 {
469         isc_event_t *event, *next_event;
470         unsigned int count = 0;
471
472         REQUIRE(VALID_TASK(task));
473         REQUIRE(last >= first);
474
475         XTRACE("dequeue_events");
476
477         /*
478          * Events matching 'sender', whose type is >= first and <= last, and
479          * whose tag is 'tag' will be dequeued.  If 'purging', matching events
480          * which are marked as unpurgable will not be dequeued.
481          *
482          * sender == NULL means "any sender", and tag == NULL means "any tag".
483          */
484
485         LOCK(&task->lock);
486
487         for (event = HEAD(task->events); event != NULL; event = next_event) {
488                 next_event = NEXT(event, ev_link);
489                 if (event->ev_type >= first && event->ev_type <= last &&
490                     (sender == NULL || event->ev_sender == sender) &&
491                     (tag == NULL || event->ev_tag == tag) &&
492                     (!purging || PURGE_OK(event))) {
493                         DEQUEUE(task->events, event, ev_link);
494                         ENQUEUE(*events, event, ev_link);
495                         count++;
496                 }
497         }
498
499         UNLOCK(&task->lock);
500
501         return (count);
502 }
503
504 unsigned int
505 isc_task_purgerange(isc_task_t *task, void *sender, isc_eventtype_t first,
506                     isc_eventtype_t last, void *tag)
507 {
508         unsigned int count;
509         isc_eventlist_t events;
510         isc_event_t *event, *next_event;
511
512         /*
513          * Purge events from a task's event queue.
514          */
515
516         XTRACE("isc_task_purgerange");
517
518         ISC_LIST_INIT(events);
519
520         count = dequeue_events(task, sender, first, last, tag, &events,
521                                ISC_TRUE);
522
523         for (event = HEAD(events); event != NULL; event = next_event) {
524                 next_event = NEXT(event, ev_link);
525                 isc_event_free(&event);
526         }
527
528         /*
529          * Note that purging never changes the state of the task.
530          */
531
532         return (count);
533 }
534
535 unsigned int
536 isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
537                void *tag)
538 {
539         /*
540          * Purge events from a task's event queue.
541          */
542
543         XTRACE("isc_task_purge");
544
545         return (isc_task_purgerange(task, sender, type, type, tag));
546 }
547
548 isc_boolean_t
549 isc_task_purgeevent(isc_task_t *task, isc_event_t *event) {
550         isc_event_t *curr_event, *next_event;
551
552         /*
553          * Purge 'event' from a task's event queue.
554          *
555          * XXXRTH:  WARNING:  This method may be removed before beta.
556          */
557
558         REQUIRE(VALID_TASK(task));
559
560         /*
561          * If 'event' is on the task's event queue, it will be purged,
562          * unless it is marked as unpurgeable.  'event' does not have to be
563          * on the task's event queue; in fact, it can even be an invalid
564          * pointer.  Purging only occurs if the event is actually on the task's
565          * event queue.
566          *
567          * Purging never changes the state of the task.
568          */
569
570         LOCK(&task->lock);
571         for (curr_event = HEAD(task->events);
572              curr_event != NULL;
573              curr_event = next_event) {
574                 next_event = NEXT(curr_event, ev_link);
575                 if (curr_event == event && PURGE_OK(event)) {
576                         DEQUEUE(task->events, curr_event, ev_link);
577                         break;
578                 }
579         }
580         UNLOCK(&task->lock);
581
582         if (curr_event == NULL)
583                 return (ISC_FALSE);
584
585         isc_event_free(&curr_event);
586
587         return (ISC_TRUE);
588 }
589
590 unsigned int
591 isc_task_unsendrange(isc_task_t *task, void *sender, isc_eventtype_t first,
592                      isc_eventtype_t last, void *tag,
593                      isc_eventlist_t *events)
594 {
595         /*
596          * Remove events from a task's event queue.
597          */
598
599         XTRACE("isc_task_unsendrange");
600
601         return (dequeue_events(task, sender, first, last, tag, events,
602                                ISC_FALSE));
603 }
604
605 unsigned int
606 isc_task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type,
607                 void *tag, isc_eventlist_t *events)
608 {
609         /*
610          * Remove events from a task's event queue.
611          */
612
613         XTRACE("isc_task_unsend");
614
615         return (dequeue_events(task, sender, type, type, tag, events,
616                                ISC_FALSE));
617 }
618
619 isc_result_t
620 isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, const void *arg)
621 {
622         isc_boolean_t disallowed = ISC_FALSE;
623         isc_result_t result = ISC_R_SUCCESS;
624         isc_event_t *event;
625
626         /*
627          * Send a shutdown event with action 'action' and argument 'arg' when
628          * 'task' is shutdown.
629          */
630
631         REQUIRE(VALID_TASK(task));
632         REQUIRE(action != NULL);
633
634         event = isc_event_allocate(task->manager->mctx,
635                                    NULL,
636                                    ISC_TASKEVENT_SHUTDOWN,
637                                    action,
638                                    arg,
639                                    sizeof *event);
640         if (event == NULL)
641                 return (ISC_R_NOMEMORY);
642
643         LOCK(&task->lock);
644         if (TASK_SHUTTINGDOWN(task)) {
645                 disallowed = ISC_TRUE;
646                 result = ISC_R_SHUTTINGDOWN;
647         } else
648                 ENQUEUE(task->on_shutdown, event, ev_link);
649         UNLOCK(&task->lock);
650
651         if (disallowed)
652                 isc_mem_put(task->manager->mctx, event, sizeof *event);
653
654         return (result);
655 }
656
657 void
658 isc_task_shutdown(isc_task_t *task) {
659         isc_boolean_t was_idle;
660
661         /*
662          * Shutdown 'task'.
663          */
664
665         REQUIRE(VALID_TASK(task));
666
667         LOCK(&task->lock);
668         was_idle = task_shutdown(task);
669         UNLOCK(&task->lock);
670
671         if (was_idle)
672                 task_ready(task);
673 }
674
675 void
676 isc_task_destroy(isc_task_t **taskp) {
677
678         /*
679          * Destroy '*taskp'.
680          */
681
682         REQUIRE(taskp != NULL);
683
684         isc_task_shutdown(*taskp);
685         isc_task_detach(taskp);
686 }
687
688 void
689 isc_task_setname(isc_task_t *task, const char *name, void *tag) {
690
691         /*
692          * Name 'task'.
693          */
694
695         REQUIRE(VALID_TASK(task));
696
697 #ifdef ISC_TASK_NAMES
698         LOCK(&task->lock);
699         memset(task->name, 0, sizeof(task->name));
700         strncpy(task->name, name, sizeof(task->name) - 1);
701         task->tag = tag;
702         UNLOCK(&task->lock);
703 #else
704         UNUSED(name);
705         UNUSED(tag);
706 #endif
707
708 }
709
710 const char *
711 isc_task_getname(isc_task_t *task) {
712         return (task->name);
713 }
714
715 void *
716 isc_task_gettag(isc_task_t *task) {
717         return (task->tag);
718 }
719
720
721 /***
722  *** Task Manager.
723  ***/
724 static void
725 dispatch(isc_taskmgr_t *manager) {
726         isc_task_t *task;
727 #ifndef ISC_PLATFORM_USETHREADS
728         unsigned int total_dispatch_count = 0;
729         isc_tasklist_t ready_tasks;
730 #endif /* ISC_PLATFORM_USETHREADS */
731
732         REQUIRE(VALID_MANAGER(manager));
733
734         /*
735          * Again we're trying to hold the lock for as short a time as possible
736          * and to do as little locking and unlocking as possible.
737          *
738          * In both while loops, the appropriate lock must be held before the
739          * while body starts.  Code which acquired the lock at the top of
740          * the loop would be more readable, but would result in a lot of
741          * extra locking.  Compare:
742          *
743          * Straightforward:
744          *
745          *      LOCK();
746          *      ...
747          *      UNLOCK();
748          *      while (expression) {
749          *              LOCK();
750          *              ...
751          *              UNLOCK();
752          *
753          *              Unlocked part here...
754          *
755          *              LOCK();
756          *              ...
757          *              UNLOCK();
758          *      }
759          *
760          * Note how if the loop continues we unlock and then immediately lock.
761          * For N iterations of the loop, this code does 2N+1 locks and 2N+1
762          * unlocks.  Also note that the lock is not held when the while
763          * condition is tested, which may or may not be important, depending
764          * on the expression.
765          *
766          * As written:
767          *
768          *      LOCK();
769          *      while (expression) {
770          *              ...
771          *              UNLOCK();
772          *
773          *              Unlocked part here...
774          *
775          *              LOCK();
776          *              ...
777          *      }
778          *      UNLOCK();
779          *
780          * For N iterations of the loop, this code does N+1 locks and N+1
781          * unlocks.  The while expression is always protected by the lock.
782          */
783
784 #ifndef ISC_PLATFORM_USETHREADS
785         ISC_LIST_INIT(ready_tasks);
786 #endif
787         LOCK(&manager->lock);
788         while (!FINISHED(manager)) {
789 #ifdef ISC_PLATFORM_USETHREADS
790                 /*
791                  * For reasons similar to those given in the comment in
792                  * isc_task_send() above, it is safe for us to dequeue
793                  * the task while only holding the manager lock, and then
794                  * change the task to running state while only holding the
795                  * task lock.
796                  */
797                 while ((EMPTY(manager->ready_tasks) ||
798                         manager->exclusive_requested) &&
799                         !FINISHED(manager)) 
800                 {
801                         XTHREADTRACE(isc_msgcat_get(isc_msgcat,
802                                                     ISC_MSGSET_GENERAL,
803                                                     ISC_MSG_WAIT, "wait"));
804                         WAIT(&manager->work_available, &manager->lock);
805                         XTHREADTRACE(isc_msgcat_get(isc_msgcat,
806                                                     ISC_MSGSET_TASK,
807                                                     ISC_MSG_AWAKE, "awake"));
808                 }
809 #else /* ISC_PLATFORM_USETHREADS */
810                 if (total_dispatch_count >= DEFAULT_TASKMGR_QUANTUM ||
811                     EMPTY(manager->ready_tasks))
812                         break;
813 #endif /* ISC_PLATFORM_USETHREADS */
814                 XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK,
815                                             ISC_MSG_WORKING, "working"));
816
817                 task = HEAD(manager->ready_tasks);
818                 if (task != NULL) {
819                         unsigned int dispatch_count = 0;
820                         isc_boolean_t done = ISC_FALSE;
821                         isc_boolean_t requeue = ISC_FALSE;
822                         isc_boolean_t finished = ISC_FALSE;
823                         isc_event_t *event;
824
825                         INSIST(VALID_TASK(task));
826
827                         /*
828                          * Note we only unlock the manager lock if we actually
829                          * have a task to do.  We must reacquire the manager
830                          * lock before exiting the 'if (task != NULL)' block.
831                          */
832                         DEQUEUE(manager->ready_tasks, task, ready_link);
833                         manager->tasks_running++;
834                         UNLOCK(&manager->lock);
835
836                         LOCK(&task->lock);
837                         INSIST(task->state == task_state_ready);
838                         task->state = task_state_running;
839                         XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
840                                               ISC_MSG_RUNNING, "running"));
841                         do {
842                                 if (!EMPTY(task->events)) {
843                                         event = HEAD(task->events);
844                                         DEQUEUE(task->events, event, ev_link);
845
846                                         /*
847                                          * Execute the event action.
848                                          */
849                                         XTRACE(isc_msgcat_get(isc_msgcat,
850                                                             ISC_MSGSET_TASK,
851                                                             ISC_MSG_EXECUTE,
852                                                             "execute action"));
853                                         if (event->ev_action != NULL) {
854                                                 UNLOCK(&task->lock);
855                                                 (event->ev_action)(task,event);
856                                                 LOCK(&task->lock);
857                                         }
858                                         dispatch_count++;
859 #ifndef ISC_PLATFORM_USETHREADS
860                                         total_dispatch_count++;
861 #endif /* ISC_PLATFORM_USETHREADS */
862                                 }
863
864                                 if (task->references == 0 &&
865                                     EMPTY(task->events) &&
866                                     !TASK_SHUTTINGDOWN(task)) {
867                                         isc_boolean_t was_idle;
868
869                                         /*
870                                          * There are no references and no
871                                          * pending events for this task,
872                                          * which means it will not become
873                                          * runnable again via an external
874                                          * action (such as sending an event
875                                          * or detaching).
876                                          *
877                                          * We initiate shutdown to prevent
878                                          * it from becoming a zombie.
879                                          *
880                                          * We do this here instead of in
881                                          * the "if EMPTY(task->events)" block
882                                          * below because:
883                                          *
884                                          *      If we post no shutdown events,
885                                          *      we want the task to finish.
886                                          *
887                                          *      If we did post shutdown events,
888                                          *      will still want the task's
889                                          *      quantum to be applied.
890                                          */
891                                         was_idle = task_shutdown(task);
892                                         INSIST(!was_idle);
893                                 }
894
895                                 if (EMPTY(task->events)) {
896                                         /*
897                                          * Nothing else to do for this task
898                                          * right now.
899                                          */
900                                         XTRACE(isc_msgcat_get(isc_msgcat,
901                                                               ISC_MSGSET_TASK,
902                                                               ISC_MSG_EMPTY,
903                                                               "empty"));
904                                         if (task->references == 0 &&
905                                             TASK_SHUTTINGDOWN(task)) {
906                                                 /*
907                                                  * The task is done.
908                                                  */
909                                                 XTRACE(isc_msgcat_get(
910                                                                isc_msgcat,
911                                                                ISC_MSGSET_TASK,
912                                                                ISC_MSG_DONE,
913                                                                "done"));
914                                                 finished = ISC_TRUE;
915                                                 task->state = task_state_done;
916                                         } else
917                                                 task->state = task_state_idle;
918                                         done = ISC_TRUE;
919                                 } else if (dispatch_count >= task->quantum) {
920                                         /*
921                                          * Our quantum has expired, but
922                                          * there is more work to be done.
923                                          * We'll requeue it to the ready
924                                          * queue later.
925                                          *
926                                          * We don't check quantum until
927                                          * dispatching at least one event,
928                                          * so the minimum quantum is one.
929                                          */
930                                         XTRACE(isc_msgcat_get(isc_msgcat,
931                                                               ISC_MSGSET_TASK,
932                                                               ISC_MSG_QUANTUM,
933                                                               "quantum"));
934                                         task->state = task_state_ready;
935                                         requeue = ISC_TRUE;
936                                         done = ISC_TRUE;
937                                 }
938                         } while (!done);
939                         UNLOCK(&task->lock);
940
941                         if (finished)
942                                 task_finished(task);
943
944                         LOCK(&manager->lock);
945                         manager->tasks_running--;
946 #ifdef ISC_PLATFORM_USETHREADS
947                         if (manager->exclusive_requested &&
948                             manager->tasks_running == 1) {
949                                 SIGNAL(&manager->exclusive_granted);
950                         }
951 #endif /* ISC_PLATFORM_USETHREADS */
952                         if (requeue) {
953                                 /*
954                                  * We know we're awake, so we don't have
955                                  * to wakeup any sleeping threads if the
956                                  * ready queue is empty before we requeue.
957                                  *
958                                  * A possible optimization if the queue is
959                                  * empty is to 'goto' the 'if (task != NULL)'
960                                  * block, avoiding the ENQUEUE of the task
961                                  * and the subsequent immediate DEQUEUE
962                                  * (since it is the only executable task).
963                                  * We don't do this because then we'd be
964                                  * skipping the exit_requested check.  The
965                                  * cost of ENQUEUE is low anyway, especially
966                                  * when you consider that we'd have to do
967                                  * an extra EMPTY check to see if we could
968                                  * do the optimization.  If the ready queue
969                                  * were usually nonempty, the 'optimization'
970                                  * might even hurt rather than help.
971                                  */
972 #ifdef ISC_PLATFORM_USETHREADS
973                                 ENQUEUE(manager->ready_tasks, task,
974                                         ready_link);
975 #else
976                                 ENQUEUE(ready_tasks, task, ready_link);
977 #endif
978                         }
979                 }
980         }
981 #ifndef ISC_PLATFORM_USETHREADS
982         ISC_LIST_APPENDLIST(manager->ready_tasks, ready_tasks, ready_link);
983 #endif
984         UNLOCK(&manager->lock);
985 }
986
987 #ifdef ISC_PLATFORM_USETHREADS
988 static isc_threadresult_t
989 #ifdef _WIN32
990 WINAPI
991 #endif
992 run(void *uap) {
993         isc_taskmgr_t *manager = uap;
994
995         XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
996                                     ISC_MSG_STARTING, "starting"));
997
998         dispatch(manager);
999
1000         XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
1001                                     ISC_MSG_EXITING, "exiting"));
1002
1003         return ((isc_threadresult_t)0);
1004 }
1005 #endif /* ISC_PLATFORM_USETHREADS */
1006
1007 static void
1008 manager_free(isc_taskmgr_t *manager) {
1009         isc_mem_t *mctx;
1010
1011 #ifdef ISC_PLATFORM_USETHREADS
1012         (void)isc_condition_destroy(&manager->exclusive_granted);       
1013         (void)isc_condition_destroy(&manager->work_available);
1014         isc_mem_put(manager->mctx, manager->threads,
1015                     manager->workers * sizeof (isc_thread_t));
1016 #endif /* ISC_PLATFORM_USETHREADS */
1017         DESTROYLOCK(&manager->lock);
1018         manager->magic = 0;
1019         mctx = manager->mctx;
1020         isc_mem_put(mctx, manager, sizeof *manager);
1021         isc_mem_detach(&mctx);
1022 }
1023
1024 isc_result_t
1025 isc_taskmgr_create(isc_mem_t *mctx, unsigned int workers,
1026                    unsigned int default_quantum, isc_taskmgr_t **managerp)
1027 {
1028         isc_result_t result;
1029         unsigned int i, started = 0;
1030         isc_taskmgr_t *manager;
1031
1032         /*
1033          * Create a new task manager.
1034          */
1035
1036         REQUIRE(workers > 0);
1037         REQUIRE(managerp != NULL && *managerp == NULL);
1038
1039 #ifndef ISC_PLATFORM_USETHREADS
1040         UNUSED(i);
1041         UNUSED(started);
1042         UNUSED(workers);
1043
1044         if (taskmgr != NULL) {
1045                 taskmgr->refs++;
1046                 *managerp = taskmgr;
1047                 return (ISC_R_SUCCESS);
1048         }
1049 #endif /* ISC_PLATFORM_USETHREADS */
1050
1051         manager = isc_mem_get(mctx, sizeof *manager);
1052         if (manager == NULL)
1053                 return (ISC_R_NOMEMORY);
1054         manager->magic = TASK_MANAGER_MAGIC;
1055         manager->mctx = NULL;
1056         manager->workers = 0;
1057         if (isc_mutex_init(&manager->lock) != ISC_R_SUCCESS) {
1058                 UNEXPECTED_ERROR(__FILE__, __LINE__,
1059                                  "isc_mutex_init() %s",
1060                                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
1061                                                 ISC_MSG_FAILED, "failed"));
1062                 result = ISC_R_UNEXPECTED;
1063                 goto cleanup_mgr;
1064         }
1065 #ifdef ISC_PLATFORM_USETHREADS
1066         manager->threads = isc_mem_get(mctx, workers * sizeof (isc_thread_t));
1067         if (manager->threads == NULL) {
1068                 result = ISC_R_NOMEMORY;
1069                 goto cleanup_lock;
1070         }
1071         if (isc_condition_init(&manager->work_available) != ISC_R_SUCCESS) {
1072                 UNEXPECTED_ERROR(__FILE__, __LINE__,
1073                                  "isc_condition_init() %s",
1074                                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
1075                                                 ISC_MSG_FAILED, "failed"));
1076                 result = ISC_R_UNEXPECTED;
1077                 goto cleanup_threads;
1078         }
1079         if (isc_condition_init(&manager->exclusive_granted) != ISC_R_SUCCESS) {
1080                 UNEXPECTED_ERROR(__FILE__, __LINE__,
1081                                  "isc_condition_init() %s",
1082                                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
1083                                                 ISC_MSG_FAILED, "failed"));
1084                 result = ISC_R_UNEXPECTED;
1085                 goto cleanup_workavailable;
1086         }
1087 #endif /* ISC_PLATFORM_USETHREADS */
1088         if (default_quantum == 0)
1089                 default_quantum = DEFAULT_DEFAULT_QUANTUM;
1090         manager->default_quantum = default_quantum;
1091         INIT_LIST(manager->tasks);
1092         INIT_LIST(manager->ready_tasks);
1093         manager->tasks_running = 0;
1094         manager->exclusive_requested = ISC_FALSE;
1095         manager->exiting = ISC_FALSE;
1096         manager->workers = 0;
1097
1098         isc_mem_attach(mctx, &manager->mctx);
1099
1100 #ifdef ISC_PLATFORM_USETHREADS
1101         LOCK(&manager->lock);
1102         /*
1103          * Start workers.
1104          */
1105         for (i = 0; i < workers; i++) {
1106                 if (isc_thread_create(run, manager,
1107                                       &manager->threads[manager->workers]) ==
1108                     ISC_R_SUCCESS) {
1109                         manager->workers++;
1110                         started++;
1111                 }
1112         }
1113         UNLOCK(&manager->lock);
1114
1115         if (started == 0) {
1116                 manager_free(manager);
1117                 return (ISC_R_NOTHREADS);
1118         }
1119         isc_thread_setconcurrency(workers);
1120 #else /* ISC_PLATFORM_USETHREADS */
1121         manager->refs = 1;
1122         taskmgr = manager;
1123 #endif /* ISC_PLATFORM_USETHREADS */
1124
1125         *managerp = manager;
1126
1127         return (ISC_R_SUCCESS);
1128
1129 #ifdef ISC_PLATFORM_USETHREADS
1130  cleanup_workavailable:
1131         (void)isc_condition_destroy(&manager->work_available);
1132  cleanup_threads:
1133         isc_mem_put(mctx, manager->threads, workers * sizeof (isc_thread_t));
1134  cleanup_lock:
1135         DESTROYLOCK(&manager->lock);
1136 #endif
1137  cleanup_mgr:
1138         isc_mem_put(mctx, manager, sizeof *manager);
1139         return (result);
1140 }
1141
1142 void
1143 isc_taskmgr_destroy(isc_taskmgr_t **managerp) {
1144         isc_taskmgr_t *manager;
1145         isc_task_t *task;
1146         unsigned int i;
1147
1148         /*
1149          * Destroy '*managerp'.
1150          */
1151
1152         REQUIRE(managerp != NULL);
1153         manager = *managerp;
1154         REQUIRE(VALID_MANAGER(manager));
1155
1156 #ifndef ISC_PLATFORM_USETHREADS
1157         UNUSED(i);
1158
1159         if (manager->refs > 1) {
1160                 manager->refs--;
1161                 *managerp = NULL;
1162                 return;
1163         }
1164 #endif /* ISC_PLATFORM_USETHREADS */
1165
1166         XTHREADTRACE("isc_taskmgr_destroy");
1167         /*
1168          * Only one non-worker thread may ever call this routine.
1169          * If a worker thread wants to initiate shutdown of the
1170          * task manager, it should ask some non-worker thread to call
1171          * isc_taskmgr_destroy(), e.g. by signalling a condition variable
1172          * that the startup thread is sleeping on.
1173          */
1174
1175         /*
1176          * Unlike elsewhere, we're going to hold this lock a long time.
1177          * We need to do so, because otherwise the list of tasks could
1178          * change while we were traversing it.
1179          *
1180          * This is also the only function where we will hold both the
1181          * task manager lock and a task lock at the same time.
1182          */
1183
1184         LOCK(&manager->lock);
1185
1186         /*
1187          * Make sure we only get called once.
1188          */
1189         INSIST(!manager->exiting);
1190         manager->exiting = ISC_TRUE;
1191
1192         /*
1193          * Post shutdown event(s) to every task (if they haven't already been
1194          * posted).
1195          */
1196         for (task = HEAD(manager->tasks);
1197              task != NULL;
1198              task = NEXT(task, link)) {
1199                 LOCK(&task->lock);
1200                 if (task_shutdown(task))
1201                         ENQUEUE(manager->ready_tasks, task, ready_link);
1202                 UNLOCK(&task->lock);
1203         }
1204 #ifdef ISC_PLATFORM_USETHREADS
1205         /*
1206          * Wake up any sleeping workers.  This ensures we get work done if
1207          * there's work left to do, and if there are already no tasks left
1208          * it will cause the workers to see manager->exiting.
1209          */
1210         BROADCAST(&manager->work_available);
1211         UNLOCK(&manager->lock);
1212
1213         /*
1214          * Wait for all the worker threads to exit.
1215          */
1216         for (i = 0; i < manager->workers; i++)
1217                 (void)isc_thread_join(manager->threads[i], NULL);
1218 #else /* ISC_PLATFORM_USETHREADS */
1219         /*
1220          * Dispatch the shutdown events.
1221          */
1222         UNLOCK(&manager->lock);
1223         while (isc__taskmgr_ready())
1224                 (void)isc__taskmgr_dispatch();
1225         INSIST(ISC_LIST_EMPTY(manager->tasks));
1226 #endif /* ISC_PLATFORM_USETHREADS */
1227
1228         manager_free(manager);
1229
1230         *managerp = NULL;
1231 }
1232
1233 #ifndef ISC_PLATFORM_USETHREADS
1234 isc_boolean_t
1235 isc__taskmgr_ready(void) {
1236         if (taskmgr == NULL)
1237                 return (ISC_FALSE);
1238         return (ISC_TF(!ISC_LIST_EMPTY(taskmgr->ready_tasks)));
1239 }
1240
1241 isc_result_t
1242 isc__taskmgr_dispatch(void) {
1243         isc_taskmgr_t *manager = taskmgr;
1244
1245         if (taskmgr == NULL)
1246                 return (ISC_R_NOTFOUND);
1247
1248         dispatch(manager);
1249
1250         return (ISC_R_SUCCESS);
1251 }
1252
1253 #endif /* ISC_PLATFORM_USETHREADS */
1254
1255 isc_result_t
1256 isc_task_beginexclusive(isc_task_t *task) {
1257 #ifdef ISC_PLATFORM_USETHREADS  
1258         isc_taskmgr_t *manager = task->manager;
1259         REQUIRE(task->state == task_state_running);
1260         LOCK(&manager->lock);
1261         if (manager->exclusive_requested) {
1262                 UNLOCK(&manager->lock);                 
1263                 return (ISC_R_LOCKBUSY);
1264         }
1265         manager->exclusive_requested = ISC_TRUE;
1266         while (manager->tasks_running > 1) {
1267                 WAIT(&manager->exclusive_granted, &manager->lock);
1268         }
1269         UNLOCK(&manager->lock); 
1270 #else
1271         UNUSED(task);
1272 #endif
1273         return (ISC_R_SUCCESS);
1274 }
1275
1276 void
1277 isc_task_endexclusive(isc_task_t *task) {
1278 #ifdef ISC_PLATFORM_USETHREADS  
1279         isc_taskmgr_t *manager = task->manager;
1280         REQUIRE(task->state == task_state_running);
1281         LOCK(&manager->lock);
1282         REQUIRE(manager->exclusive_requested);
1283         manager->exclusive_requested = ISC_FALSE;
1284         BROADCAST(&manager->work_available);
1285         UNLOCK(&manager->lock);
1286 #else
1287         UNUSED(task);
1288 #endif
1289 }