task.c 39.3 KB
Newer Older
Bob Halley's avatar
Bob Halley committed
1
/*
Automatic Updater's avatar
Automatic Updater committed
2
 * Copyright (C) 2004-2010  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

18
/* $Id: task.c,v 1.116 2011/02/03 05:41:54 marka 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

Francis Dupont's avatar
Francis Dupont committed
43 44 45 46
#ifdef OPENSSL_LEAKS
#include <openssl/err.h>
#endif

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
/*%
 * 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
68
#include "task_p.h"
69
#endif /* USE_WORKER_THREADS */
70

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

Bob Halley's avatar
Bob Halley committed
84
/***
Bob Halley's avatar
Bob Halley committed
85
 *** Types.
Bob Halley's avatar
Bob Halley committed
86 87
 ***/

Bob Halley's avatar
Bob Halley committed
88 89
typedef enum {
	task_state_idle, task_state_ready, task_state_running,
90
	task_state_done
Bob Halley's avatar
Bob Halley committed
91 92
} task_state_t;

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

99 100
#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
101

102 103 104 105
typedef struct isc__task isc__task_t;
typedef struct isc__taskmgr isc__taskmgr_t;

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

Bob Halley's avatar
Bob Halley committed
125
#define TASK_F_SHUTTINGDOWN		0x01
126

127 128
#define TASK_SHUTTINGDOWN(t)		(((t)->flags & TASK_F_SHUTTINGDOWN) \
					 != 0)
129

130 131
#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
132

133 134 135
typedef ISC_LIST(isc__task_t)	isc__tasklist_t;

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

160
#define DEFAULT_TASKMGR_QUANTUM		10
Bob Halley's avatar
Bob Halley committed
161 162
#define DEFAULT_DEFAULT_QUANTUM		5
#define FINISHED(m)			((m)->exiting && EMPTY((m)->tasks))
Bob Halley's avatar
Bob Halley committed
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 231 232 233 234
#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.
	 */
235
#ifndef BIND9
236
	void *purgeevent, *unsendrange, *getname, *gettag, *getcurrenttime;
237
#endif
238 239 240 241 242 243 244 245 246 247 248 249
} 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,
250 251 252
		isc__task_purgerange,
		isc__task_beginexclusive,
		isc__task_endexclusive
253 254 255 256 257
	}
#ifndef BIND9
	,
	(void *)isc__task_purgeevent, (void *)isc__task_unsendrange,
	(void *)isc__task_getname, (void *)isc__task_gettag,
258
	(void *)isc__task_getcurrenttime
259
#endif
260 261 262 263 264 265
};

static isc_taskmgrmethods_t taskmgrmethods = {
	isc__taskmgr_destroy,
	isc__task_create
};
266

Bob Halley's avatar
base  
Bob Halley committed
267 268 269 270 271
/***
 *** Tasks.
 ***/

static void
272 273
task_finished(isc__task_t *task) {
	isc__taskmgr_t *manager = task->manager;
Bob Halley's avatar
base  
Bob Halley committed
274 275

	REQUIRE(EMPTY(task->events));
276
	REQUIRE(EMPTY(task->on_shutdown));
277 278 279 280
	REQUIRE(task->references == 0);
	REQUIRE(task->state == task_state_done);

	XTRACE("task_finished");
Bob Halley's avatar
base  
Bob Halley committed
281 282 283

	LOCK(&manager->lock);
	UNLINK(manager->tasks, task, link);
284
#ifdef USE_WORKER_THREADS
Bob Halley's avatar
base  
Bob Halley committed
285 286 287 288 289 290 291 292 293
	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);
	}
294
#endif /* USE_WORKER_THREADS */
Bob Halley's avatar
base  
Bob Halley committed
295
	UNLOCK(&manager->lock);
296

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

303 304 305
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
306
{
Automatic Updater's avatar
Automatic Updater committed
307
	isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0;
308
	isc__task_t *task;
309
	isc_boolean_t exiting;
310
	isc_result_t result;
Bob Halley's avatar
base  
Bob Halley committed
311 312 313 314

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

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

337
	exiting = ISC_FALSE;
Bob Halley's avatar
base  
Bob Halley committed
338
	LOCK(&manager->lock);
339 340 341 342 343 344
	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
345 346
	UNLOCK(&manager->lock);

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

353 354 355 356
	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
357

Bob Halley's avatar
Bob Halley committed
358
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
359 360
}

361 362 363
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
364

365 366 367
	/*
	 * Attach *targetp to source.
	 */
Bob Halley's avatar
base  
Bob Halley committed
368

369 370
	REQUIRE(VALID_TASK(source));
	REQUIRE(targetp != NULL && *targetp == NULL);
Bob Halley's avatar
base  
Bob Halley committed
371

372
	XTTRACE(source, "isc_task_attach");
373

374 375 376 377
	LOCK(&source->lock);
	source->references++;
	UNLOCK(&source->lock);

378
	*targetp = (isc_task_t *)source;
Bob Halley's avatar
base  
Bob Halley committed
379 380
}

381
static inline isc_boolean_t
382
task_shutdown(isc__task_t *task) {
383 384
	isc_boolean_t was_idle = ISC_FALSE;
	isc_event_t *event, *prev;
385

386 387 388 389 390 391 392
	/*
	 * Caller must be holding the task's lock.
	 */

	XTRACE("task_shutdown");

	if (! TASK_SHUTTINGDOWN(task)) {
393 394
		XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
				      ISC_MSG_SHUTTINGDOWN, "shutting down"));
395 396 397 398 399 400 401 402 403 404 405 406 407 408
		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) {
409 410 411
			prev = PREV(event, ev_link);
			DEQUEUE(task->on_shutdown, event, ev_link);
			ENQUEUE(task->events, event, ev_link);
412 413 414 415 416 417 418
		}
	}

	return (was_idle);
}

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

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

	XTRACE("task_ready");

	LOCK(&manager->lock);

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

	UNLOCK(&manager->lock);
}

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

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

	REQUIRE(task->references > 0);

	XTRACE("detach");

	task->references--;
Bob Halley's avatar
Bob Halley committed
449 450 451 452 453 454 455
	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
456 457
		 * 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
458 459 460
		 */
		task->state = task_state_ready;
		return (ISC_TRUE);
461 462
	}

Bob Halley's avatar
Bob Halley committed
463
	return (ISC_FALSE);
464 465
}

466 467 468
ISC_TASKFUNC_SCOPE void
isc__task_detach(isc_task_t **taskp) {
	isc__task_t *task;
Bob Halley's avatar
Bob Halley committed
469
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
470

471 472 473 474
	/*
	 * Detach *taskp from its task.
	 */

Bob Halley's avatar
base  
Bob Halley committed
475
	REQUIRE(taskp != NULL);
476
	task = (isc__task_t *)*taskp;
Bob Halley's avatar
base  
Bob Halley committed
477 478
	REQUIRE(VALID_TASK(task));

479 480
	XTRACE("isc_task_detach");

Bob Halley's avatar
base  
Bob Halley committed
481
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
482
	was_idle = task_detach(task);
Bob Halley's avatar
base  
Bob Halley committed
483 484
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
485
	if (was_idle)
486
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
487 488 489 490

	*taskp = NULL;
}

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

496
	/*
497
	 * Caller must be holding the task lock.
498 499
	 */

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

506 507
	XTRACE("task_send");

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

Bob Halley's avatar
Bob Halley committed
518
	return (was_idle);
519
}
520

521 522 523
ISC_TASKFUNC_SCOPE void
isc__task_send(isc_task_t *task0, isc_event_t **eventp) {
	isc__task_t *task = (isc__task_t *)task0;
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
	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
540
	was_idle = task_send(task, eventp);
Bob Halley's avatar
base  
Bob Halley committed
541 542 543 544 545 546
	UNLOCK(&task->lock);

	if (was_idle) {
		/*
		 * We need to add this task to the ready queue.
		 *
547 548 549
		 * 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
550 551
		 *
		 * We've changed the state to ready, so no one else will
552 553 554
		 * 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,
555
		 * removed, or a shutdown is started in the interval
556 557
		 * 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
558
		 */
559
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
560
	}
561 562
}

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

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

	REQUIRE(taskp != NULL);
574
	task = (isc__task_t *)*taskp;
575 576 577
	REQUIRE(VALID_TASK(task));

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

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

Bob Halley's avatar
Bob Halley committed
584 585 586 587 588 589
	/*
	 * 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));
590

Bob Halley's avatar
Bob Halley committed
591 592
	if (idle1 || idle2)
		task_ready(task);
593 594

	*taskp = NULL;
Bob Halley's avatar
base  
Bob Halley committed
595 596
}

597
#define PURGE_OK(event)	(((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0)
598 599

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

Bob Halley's avatar
Bob Halley committed
607
	REQUIRE(VALID_TASK(task));
608
	REQUIRE(last >= first);
Bob Halley's avatar
Bob Halley committed
609

610
	XTRACE("dequeue_events");
Bob Halley's avatar
Bob Halley committed
611

Bob Halley's avatar
Bob Halley committed
612
	/*
613 614 615
	 * 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.
616
	 *
617
	 * sender == NULL means "any sender", and tag == NULL means "any tag".
Bob Halley's avatar
Bob Halley committed
618 619 620
	 */

	LOCK(&task->lock);
621 622

	for (event = HEAD(task->events); event != NULL; event = next_event) {
623 624 625 626
		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) &&
627
		    (!purging || PURGE_OK(event))) {
628 629
			DEQUEUE(task->events, event, ev_link);
			ENQUEUE(*events, event, ev_link);
630
			count++;
Bob Halley's avatar
Bob Halley committed
631 632
		}
	}
633

Bob Halley's avatar
Bob Halley committed
634 635
	UNLOCK(&task->lock);

636 637 638
	return (count);
}

639 640 641
ISC_TASKFUNC_SCOPE unsigned int
isc__task_purgerange(isc_task_t *task0, void *sender, isc_eventtype_t first,
		     isc_eventtype_t last, void *tag)
642
{
643
	isc__task_t *task = (isc__task_t *)task0;
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
	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) {
660
		next_event = NEXT(event, ev_link);
Bob Halley's avatar
Bob Halley committed
661
		isc_event_free(&event);
Bob Halley's avatar
Bob Halley committed
662
	}
663

664 665 666 667 668
	/*
	 * Note that purging never changes the state of the task.
	 */

	return (count);
Bob Halley's avatar
Bob Halley committed
669 670
}

671 672 673
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
674
{
675 676 677 678
	/*
	 * Purge events from a task's event queue.
	 */

679 680
	XTRACE("isc_task_purge");

681
	return (isc__task_purgerange(task, sender, type, type, tag));
682 683
}

684 685 686
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
687 688 689 690
	isc_event_t *curr_event, *next_event;

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

	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) {
711
		next_event = NEXT(curr_event, ev_link);
712
		if (curr_event == event && PURGE_OK(event)) {
713
			DEQUEUE(task->events, curr_event, ev_link);
Bob Halley's avatar
Bob Halley committed
714 715 716 717 718 719 720 721 722 723 724 725 726
			break;
		}
	}
	UNLOCK(&task->lock);

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

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

	XTRACE("isc_task_unsendrange");

738 739
	return (dequeue_events((isc__task_t *)task, sender, first,
			       last, tag, events, ISC_FALSE));
740 741
}

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

	XTRACE("isc_task_unsend");

752 753
	return (dequeue_events((isc__task_t *)task, sender, type,
			       type, tag, events, ISC_FALSE));
754 755
}

756 757 758
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
759
{
760
	isc__task_t *task = (isc__task_t *)task0;
761 762 763 764
	isc_boolean_t disallowed = ISC_FALSE;
	isc_result_t result = ISC_R_SUCCESS;
	isc_event_t *event;

765 766 767 768 769
	/*
	 * Send a shutdown event with action 'action' and argument 'arg' when
	 * 'task' is shutdown.
	 */

770
	REQUIRE(VALID_TASK(task));
771
	REQUIRE(action != NULL);
772

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

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

	if (disallowed)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
791
		isc_mem_put(task->manager->mctx, event, sizeof(*event));
792 793 794 795

	return (result);
}

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

801 802 803 804
	/*
	 * Shutdown 'task'.
	 */

Bob Halley's avatar
base  
Bob Halley committed
805 806 807
	REQUIRE(VALID_TASK(task));

	LOCK(&task->lock);
808
	was_idle = task_shutdown(task);
Bob Halley's avatar
base  
Bob Halley committed
809 810
	UNLOCK(&task->lock);

811 812
	if (was_idle)
		task_ready(task);
Bob Halley's avatar
Bob Halley committed
813
}
Bob Halley's avatar
base  
Bob Halley committed
814

815 816
ISC_TASKFUNC_SCOPE void
isc__task_destroy(isc_task_t **taskp) {
Bob Halley's avatar
Bob Halley committed
817

818 819 820 821
	/*
	 * Destroy '*taskp'.
	 */

Bob Halley's avatar
Bob Halley committed
822 823
	REQUIRE(taskp != NULL);

Bob Halley's avatar
Bob Halley committed
824 825
	isc_task_shutdown(*taskp);
	isc_task_detach(taskp);
Bob Halley's avatar
base  
Bob Halley committed
826 827
}

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

	/*
	 * 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
844

845 846 847 848 849 850
ISC_TASKFUNC_SCOPE const char *
isc__task_getname(isc_task_t *task0) {
	isc__task_t *task = (isc__task_t *)task0;

	REQUIRE(VALID_TASK(task));

851 852 853
	return (task->name);
}

854 855 856 857 858 859
ISC_TASKFUNC_SCOPE void *
isc__task_gettag(isc_task_t *task0) {
	isc__task_t *task = (isc__task_t *)task0;

	REQUIRE(VALID_TASK(task));

860 861 862
	return (task->tag);
}

863 864 865 866
ISC_TASKFUNC_SCOPE void
isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t) {
	isc__task_t *task = (isc__task_t *)task0;

867 868 869 870 871 872 873 874 875
	REQUIRE(VALID_TASK(task));
	REQUIRE(t != NULL);

	LOCK(&task->lock);

	*t = task->now;

	UNLOCK(&task->lock);
}
876

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

	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.
893
	 *
Bob Halley's avatar
base  
Bob Halley committed
894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920
	 * 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.
921
	 *
Bob Halley's avatar
base  
Bob Halley committed
922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939
	 * 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.
	 */

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

Bob Halley's avatar
base  
Bob Halley committed
973 974
		task = HEAD(manager->ready_tasks);
		if (task != NULL) {
Bob Halley's avatar
Bob Halley committed
975
			unsigned int dispatch_count = 0;
Bob Halley's avatar
Bob Halley committed
976 977
			isc_boolean_t done = ISC_FALSE;
			isc_boolean_t requeue = ISC_FALSE;
978
			isc_boolean_t finished = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
979
			isc_event_t *event;
Bob Halley's avatar
base  
Bob Halley committed
980 981 982 983 984

			INSIST(VALID_TASK(task));

			/*
			 * Note we only unlock the manager lock if we actually
985
			 * have a task to do.  We must reacquire the manager
Bob Halley's avatar
base  
Bob Halley committed
986 987 988
			 * lock before exiting the 'if (task != NULL)' block.
			 */
			DEQUEUE(manager->ready_tasks, task, ready_link);
989
			manager->tasks_running++;
Bob Halley's avatar
base  
Bob Halley committed
990 991 992
			UNLOCK(&manager->lock);

			LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
993
			INSIST(task->state == task_state_ready);
994
			task->state = task_state_running;
995 996
			XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
					      ISC_MSG_RUNNING, "running"));
997
			isc_stdtime_get(&task->now);
998 999 1000
			do {
				if (!EMPTY(task->events)) {
					event = HEAD(task->events);
1001
					DEQUEUE(task->events, event, ev_link);
Bob Halley's avatar
base  
Bob Halley committed
1002

1003 1004 1005
					/*
					 * Execute the event action.
					 */
1006 1007 1008 1009
					XTRACE(isc_msgcat_get(isc_msgcat,
							    ISC_MSGSET_TASK,
							    ISC_MSG_EXECUTE,
							    "execute action"));
1010
					if (event->ev_action != NULL) {
1011
						UNLOCK(&task->lock);
1012 1013 1014
						(event->ev_action)(
							(isc_task_t *)task,
							event);
1015 1016 1017
						LOCK(&task->lock);
					}
					dispatch_count++;
1018
#ifndef USE_WORKER_THREADS
1019
					total_dispatch_count++;
1020
#endif /* USE_WORKER_THREADS */
1021
				}
1022

Bob Halley's avatar
Bob Halley committed
1023 1024 1025 1026
				if (task->references == 0 &&
				    EMPTY(task->events) &&
				    !TASK_SHUTTINGDOWN(task)) {
					isc_boolean_t was_idle;
1027

Bob Halley's avatar
Bob Halley committed
1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041