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