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