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