task.c 38.9 KB
Newer Older
Bob Halley's avatar
Bob Halley committed
1
/*
Automatic Updater's avatar
Automatic Updater committed
2
 * Copyright (C) 2004-2009  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Copyright (C) 1998-2003  Internet Software Consortium.
4
 *
Automatic Updater's avatar
Automatic Updater committed
5
 * Permission to use, copy, modify, and/or distribute this software for any
Bob Halley's avatar
Bob Halley committed
6 7
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
8
 *
Mark Andrews's avatar
Mark Andrews committed
9 10 11 12 13 14 15
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
Bob Halley's avatar
Bob Halley committed
16
 */
Bob Halley's avatar
base  
Bob Halley committed
17

Automatic Updater's avatar
Automatic Updater committed
18
/* $Id: task.c,v 1.110 2009/09/02 23:48:02 tbox Exp $ */
David Lawrence's avatar
David Lawrence committed
19

20 21
/*! \file
 * \author Principal Author: Bob Halley
Bob Halley's avatar
Bob Halley committed
22 23
 */

24 25 26 27 28
/*
 * XXXRTH  Need to document the states a task can be in, and the rules
 * for changing states.
 */

Bob Halley's avatar
Bob Halley committed
29 30
#include <config.h>

31
#include <isc/condition.h>
32
#include <isc/event.h>
33
#include <isc/magic.h>
34
#include <isc/mem.h>
35
#include <isc/msgs.h>
Brian Wellington's avatar
Brian Wellington committed
36
#include <isc/platform.h>
37
#include <isc/string.h>
Bob Halley's avatar
Bob Halley committed
38
#include <isc/task.h>
39
#include <isc/thread.h>
Michael Graff's avatar
Michael Graff committed
40
#include <isc/util.h>
41
#include <isc/xml.h>
Bob Halley's avatar
base  
Bob Halley committed
42

43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
/*%
 * For BIND9 internal applications:
 * when built with threads we use multiple worker threads shared by the whole
 * application.
 * when built without threads we share a single global task manager and use
 * an integrated event loop for socket, timer, and other generic task events.
 * For generic library:
 * we don't use either of them: an application can have multiple task managers
 * whether or not it's threaded, and if the application is threaded each thread
 * is expected to have a separate manager; no "worker threads" are shared by
 * the application threads.
 */
#ifdef BIND9
#ifdef ISC_PLATFORM_USETHREADS
#define USE_WORKER_THREADS
#else
#define USE_SHARED_MANAGER
#endif	/* ISC_PLATFORM_USETHREADS */
#endif	/* BIND9 */

#ifndef USE_WORKER_THREADS
64
#include "task_p.h"
65
#endif /* USE_WORKER_THREADS */
66

Bob Halley's avatar
Bob Halley committed
67
#ifdef ISC_TASK_TRACE
68
#define XTRACE(m)		fprintf(stderr, "task %p thread %lu: %s\n", \
69
				       task, isc_thread_self(), (m))
70
#define XTTRACE(t, m)		fprintf(stderr, "task %p thread %lu: %s\n", \
71
				       (t), isc_thread_self(), (m))
72
#define XTHREADTRACE(m)		fprintf(stderr, "thread %lu: %s\n", \
73
				       isc_thread_self(), (m))
Bob Halley's avatar
Bob Halley committed
74 75
#else
#define XTRACE(m)
76
#define XTTRACE(t, m)
77
#define XTHREADTRACE(m)
Bob Halley's avatar
Bob Halley committed
78
#endif
Bob Halley's avatar
base  
Bob Halley committed
79

Bob Halley's avatar
Bob Halley committed
80
/***
Bob Halley's avatar
Bob Halley committed
81
 *** Types.
Bob Halley's avatar
Bob Halley committed
82 83
 ***/

Bob Halley's avatar
Bob Halley committed
84 85
typedef enum {
	task_state_idle, task_state_ready, task_state_running,
86
	task_state_done
Bob Halley's avatar
Bob Halley committed
87 88
} task_state_t;

89
#if defined(HAVE_LIBXML2) && defined(BIND9)
90 91 92
static const char *statenames[] = {
	"idle", "ready", "running", "done",
};
Mark Andrews's avatar
Mark Andrews committed
93
#endif
94

95 96
#define TASK_MAGIC			ISC_MAGIC('T', 'A', 'S', 'K')
#define VALID_TASK(t)			ISC_MAGIC_VALID(t, TASK_MAGIC)
Bob Halley's avatar
Bob Halley committed
97

98 99 100 101
typedef struct isc__task isc__task_t;
typedef struct isc__taskmgr isc__taskmgr_t;

struct isc__task {
Bob Halley's avatar
Bob Halley committed
102
	/* Not locked. */
103 104
	isc_task_t			common;
	isc__taskmgr_t *		manager;
Bob Halley's avatar
Bob Halley committed
105
	isc_mutex_t			lock;
Bob Halley's avatar
Bob Halley committed
106 107 108
	/* Locked by task lock. */
	task_state_t			state;
	unsigned int			references;
Bob Halley's avatar
Bob Halley committed
109
	isc_eventlist_t			events;
110
	isc_eventlist_t			on_shutdown;
Bob Halley's avatar
Bob Halley committed
111
	unsigned int			quantum;
112
	unsigned int			flags;
113
	isc_stdtime_t			now;
114 115
	char				name[16];
	void *				tag;
Bob Halley's avatar
Bob Halley committed
116
	/* Locked by task manager lock. */
117 118
	LINK(isc__task_t)		link;
	LINK(isc__task_t)		ready_link;
Bob Halley's avatar
Bob Halley committed
119 120
};

Bob Halley's avatar
Bob Halley committed
121
#define TASK_F_SHUTTINGDOWN		0x01
122

123 124
#define TASK_SHUTTINGDOWN(t)		(((t)->flags & TASK_F_SHUTTINGDOWN) \
					 != 0)
125

126 127
#define TASK_MANAGER_MAGIC		ISC_MAGIC('T', 'S', 'K', 'M')
#define VALID_MANAGER(m)		ISC_MAGIC_VALID(m, TASK_MANAGER_MAGIC)
Bob Halley's avatar
Bob Halley committed
128

129 130 131
typedef ISC_LIST(isc__task_t)	isc__tasklist_t;

struct isc__taskmgr {
Bob Halley's avatar
Bob Halley committed
132
	/* Not locked. */
133
	isc_taskmgr_t			common;
Bob Halley's avatar
Bob Halley committed
134
	isc_mem_t *			mctx;
Bob Halley's avatar
Bob Halley committed
135
	isc_mutex_t			lock;
136
#ifdef ISC_PLATFORM_USETHREADS
137
	unsigned int			workers;
138
	isc_thread_t *			threads;
139
#endif /* ISC_PLATFORM_USETHREADS */
Bob Halley's avatar
Bob Halley committed
140 141
	/* Locked by task manager lock. */
	unsigned int			default_quantum;
142 143
	LIST(isc__task_t)		tasks;
	isc__tasklist_t			ready_tasks;
144
#ifdef ISC_PLATFORM_USETHREADS
Bob Halley's avatar
Bob Halley committed
145
	isc_condition_t			work_available;
146
	isc_condition_t			exclusive_granted;
147
#endif /* ISC_PLATFORM_USETHREADS */
148 149
	unsigned int			tasks_running;
	isc_boolean_t			exclusive_requested;
Bob Halley's avatar
Bob Halley committed
150
	isc_boolean_t			exiting;
151
#ifdef USE_SHARED_MANAGER
152
	unsigned int			refs;
153
#endif /* ISC_PLATFORM_USETHREADS */
Bob Halley's avatar
Bob Halley committed
154
};
Bob Halley's avatar
Bob Halley committed
155

156
#define DEFAULT_TASKMGR_QUANTUM		10
Bob Halley's avatar
Bob Halley committed
157 158
#define DEFAULT_DEFAULT_QUANTUM		5
#define FINISHED(m)			((m)->exiting && EMPTY((m)->tasks))
Bob Halley's avatar
Bob Halley committed
159

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
#ifdef USE_SHARED_MANAGER
static isc__taskmgr_t *taskmgr = NULL;
#endif /* USE_SHARED_MANAGER */

/*%
 * The following can be either static or public, depending on build environment.
 */

#ifdef BIND9
#define ISC_TASKFUNC_SCOPE
#else
#define ISC_TASKFUNC_SCOPE static
#endif

ISC_TASKFUNC_SCOPE isc_result_t
isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum,
		 isc_task_t **taskp);
ISC_TASKFUNC_SCOPE void
isc__task_attach(isc_task_t *source0, isc_task_t **targetp);
ISC_TASKFUNC_SCOPE void
isc__task_detach(isc_task_t **taskp);
ISC_TASKFUNC_SCOPE void
isc__task_send(isc_task_t *task0, isc_event_t **eventp);
ISC_TASKFUNC_SCOPE void
isc__task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp);
ISC_TASKFUNC_SCOPE unsigned int
isc__task_purgerange(isc_task_t *task0, void *sender, isc_eventtype_t first,
		     isc_eventtype_t last, void *tag);
ISC_TASKFUNC_SCOPE unsigned int
isc__task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
		void *tag);
ISC_TASKFUNC_SCOPE isc_boolean_t
isc__task_purgeevent(isc_task_t *task0, isc_event_t *event);
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);
ISC_TASKFUNC_SCOPE unsigned int
isc__task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type,
		 void *tag, isc_eventlist_t *events);
ISC_TASKFUNC_SCOPE isc_result_t
isc__task_onshutdown(isc_task_t *task0, isc_taskaction_t action,
		     const void *arg);
ISC_TASKFUNC_SCOPE void
isc__task_shutdown(isc_task_t *task0);
ISC_TASKFUNC_SCOPE void
isc__task_destroy(isc_task_t **taskp);
ISC_TASKFUNC_SCOPE void
isc__task_setname(isc_task_t *task0, const char *name, void *tag);
ISC_TASKFUNC_SCOPE const char *
isc__task_getname(isc_task_t *task0);
ISC_TASKFUNC_SCOPE void *
isc__task_gettag(isc_task_t *task0);
ISC_TASKFUNC_SCOPE void
isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t);
ISC_TASKFUNC_SCOPE isc_result_t
isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers,
		    unsigned int default_quantum, isc_taskmgr_t **managerp);
ISC_TASKFUNC_SCOPE void
isc__taskmgr_destroy(isc_taskmgr_t **managerp);
ISC_TASKFUNC_SCOPE isc_result_t
isc__task_beginexclusive(isc_task_t *task);
ISC_TASKFUNC_SCOPE void
isc__task_endexclusive(isc_task_t *task0);

static struct isc__taskmethods {
	isc_taskmethods_t methods;

	/*%
	 * The following are defined just for avoiding unused static functions.
	 */
231
#ifndef BIND9
232 233 234
	void *purgeevent, *unsendrange,
		*getname, *gettag, *getcurrenttime, *beginexclusive,
		*endexclusive;
235
#endif
236 237 238 239 240 241 242 243 244 245 246 247 248
} taskmethods = {
	{
		isc__task_attach,
		isc__task_detach,
		isc__task_destroy,
		isc__task_send,
		isc__task_sendanddetach,
		isc__task_unsend,
		isc__task_onshutdown,
		isc__task_shutdown,
		isc__task_setname,
		isc__task_purge,
		isc__task_purgerange
249 250 251 252 253 254 255 256
	}
#ifndef BIND9
	,
	(void *)isc__task_purgeevent, (void *)isc__task_unsendrange,
	(void *)isc__task_getname, (void *)isc__task_gettag,
	(void *)isc__task_getcurrenttime, (void *)isc__task_beginexclusive,
	(void *)isc__task_endexclusive
#endif
257 258 259 260 261 262
};

static isc_taskmgrmethods_t taskmgrmethods = {
	isc__taskmgr_destroy,
	isc__task_create
};
263

Bob Halley's avatar
base  
Bob Halley committed
264 265 266 267 268
/***
 *** Tasks.
 ***/

static void
269 270
task_finished(isc__task_t *task) {
	isc__taskmgr_t *manager = task->manager;
Bob Halley's avatar
base  
Bob Halley committed
271 272

	REQUIRE(EMPTY(task->events));
273
	REQUIRE(EMPTY(task->on_shutdown));
274 275 276 277
	REQUIRE(task->references == 0);
	REQUIRE(task->state == task_state_done);

	XTRACE("task_finished");
Bob Halley's avatar
base  
Bob Halley committed
278 279 280

	LOCK(&manager->lock);
	UNLINK(manager->tasks, task, link);
281
#ifdef USE_WORKER_THREADS
Bob Halley's avatar
base  
Bob Halley committed
282 283 284 285 286 287 288 289 290
	if (FINISHED(manager)) {
		/*
		 * All tasks have completed and the
		 * task manager is exiting.  Wake up
		 * any idle worker threads so they
		 * can exit.
		 */
		BROADCAST(&manager->work_available);
	}
291
#endif /* USE_WORKER_THREADS */
Bob Halley's avatar
base  
Bob Halley committed
292
	UNLOCK(&manager->lock);
293

Brian Wellington's avatar
Brian Wellington committed
294
	DESTROYLOCK(&task->lock);
295 296
	task->common.impmagic = 0;
	task->common.magic = 0;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
297
	isc_mem_put(manager->mctx, task, sizeof(*task));
Bob Halley's avatar
base  
Bob Halley committed
298 299
}

300 301 302
ISC_TASKFUNC_SCOPE isc_result_t
isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum,
		 isc_task_t **taskp)
Bob Halley's avatar
Bob Halley committed
303
{
Automatic Updater's avatar
Automatic Updater committed
304
	isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0;
305
	isc__task_t *task;
306
	isc_boolean_t exiting;
307
	isc_result_t result;
Bob Halley's avatar
base  
Bob Halley committed
308 309 310 311

	REQUIRE(VALID_MANAGER(manager));
	REQUIRE(taskp != NULL && *taskp == NULL);

Andreas Gustafsson's avatar
Andreas Gustafsson committed
312
	task = isc_mem_get(manager->mctx, sizeof(*task));
Bob Halley's avatar
base  
Bob Halley committed
313
	if (task == NULL)
Bob Halley's avatar
Bob Halley committed
314
		return (ISC_R_NOMEMORY);
315
	XTRACE("isc_task_create");
Bob Halley's avatar
base  
Bob Halley committed
316
	task->manager = manager;
317 318
	result = isc_mutex_init(&task->lock);
	if (result != ISC_R_SUCCESS) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
319
		isc_mem_put(manager->mctx, task, sizeof(*task));
320
		return (result);
Bob Halley's avatar
update  
Bob Halley committed
321
	}
Bob Halley's avatar
base  
Bob Halley committed
322 323 324
	task->state = task_state_idle;
	task->references = 1;
	INIT_LIST(task->events);
325
	INIT_LIST(task->on_shutdown);
Bob Halley's avatar
base  
Bob Halley committed
326
	task->quantum = quantum;
Bob Halley's avatar
Bob Halley committed
327
	task->flags = 0;
328
	task->now = 0;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
329
	memset(task->name, 0, sizeof(task->name));
330
	task->tag = NULL;
Bob Halley's avatar
base  
Bob Halley committed
331 332 333
	INIT_LINK(task, link);
	INIT_LINK(task, ready_link);

334
	exiting = ISC_FALSE;
Bob Halley's avatar
base  
Bob Halley committed
335
	LOCK(&manager->lock);
336 337 338 339 340 341
	if (!manager->exiting) {
		if (task->quantum == 0)
			task->quantum = manager->default_quantum;
		APPEND(manager->tasks, task, link);
	} else
		exiting = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
342 343
	UNLOCK(&manager->lock);

344
	if (exiting) {
Brian Wellington's avatar
Brian Wellington committed
345
		DESTROYLOCK(&task->lock);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
346
		isc_mem_put(manager->mctx, task, sizeof(*task));
347 348 349
		return (ISC_R_SHUTTINGDOWN);
	}

350 351 352 353
	task->common.methods = (isc_taskmethods_t *)&taskmethods;
	task->common.magic = ISCAPI_TASK_MAGIC;
	task->common.impmagic = TASK_MAGIC;
	*taskp = (isc_task_t *)task;
Bob Halley's avatar
base  
Bob Halley committed
354

Bob Halley's avatar
Bob Halley committed
355
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
356 357
}

358 359 360
ISC_TASKFUNC_SCOPE void
isc__task_attach(isc_task_t *source0, isc_task_t **targetp) {
	isc__task_t *source = (isc__task_t *)source0;
Bob Halley's avatar
base  
Bob Halley committed
361

362 363 364
	/*
	 * Attach *targetp to source.
	 */
Bob Halley's avatar
base  
Bob Halley committed
365

366 367
	REQUIRE(VALID_TASK(source));
	REQUIRE(targetp != NULL && *targetp == NULL);
Bob Halley's avatar
base  
Bob Halley committed
368

369
	XTTRACE(source, "isc_task_attach");
370

371 372 373 374
	LOCK(&source->lock);
	source->references++;
	UNLOCK(&source->lock);

375
	*targetp = (isc_task_t *)source;
Bob Halley's avatar
base  
Bob Halley committed
376 377
}

378
static inline isc_boolean_t
379
task_shutdown(isc__task_t *task) {
380 381
	isc_boolean_t was_idle = ISC_FALSE;
	isc_event_t *event, *prev;
382

383 384 385 386 387 388 389
	/*
	 * Caller must be holding the task's lock.
	 */

	XTRACE("task_shutdown");

	if (! TASK_SHUTTINGDOWN(task)) {
390 391
		XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
				      ISC_MSG_SHUTTINGDOWN, "shutting down"));
392 393 394 395 396 397 398 399 400 401 402 403 404 405
		task->flags |= TASK_F_SHUTTINGDOWN;
		if (task->state == task_state_idle) {
			INSIST(EMPTY(task->events));
			task->state = task_state_ready;
			was_idle = ISC_TRUE;
		}
		INSIST(task->state == task_state_ready ||
		       task->state == task_state_running);
		/*
		 * Note that we post shutdown events LIFO.
		 */
		for (event = TAIL(task->on_shutdown);
		     event != NULL;
		     event = prev) {
406 407 408
			prev = PREV(event, ev_link);
			DEQUEUE(task->on_shutdown, event, ev_link);
			ENQUEUE(task->events, event, ev_link);
409 410 411 412 413 414 415
		}
	}

	return (was_idle);
}

static inline void
416 417
task_ready(isc__task_t *task) {
	isc__taskmgr_t *manager = task->manager;
418 419 420 421 422 423 424 425 426

	REQUIRE(VALID_MANAGER(manager));
	REQUIRE(task->state == task_state_ready);

	XTRACE("task_ready");

	LOCK(&manager->lock);

	ENQUEUE(manager->ready_tasks, task, ready_link);
427
#ifdef USE_WORKER_THREADS
428
	SIGNAL(&manager->work_available);
429
#endif /* USE_WORKER_THREADS */
430 431 432 433

	UNLOCK(&manager->lock);
}

Bob Halley's avatar
Bob Halley committed
434
static inline isc_boolean_t
435
task_detach(isc__task_t *task) {
436 437 438 439 440 441 442 443 444 445

	/*
	 * Caller must be holding the task lock.
	 */

	REQUIRE(task->references > 0);

	XTRACE("detach");

	task->references--;
Bob Halley's avatar
Bob Halley committed
446 447 448 449 450 451 452
	if (task->references == 0 && task->state == task_state_idle) {
		INSIST(EMPTY(task->events));
		/*
		 * There are no references to this task, and no
		 * pending events.  We could try to optimize and
		 * either initiate shutdown or clean up the task,
		 * depending on its state, but it's easier to just
453 454
		 * make the task ready and allow run() or the event
		 * loop to deal with shutting down and termination.
Bob Halley's avatar
Bob Halley committed
455 456 457
		 */
		task->state = task_state_ready;
		return (ISC_TRUE);
458 459
	}

Bob Halley's avatar
Bob Halley committed
460
	return (ISC_FALSE);
461 462
}

463 464 465
ISC_TASKFUNC_SCOPE void
isc__task_detach(isc_task_t **taskp) {
	isc__task_t *task;
Bob Halley's avatar
Bob Halley committed
466
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
467

468 469 470 471
	/*
	 * Detach *taskp from its task.
	 */

Bob Halley's avatar
base  
Bob Halley committed
472
	REQUIRE(taskp != NULL);
473
	task = (isc__task_t *)*taskp;
Bob Halley's avatar
base  
Bob Halley committed
474 475
	REQUIRE(VALID_TASK(task));

476 477
	XTRACE("isc_task_detach");

Bob Halley's avatar
base  
Bob Halley committed
478
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
479
	was_idle = task_detach(task);
Bob Halley's avatar
base  
Bob Halley committed
480 481
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
482
	if (was_idle)
483
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
484 485 486 487

	*taskp = NULL;
}

Bob Halley's avatar
Bob Halley committed
488
static inline isc_boolean_t
489
task_send(isc__task_t *task, isc_event_t **eventp) {
Bob Halley's avatar
Bob Halley committed
490
	isc_boolean_t was_idle = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
491
	isc_event_t *event;
492

493
	/*
494
	 * Caller must be holding the task lock.
495 496
	 */

Bob Halley's avatar
update  
Bob Halley committed
497 498
	REQUIRE(eventp != NULL);
	event = *eventp;
Bob Halley's avatar
base  
Bob Halley committed
499
	REQUIRE(event != NULL);
500
	REQUIRE(event->ev_type > 0);
Bob Halley's avatar
Bob Halley committed
501
	REQUIRE(task->state != task_state_done);
Bob Halley's avatar
base  
Bob Halley committed
502

503 504
	XTRACE("task_send");

Bob Halley's avatar
Bob Halley committed
505 506 507 508
	if (task->state == task_state_idle) {
		was_idle = ISC_TRUE;
		INSIST(EMPTY(task->events));
		task->state = task_state_ready;
509
	}
Bob Halley's avatar
Bob Halley committed
510 511
	INSIST(task->state == task_state_ready ||
	       task->state == task_state_running);
512
	ENQUEUE(task->events, event, ev_link);
Bob Halley's avatar
Bob Halley committed
513
	*eventp = NULL;
514

Bob Halley's avatar
Bob Halley committed
515
	return (was_idle);
516
}
517

518 519 520
ISC_TASKFUNC_SCOPE void
isc__task_send(isc_task_t *task0, isc_event_t **eventp) {
	isc__task_t *task = (isc__task_t *)task0;
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
	isc_boolean_t was_idle;

	/*
	 * Send '*event' to 'task'.
	 */

	REQUIRE(VALID_TASK(task));

	XTRACE("isc_task_send");

	/*
	 * We're trying hard to hold locks for as short a time as possible.
	 * We're also trying to hold as few locks as possible.  This is why
	 * some processing is deferred until after the lock is released.
	 */
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
537
	was_idle = task_send(task, eventp);
Bob Halley's avatar
base  
Bob Halley committed
538 539 540 541 542 543
	UNLOCK(&task->lock);

	if (was_idle) {
		/*
		 * We need to add this task to the ready queue.
		 *
544 545 546
		 * We've waited until now to do it because making a task
		 * ready requires locking the manager.  If we tried to do
		 * this while holding the task lock, we could deadlock.
Bob Halley's avatar
base  
Bob Halley committed
547 548
		 *
		 * We've changed the state to ready, so no one else will
549 550 551
		 * be trying to add this task to the ready queue.  The
		 * only way to leave the ready state is by executing the
		 * task.  It thus doesn't matter if events are added,
552
		 * removed, or a shutdown is started in the interval
553 554
		 * between the time we released the task lock, and the time
		 * we add the task to the ready queue.
Bob Halley's avatar
base  
Bob Halley committed
555
		 */
556
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
557
	}
558 559
}

560 561
ISC_TASKFUNC_SCOPE void
isc__task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
Bob Halley's avatar
Bob Halley committed
562
	isc_boolean_t idle1, idle2;
563
	isc__task_t *task;
564 565 566 567 568 569 570

	/*
	 * Send '*event' to '*taskp' and then detach '*taskp' from its
	 * task.
	 */

	REQUIRE(taskp != NULL);
571
	task = (isc__task_t *)*taskp;
572 573 574
	REQUIRE(VALID_TASK(task));

	XTRACE("isc_task_sendanddetach");
Bob Halley's avatar
update  
Bob Halley committed
575

576
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
577 578
	idle1 = task_send(task, eventp);
	idle2 = task_detach(task);
579 580
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
581 582 583 584 585 586
	/*
	 * If idle1, then idle2 shouldn't be true as well since we're holding
	 * the task lock, and thus the task cannot switch from ready back to
	 * idle.
	 */
	INSIST(!(idle1 && idle2));
587

Bob Halley's avatar
Bob Halley committed
588 589
	if (idle1 || idle2)
		task_ready(task);
590 591

	*taskp = NULL;
Bob Halley's avatar
base  
Bob Halley committed
592 593
}

594
#define PURGE_OK(event)	(((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0)
595 596

static unsigned int
597
dequeue_events(isc__task_t *task, void *sender, isc_eventtype_t first,
598 599
	       isc_eventtype_t last, void *tag,
	       isc_eventlist_t *events, isc_boolean_t purging)
600
{
Bob Halley's avatar
Bob Halley committed
601
	isc_event_t *event, *next_event;
602
	unsigned int count = 0;
603

Bob Halley's avatar
Bob Halley committed
604
	REQUIRE(VALID_TASK(task));
605
	REQUIRE(last >= first);
Bob Halley's avatar
Bob Halley committed
606

607
	XTRACE("dequeue_events");
Bob Halley's avatar
Bob Halley committed
608

Bob Halley's avatar
Bob Halley committed
609
	/*
610 611 612
	 * Events matching 'sender', whose type is >= first and <= last, and
	 * whose tag is 'tag' will be dequeued.  If 'purging', matching events
	 * which are marked as unpurgable will not be dequeued.
613
	 *
614
	 * sender == NULL means "any sender", and tag == NULL means "any tag".
Bob Halley's avatar
Bob Halley committed
615 616 617
	 */

	LOCK(&task->lock);
618 619

	for (event = HEAD(task->events); event != NULL; event = next_event) {
620 621 622 623
		next_event = NEXT(event, ev_link);
		if (event->ev_type >= first && event->ev_type <= last &&
		    (sender == NULL || event->ev_sender == sender) &&
		    (tag == NULL || event->ev_tag == tag) &&
624
		    (!purging || PURGE_OK(event))) {
625 626
			DEQUEUE(task->events, event, ev_link);
			ENQUEUE(*events, event, ev_link);
627
			count++;
Bob Halley's avatar
Bob Halley committed
628 629
		}
	}
630

Bob Halley's avatar
Bob Halley committed
631 632
	UNLOCK(&task->lock);

633 634 635
	return (count);
}

636 637 638
ISC_TASKFUNC_SCOPE unsigned int
isc__task_purgerange(isc_task_t *task0, void *sender, isc_eventtype_t first,
		     isc_eventtype_t last, void *tag)
639
{
640
	isc__task_t *task = (isc__task_t *)task0;
641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656
	unsigned int count;
	isc_eventlist_t events;
	isc_event_t *event, *next_event;

	/*
	 * Purge events from a task's event queue.
	 */

	XTRACE("isc_task_purgerange");

	ISC_LIST_INIT(events);

	count = dequeue_events(task, sender, first, last, tag, &events,
			       ISC_TRUE);

	for (event = HEAD(events); event != NULL; event = next_event) {
657
		next_event = NEXT(event, ev_link);
Bob Halley's avatar
Bob Halley committed
658
		isc_event_free(&event);
Bob Halley's avatar
Bob Halley committed
659
	}
660

661 662 663 664 665
	/*
	 * Note that purging never changes the state of the task.
	 */

	return (count);
Bob Halley's avatar
Bob Halley committed
666 667
}

668 669 670
ISC_TASKFUNC_SCOPE unsigned int
isc__task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
		void *tag)
Bob Halley's avatar
Bob Halley committed
671
{
672 673 674 675
	/*
	 * Purge events from a task's event queue.
	 */

676 677
	XTRACE("isc_task_purge");

678
	return (isc__task_purgerange(task, sender, type, type, tag));
679 680
}

681 682 683
ISC_TASKFUNC_SCOPE isc_boolean_t
isc__task_purgeevent(isc_task_t *task0, isc_event_t *event) {
	isc__task_t *task = (isc__task_t *)task0;
Bob Halley's avatar
Bob Halley committed
684 685 686 687
	isc_event_t *curr_event, *next_event;

	/*
	 * Purge 'event' from a task's event queue.
688 689
	 *
	 * XXXRTH:  WARNING:  This method may be removed before beta.
Bob Halley's avatar
Bob Halley committed
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
	 */

	REQUIRE(VALID_TASK(task));

	/*
	 * If 'event' is on the task's event queue, it will be purged,
	 * unless it is marked as unpurgeable.  'event' does not have to be
	 * on the task's event queue; in fact, it can even be an invalid
	 * pointer.  Purging only occurs if the event is actually on the task's
	 * event queue.
	 *
	 * Purging never changes the state of the task.
	 */

	LOCK(&task->lock);
	for (curr_event = HEAD(task->events);
	     curr_event != NULL;
	     curr_event = next_event) {
708
		next_event = NEXT(curr_event, ev_link);
709
		if (curr_event == event && PURGE_OK(event)) {
710
			DEQUEUE(task->events, curr_event, ev_link);
Bob Halley's avatar
Bob Halley committed
711 712 713 714 715 716 717 718 719 720 721 722 723
			break;
		}
	}
	UNLOCK(&task->lock);

	if (curr_event == NULL)
		return (ISC_FALSE);

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

724 725 726 727
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)
728 729 730 731 732 733 734
{
	/*
	 * Remove events from a task's event queue.
	 */

	XTRACE("isc_task_unsendrange");

735 736
	return (dequeue_events((isc__task_t *)task, sender, first,
			       last, tag, events, ISC_FALSE));
737 738
}

739 740 741
ISC_TASKFUNC_SCOPE unsigned int
isc__task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type,
		 void *tag, isc_eventlist_t *events)
742 743 744 745 746 747 748
{
	/*
	 * Remove events from a task's event queue.
	 */

	XTRACE("isc_task_unsend");

749 750
	return (dequeue_events((isc__task_t *)task, sender, type,
			       type, tag, events, ISC_FALSE));
751 752
}

753 754 755
ISC_TASKFUNC_SCOPE isc_result_t
isc__task_onshutdown(isc_task_t *task0, isc_taskaction_t action,
		     const void *arg)
David Lawrence's avatar
David Lawrence committed
756
{
757
	isc__task_t *task = (isc__task_t *)task0;
758 759 760 761
	isc_boolean_t disallowed = ISC_FALSE;
	isc_result_t result = ISC_R_SUCCESS;
	isc_event_t *event;

762 763 764 765 766
	/*
	 * Send a shutdown event with action 'action' and argument 'arg' when
	 * 'task' is shutdown.
	 */

767
	REQUIRE(VALID_TASK(task));
768
	REQUIRE(action != NULL);
769

770
	event = isc_event_allocate(task->manager->mctx,
771 772 773 774
				   NULL,
				   ISC_TASKEVENT_SHUTDOWN,
				   action,
				   arg,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
775
				   sizeof(*event));
776 777 778 779
	if (event == NULL)
		return (ISC_R_NOMEMORY);

	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
780
	if (TASK_SHUTTINGDOWN(task)) {
781
		disallowed = ISC_TRUE;
782
		result = ISC_R_SHUTTINGDOWN;
783
	} else
784
		ENQUEUE(task->on_shutdown, event, ev_link);
785 786 787
	UNLOCK(&task->lock);

	if (disallowed)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
788
		isc_mem_put(task->manager->mctx, event, sizeof(*event));
789 790 791 792

	return (result);
}

793 794 795
ISC_TASKFUNC_SCOPE void
isc__task_shutdown(isc_task_t *task0) {
	isc__task_t *task = (isc__task_t *)task0;
796
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
797

798 799 800 801
	/*
	 * Shutdown 'task'.
	 */

Bob Halley's avatar
base  
Bob Halley committed
802 803 804
	REQUIRE(VALID_TASK(task));

	LOCK(&task->lock);
805
	was_idle = task_shutdown(task);
Bob Halley's avatar
base  
Bob Halley committed
806 807
	UNLOCK(&task->lock);

808 809
	if (was_idle)
		task_ready(task);
Bob Halley's avatar
Bob Halley committed
810
}
Bob Halley's avatar
base  
Bob Halley committed
811

812 813
ISC_TASKFUNC_SCOPE void
isc__task_destroy(isc_task_t **taskp) {
Bob Halley's avatar
Bob Halley committed
814

815 816 817 818
	/*
	 * Destroy '*taskp'.
	 */

Bob Halley's avatar
Bob Halley committed
819 820
	REQUIRE(taskp != NULL);

Bob Halley's avatar
Bob Halley committed
821 822
	isc_task_shutdown(*taskp);
	isc_task_detach(taskp);
Bob Halley's avatar
base  
Bob Halley committed
823 824
}

825 826 827
ISC_TASKFUNC_SCOPE void
isc__task_setname(isc_task_t *task0, const char *name, void *tag) {
	isc__task_t *task = (isc__task_t *)task0;
828 829 830 831 832 833 834 835 836 837 838 839 840

	/*
	 * Name 'task'.
	 */

	REQUIRE(VALID_TASK(task));

	LOCK(&task->lock);
	memset(task->name, 0, sizeof(task->name));
	strncpy(task->name, name, sizeof(task->name) - 1);
	task->tag = tag;
	UNLOCK(&task->lock);
}
Bob Halley's avatar
Bob Halley committed
841

842 843 844 845 846 847
ISC_TASKFUNC_SCOPE const char *
isc__task_getname(isc_task_t *task0) {
	isc__task_t *task = (isc__task_t *)task0;

	REQUIRE(VALID_TASK(task));

848 849 850
	return (task->name);
}

851 852 853 854 855 856
ISC_TASKFUNC_SCOPE void *
isc__task_gettag(isc_task_t *task0) {
	isc__task_t *task = (isc__task_t *)task0;

	REQUIRE(VALID_TASK(task));

857 858 859
	return (task->tag);
}

860 861 862 863
ISC_TASKFUNC_SCOPE void
isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t) {
	isc__task_t *task = (isc__task_t *)task0;

864 865 866 867 868 869 870 871 872
	REQUIRE(VALID_TASK(task));
	REQUIRE(t != NULL);

	LOCK(&task->lock);

	*t = task->now;

	UNLOCK(&task->lock);
}
873

Bob Halley's avatar
base  
Bob Halley committed
874 875 876
/***
 *** Task Manager.
 ***/
877
static void
878 879 880
dispatch(isc__taskmgr_t *manager) {
	isc__task_t *task;
#ifndef USE_WORKER_THREADS
881
	unsigned int total_dispatch_count = 0;
882 883
	isc__tasklist_t ready_tasks;
#endif /* USE_WORKER_THREADS */
Bob Halley's avatar
base  
Bob Halley committed
884 885 886 887 888 889

	REQUIRE(VALID_MANAGER(manager));

	/*
	 * Again we're trying to hold the lock for as short a time as possible
	 * and to do as little locking and unlocking as possible.
890
	 *
Bob Halley's avatar
base  
Bob Halley committed
891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917
	 * In both while loops, the appropriate lock must be held before the
	 * while body starts.  Code which acquired the lock at the top of
	 * the loop would be more readable, but would result in a lot of
	 * extra locking.  Compare:
	 *
	 * Straightforward:
	 *
	 *	LOCK();
	 *	...
	 *	UNLOCK();
	 *	while (expression) {
	 *		LOCK();
	 *		...
	 *		UNLOCK();
	 *
	 *	       	Unlocked part here...
	 *
	 *		LOCK();
	 *		...
	 *		UNLOCK();
	 *	}
	 *
	 * Note how if the loop continues we unlock and then immediately lock.
	 * For N iterations of the loop, this code does 2N+1 locks and 2N+1
	 * unlocks.  Also note that the lock is not held when the while
	 * condition is tested, which may or may not be important, depending
	 * on the expression.
918
	 *
Bob Halley's avatar
base  
Bob Halley committed
919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936
	 * As written:
	 *
	 *	LOCK();
	 *	while (expression) {
	 *		...
	 *		UNLOCK();
	 *
	 *	       	Unlocked part here...
	 *
	 *		LOCK();
	 *		...
	 *	}
	 *	UNLOCK();
	 *
	 * For N iterations of the loop, this code does N+1 locks and N+1
	 * unlocks.  The while expression is always protected by the lock.
	 */

937
#ifndef USE_WORKER_THREADS
938 939
	ISC_LIST_INIT(ready_tasks);
#endif
Bob Halley's avatar
base  
Bob Halley committed
940 941
	LOCK(&manager->lock);
	while (!FINISHED(manager)) {
942
#ifdef USE_WORKER_THREADS
Bob Halley's avatar
base  
Bob Halley committed
943 944
		/*
		 * For reasons similar to those given in the comment in
945
		 * isc_task_send() above, it is safe for us to dequeue
Bob Halley's avatar
base  
Bob Halley committed
946 947 948 949
		 * the task while only holding the manager lock, and then
		 * change the task to running state while only holding the
		 * task lock.
		 */
950
		while ((EMPTY(manager->ready_tasks) ||
Automatic Updater's avatar
Automatic Updater committed
951 952 953
			manager->exclusive_requested) &&
			!FINISHED(manager))
		{
954 955 956
			XTHREADTRACE(isc_msgcat_get(isc_msgcat,
						    ISC_MSGSET_GENERAL,
						    ISC_MSG_WAIT, "wait"));
Bob Halley's avatar
base  
Bob Halley committed
957
			WAIT(&manager->work_available, &manager->lock);
958 959 960
			XTHREADTRACE(isc_msgcat_get(isc_msgcat,
						    ISC_MSGSET_TASK,
						    ISC_MSG_AWAKE, "awake"));
Bob Halley's avatar
base  
Bob Halley committed
961
		}
962
#else /* USE_WORKER_THREADS */
963 964 965
		if (total_dispatch_count >= DEFAULT_TASKMGR_QUANTUM ||
		    EMPTY(manager->ready_tasks))
			break;
966
#endif /* USE_WORKER_THREADS */
967 968
		XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK,
					    ISC_MSG_WORKING, "working"));
969

Bob Halley's avatar
base  
Bob Halley committed
970 971
		task = HEAD(manager->ready_tasks);
		if (task != NULL) {
Bob Halley's avatar
Bob Halley committed
972
			unsigned<