task.c 39.2 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.114 2010/12/04 13:25:59 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 237 238
	void *purgeevent, *unsendrange,
		*getname, *gettag, *getcurrenttime, *beginexclusive,
		*endexclusive;
239
#endif
240 241 242 243 244 245 246 247 248 249 250 251 252
} 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
253 254 255 256 257 258 259 260
	}
#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
261 262 263 264 265 266
};

static isc_taskmgrmethods_t taskmgrmethods = {
	isc__taskmgr_destroy,
	isc__task_create
};
267

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

373
	XTTRACE(source, "isc_task_attach");
374

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

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

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

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

	XTRACE("task_shutdown");

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

	return (was_idle);
}

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

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

	XTRACE("task_ready");

	LOCK(&manager->lock);

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

	UNLOCK(&manager->lock);
}

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

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

	REQUIRE(task->references > 0);

	XTRACE("detach");

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

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

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

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

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

480 481
	XTRACE("isc_task_detach");

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

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

	*taskp = NULL;
}

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

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

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

507 508
	XTRACE("task_send");

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	LOCK(&task->lock);
622 623

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

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

637 638 639
	return (count);
}

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

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

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

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

680 681
	XTRACE("isc_task_purge");

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

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

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

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

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

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

	XTRACE("isc_task_unsendrange");

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

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

	XTRACE("isc_task_unsend");

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

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

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

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

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

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

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

	return (result);
}

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

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

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

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

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

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

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

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

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

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

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

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

	REQUIRE(VALID_TASK(task));

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

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

	REQUIRE(VALID_TASK(task));

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

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

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

	LOCK(&task->lock);

	*t = task->now;

	UNLOCK(&task->lock);
}
877

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

	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.
894
	 *
Bob Halley's avatar
base  
Bob Halley committed
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 921
	 * 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.
922
	 *
Bob Halley's avatar
base  
Bob Halley committed
923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940
	 * 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.
	 */

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

Bob Halley's avatar
base  
Bob Halley committed