task.c 33.2 KB
Newer Older
Bob Halley's avatar
Bob Halley committed
1
/*
Mark Andrews's avatar
Mark Andrews committed
2
 * Copyright (C) 2004-2007  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Copyright (C) 1998-2003  Internet Software Consortium.
4
 *
Bob Halley's avatar
Bob Halley committed
5 6 7
 * Permission to use, copy, modify, and distribute this software for any
 * 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.103 2007/02/13 02:49:08 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

43
#ifndef ISC_PLATFORM_USETHREADS
44
#include "task_p.h"
45
#endif /* ISC_PLATFORM_USETHREADS */
46

47 48
#define ISC_TASK_NAMES 1

Bob Halley's avatar
Bob Halley committed
49
#ifdef ISC_TASK_TRACE
50
#define XTRACE(m)		fprintf(stderr, "task %p thread %lu: %s\n", \
51
				       task, isc_thread_self(), (m))
52
#define XTTRACE(t, m)		fprintf(stderr, "task %p thread %lu: %s\n", \
53
				       (t), isc_thread_self(), (m))
54
#define XTHREADTRACE(m)		fprintf(stderr, "thread %lu: %s\n", \
55
				       isc_thread_self(), (m))
Bob Halley's avatar
Bob Halley committed
56 57
#else
#define XTRACE(m)
58
#define XTTRACE(t, m)
59
#define XTHREADTRACE(m)
Bob Halley's avatar
Bob Halley committed
60
#endif
Bob Halley's avatar
base  
Bob Halley committed
61

Bob Halley's avatar
Bob Halley committed
62
/***
Bob Halley's avatar
Bob Halley committed
63
 *** Types.
Bob Halley's avatar
Bob Halley committed
64 65
 ***/

Bob Halley's avatar
Bob Halley committed
66 67
typedef enum {
	task_state_idle, task_state_ready, task_state_running,
68
	task_state_done
Bob Halley's avatar
Bob Halley committed
69 70
} task_state_t;

71 72 73 74
static const char *statenames[] = {
	"idle", "ready", "running", "done",
};

75 76
#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
77

Bob Halley's avatar
Bob Halley committed
78
struct isc_task {
Bob Halley's avatar
Bob Halley committed
79 80
	/* Not locked. */
	unsigned int			magic;
Bob Halley's avatar
Bob Halley committed
81
	isc_taskmgr_t *			manager;
Bob Halley's avatar
Bob Halley committed
82
	isc_mutex_t			lock;
Bob Halley's avatar
Bob Halley committed
83 84 85
	/* Locked by task lock. */
	task_state_t			state;
	unsigned int			references;
Bob Halley's avatar
Bob Halley committed
86
	isc_eventlist_t			events;
87
	isc_eventlist_t			on_shutdown;
Bob Halley's avatar
Bob Halley committed
88
	unsigned int			quantum;
89
	unsigned int			flags;
90
	isc_stdtime_t			now;
91 92 93 94
#ifdef ISC_TASK_NAMES
	char				name[16];
	void *				tag;
#endif
Bob Halley's avatar
Bob Halley committed
95
	/* Locked by task manager lock. */
Bob Halley's avatar
Bob Halley committed
96 97
	LINK(isc_task_t)		link;
	LINK(isc_task_t)		ready_link;
Bob Halley's avatar
Bob Halley committed
98 99
};

Bob Halley's avatar
Bob Halley committed
100
#define TASK_F_SHUTTINGDOWN		0x01
101

102 103
#define TASK_SHUTTINGDOWN(t)		(((t)->flags & TASK_F_SHUTTINGDOWN) \
					 != 0)
104

105 106
#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
107

Bob Halley's avatar
Bob Halley committed
108
struct isc_taskmgr {
Bob Halley's avatar
Bob Halley committed
109 110
	/* Not locked. */
	unsigned int			magic;
Bob Halley's avatar
Bob Halley committed
111
	isc_mem_t *			mctx;
Bob Halley's avatar
Bob Halley committed
112
	isc_mutex_t			lock;
113
#ifdef ISC_PLATFORM_USETHREADS
114
	unsigned int			workers;
115
	isc_thread_t *			threads;
116
#endif /* ISC_PLATFORM_USETHREADS */
Bob Halley's avatar
Bob Halley committed
117 118
	/* Locked by task manager lock. */
	unsigned int			default_quantum;
Bob Halley's avatar
Bob Halley committed
119
	LIST(isc_task_t)		tasks;
120
	isc_tasklist_t			ready_tasks;
121
#ifdef ISC_PLATFORM_USETHREADS
Bob Halley's avatar
Bob Halley committed
122
	isc_condition_t			work_available;
123
	isc_condition_t			exclusive_granted;
124
#endif /* ISC_PLATFORM_USETHREADS */
125 126
	unsigned int			tasks_running;
	isc_boolean_t			exclusive_requested;
Bob Halley's avatar
Bob Halley committed
127
	isc_boolean_t			exiting;
128 129
#ifndef ISC_PLATFORM_USETHREADS
	unsigned int			refs;
130
#endif /* ISC_PLATFORM_USETHREADS */
Bob Halley's avatar
Bob Halley committed
131
};
Bob Halley's avatar
Bob Halley committed
132

133
#define DEFAULT_TASKMGR_QUANTUM		10
Bob Halley's avatar
Bob Halley committed
134 135
#define DEFAULT_DEFAULT_QUANTUM		5
#define FINISHED(m)			((m)->exiting && EMPTY((m)->tasks))
Bob Halley's avatar
Bob Halley committed
136

137 138
#ifndef ISC_PLATFORM_USETHREADS
static isc_taskmgr_t *taskmgr = NULL;
139
#endif /* ISC_PLATFORM_USETHREADS */
140

Bob Halley's avatar
base  
Bob Halley committed
141 142 143 144 145
/***
 *** Tasks.
 ***/

static void
146
task_finished(isc_task_t *task) {
Bob Halley's avatar
Bob Halley committed
147
	isc_taskmgr_t *manager = task->manager;
Bob Halley's avatar
base  
Bob Halley committed
148 149

	REQUIRE(EMPTY(task->events));
150
	REQUIRE(EMPTY(task->on_shutdown));
151 152 153 154
	REQUIRE(task->references == 0);
	REQUIRE(task->state == task_state_done);

	XTRACE("task_finished");
Bob Halley's avatar
base  
Bob Halley committed
155 156 157

	LOCK(&manager->lock);
	UNLINK(manager->tasks, task, link);
158
#ifdef ISC_PLATFORM_USETHREADS
Bob Halley's avatar
base  
Bob Halley committed
159 160 161 162 163 164 165 166 167
	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);
	}
168
#endif /* ISC_PLATFORM_USETHREADS */
Bob Halley's avatar
base  
Bob Halley committed
169
	UNLOCK(&manager->lock);
170

Brian Wellington's avatar
Brian Wellington committed
171
	DESTROYLOCK(&task->lock);
Bob Halley's avatar
base  
Bob Halley committed
172
	task->magic = 0;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
173
	isc_mem_put(manager->mctx, task, sizeof(*task));
Bob Halley's avatar
base  
Bob Halley committed
174 175
}

Bob Halley's avatar
Bob Halley committed
176
isc_result_t
177
isc_task_create(isc_taskmgr_t *manager, unsigned int quantum,
178
		isc_task_t **taskp)
Bob Halley's avatar
Bob Halley committed
179
{
Bob Halley's avatar
Bob Halley committed
180
	isc_task_t *task;
181
	isc_boolean_t exiting;
182
	isc_result_t result;
Bob Halley's avatar
base  
Bob Halley committed
183 184 185 186

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

Andreas Gustafsson's avatar
Andreas Gustafsson committed
187
	task = isc_mem_get(manager->mctx, sizeof(*task));
Bob Halley's avatar
base  
Bob Halley committed
188
	if (task == NULL)
Bob Halley's avatar
Bob Halley committed
189
		return (ISC_R_NOMEMORY);
190
	XTRACE("isc_task_create");
Bob Halley's avatar
base  
Bob Halley committed
191
	task->manager = manager;
192 193
	result = isc_mutex_init(&task->lock);
	if (result != ISC_R_SUCCESS) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
194
		isc_mem_put(manager->mctx, task, sizeof(*task));
195
		return (result);
Bob Halley's avatar
update  
Bob Halley committed
196
	}
Bob Halley's avatar
base  
Bob Halley committed
197 198 199
	task->state = task_state_idle;
	task->references = 1;
	INIT_LIST(task->events);
200
	INIT_LIST(task->on_shutdown);
Bob Halley's avatar
base  
Bob Halley committed
201
	task->quantum = quantum;
Bob Halley's avatar
Bob Halley committed
202
	task->flags = 0;
203
	task->now = 0;
204
#ifdef ISC_TASK_NAMES
Andreas Gustafsson's avatar
Andreas Gustafsson committed
205
	memset(task->name, 0, sizeof(task->name));
206 207
	task->tag = NULL;
#endif
Bob Halley's avatar
base  
Bob Halley committed
208 209 210
	INIT_LINK(task, link);
	INIT_LINK(task, ready_link);

211
	exiting = ISC_FALSE;
Bob Halley's avatar
base  
Bob Halley committed
212
	LOCK(&manager->lock);
213 214 215 216 217 218
	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
219 220
	UNLOCK(&manager->lock);

221
	if (exiting) {
Brian Wellington's avatar
Brian Wellington committed
222
		DESTROYLOCK(&task->lock);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
223
		isc_mem_put(manager->mctx, task, sizeof(*task));
224 225 226
		return (ISC_R_SHUTTINGDOWN);
	}

227
	task->magic = TASK_MAGIC;
Bob Halley's avatar
base  
Bob Halley committed
228 229
	*taskp = task;

Bob Halley's avatar
Bob Halley committed
230
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
231 232
}

Bob Halley's avatar
update  
Bob Halley committed
233
void
234
isc_task_attach(isc_task_t *source, isc_task_t **targetp) {
Bob Halley's avatar
base  
Bob Halley committed
235

236 237 238
	/*
	 * Attach *targetp to source.
	 */
Bob Halley's avatar
base  
Bob Halley committed
239

240 241
	REQUIRE(VALID_TASK(source));
	REQUIRE(targetp != NULL && *targetp == NULL);
Bob Halley's avatar
base  
Bob Halley committed
242

243
	XTTRACE(source, "isc_task_attach");
244

245 246 247 248 249
	LOCK(&source->lock);
	source->references++;
	UNLOCK(&source->lock);

	*targetp = source;
Bob Halley's avatar
base  
Bob Halley committed
250 251
}

252 253 254 255
static inline isc_boolean_t
task_shutdown(isc_task_t *task) {
	isc_boolean_t was_idle = ISC_FALSE;
	isc_event_t *event, *prev;
256

257 258 259 260 261 262 263
	/*
	 * Caller must be holding the task's lock.
	 */

	XTRACE("task_shutdown");

	if (! TASK_SHUTTINGDOWN(task)) {
264 265
		XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
				      ISC_MSG_SHUTTINGDOWN, "shutting down"));
266 267 268 269 270 271 272 273 274 275 276 277 278 279
		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) {
280 281 282
			prev = PREV(event, ev_link);
			DEQUEUE(task->on_shutdown, event, ev_link);
			ENQUEUE(task->events, event, ev_link);
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
		}
	}

	return (was_idle);
}

static inline void
task_ready(isc_task_t *task) {
	isc_taskmgr_t *manager = task->manager;

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

	XTRACE("task_ready");

	LOCK(&manager->lock);

	ENQUEUE(manager->ready_tasks, task, ready_link);
301
#ifdef ISC_PLATFORM_USETHREADS
302
	SIGNAL(&manager->work_available);
303
#endif /* ISC_PLATFORM_USETHREADS */
304 305 306 307

	UNLOCK(&manager->lock);
}

Bob Halley's avatar
Bob Halley committed
308
static inline isc_boolean_t
309 310 311 312 313 314 315 316 317 318 319
task_detach(isc_task_t *task) {

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

	REQUIRE(task->references > 0);

	XTRACE("detach");

	task->references--;
Bob Halley's avatar
Bob Halley committed
320 321 322 323 324 325 326
	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
327 328
		 * 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
329 330 331
		 */
		task->state = task_state_ready;
		return (ISC_TRUE);
332 333
	}

Bob Halley's avatar
Bob Halley committed
334
	return (ISC_FALSE);
335 336
}

Bob Halley's avatar
Bob Halley committed
337
void
Bob Halley's avatar
Bob Halley committed
338 339
isc_task_detach(isc_task_t **taskp) {
	isc_task_t *task;
Bob Halley's avatar
Bob Halley committed
340
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
341

342 343 344 345
	/*
	 * Detach *taskp from its task.
	 */

Bob Halley's avatar
base  
Bob Halley committed
346 347 348 349
	REQUIRE(taskp != NULL);
	task = *taskp;
	REQUIRE(VALID_TASK(task));

350 351
	XTRACE("isc_task_detach");

Bob Halley's avatar
base  
Bob Halley committed
352
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
353
	was_idle = task_detach(task);
Bob Halley's avatar
base  
Bob Halley committed
354 355
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
356
	if (was_idle)
357
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
358 359 360 361

	*taskp = NULL;
}

Bob Halley's avatar
Bob Halley committed
362 363 364
static inline isc_boolean_t
task_send(isc_task_t *task, isc_event_t **eventp) {
	isc_boolean_t was_idle = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
365
	isc_event_t *event;
366

367
	/*
368
	 * Caller must be holding the task lock.
369 370
	 */

Bob Halley's avatar
update  
Bob Halley committed
371 372
	REQUIRE(eventp != NULL);
	event = *eventp;
Bob Halley's avatar
base  
Bob Halley committed
373
	REQUIRE(event != NULL);
374
	REQUIRE(event->ev_type > 0);
Bob Halley's avatar
Bob Halley committed
375
	REQUIRE(task->state != task_state_done);
Bob Halley's avatar
base  
Bob Halley committed
376

377 378
	XTRACE("task_send");

Bob Halley's avatar
Bob Halley committed
379 380 381 382
	if (task->state == task_state_idle) {
		was_idle = ISC_TRUE;
		INSIST(EMPTY(task->events));
		task->state = task_state_ready;
383
	}
Bob Halley's avatar
Bob Halley committed
384 385
	INSIST(task->state == task_state_ready ||
	       task->state == task_state_running);
386
	ENQUEUE(task->events, event, ev_link);
Bob Halley's avatar
Bob Halley committed
387
	*eventp = NULL;
388

Bob Halley's avatar
Bob Halley committed
389
	return (was_idle);
390
}
391

Bob Halley's avatar
Bob Halley committed
392
void
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
isc_task_send(isc_task_t *task, isc_event_t **eventp) {
	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
410
	was_idle = task_send(task, eventp);
Bob Halley's avatar
base  
Bob Halley committed
411 412 413 414 415 416
	UNLOCK(&task->lock);

	if (was_idle) {
		/*
		 * We need to add this task to the ready queue.
		 *
417 418 419
		 * 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
420 421
		 *
		 * We've changed the state to ready, so no one else will
422 423 424
		 * 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,
425
		 * removed, or a shutdown is started in the interval
426 427
		 * 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
428
		 */
429
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
430
	}
431 432
}

Bob Halley's avatar
Bob Halley committed
433
void
434
isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
Bob Halley's avatar
Bob Halley committed
435
	isc_boolean_t idle1, idle2;
436 437 438 439 440 441 442 443 444 445 446 447
	isc_task_t *task;

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

	REQUIRE(taskp != NULL);
	task = *taskp;
	REQUIRE(VALID_TASK(task));

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

449
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
450 451
	idle1 = task_send(task, eventp);
	idle2 = task_detach(task);
452 453
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
454 455 456 457 458 459
	/*
	 * 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));
460

Bob Halley's avatar
Bob Halley committed
461 462
	if (idle1 || idle2)
		task_ready(task);
463 464

	*taskp = NULL;
Bob Halley's avatar
base  
Bob Halley committed
465 466
}

467
#define PURGE_OK(event)	(((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0)
468 469 470 471 472

static unsigned int
dequeue_events(isc_task_t *task, void *sender, isc_eventtype_t first,
	       isc_eventtype_t last, void *tag,
	       isc_eventlist_t *events, isc_boolean_t purging)
473
{
Bob Halley's avatar
Bob Halley committed
474
	isc_event_t *event, *next_event;
475
	unsigned int count = 0;
476

Bob Halley's avatar
Bob Halley committed
477
	REQUIRE(VALID_TASK(task));
478
	REQUIRE(last >= first);
Bob Halley's avatar
Bob Halley committed
479

480
	XTRACE("dequeue_events");
Bob Halley's avatar
Bob Halley committed
481

Bob Halley's avatar
Bob Halley committed
482
	/*
483 484 485
	 * 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.
486
	 *
487
	 * sender == NULL means "any sender", and tag == NULL means "any tag".
Bob Halley's avatar
Bob Halley committed
488 489 490
	 */

	LOCK(&task->lock);
491 492

	for (event = HEAD(task->events); event != NULL; event = next_event) {
493 494 495 496
		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) &&
497
		    (!purging || PURGE_OK(event))) {
498 499
			DEQUEUE(task->events, event, ev_link);
			ENQUEUE(*events, event, ev_link);
500
			count++;
Bob Halley's avatar
Bob Halley committed
501 502
		}
	}
503

Bob Halley's avatar
Bob Halley committed
504 505
	UNLOCK(&task->lock);

506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
	return (count);
}

unsigned int
isc_task_purgerange(isc_task_t *task, void *sender, isc_eventtype_t first,
		    isc_eventtype_t last, void *tag)
{
	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) {
529
		next_event = NEXT(event, ev_link);
Bob Halley's avatar
Bob Halley committed
530
		isc_event_free(&event);
Bob Halley's avatar
Bob Halley committed
531
	}
532

533 534 535 536 537
	/*
	 * Note that purging never changes the state of the task.
	 */

	return (count);
Bob Halley's avatar
Bob Halley committed
538 539
}

540
unsigned int
Bob Halley's avatar
Bob Halley committed
541
isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
542
	       void *tag)
Bob Halley's avatar
Bob Halley committed
543
{
544 545 546 547
	/*
	 * Purge events from a task's event queue.
	 */

548 549
	XTRACE("isc_task_purge");

Bob Halley's avatar
Bob Halley committed
550
	return (isc_task_purgerange(task, sender, type, type, tag));
551 552
}

Bob Halley's avatar
Bob Halley committed
553 554 555 556 557 558
isc_boolean_t
isc_task_purgeevent(isc_task_t *task, isc_event_t *event) {
	isc_event_t *curr_event, *next_event;

	/*
	 * Purge 'event' from a task's event queue.
559 560
	 *
	 * XXXRTH:  WARNING:  This method may be removed before beta.
Bob Halley's avatar
Bob Halley committed
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
	 */

	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) {
579
		next_event = NEXT(curr_event, ev_link);
580
		if (curr_event == event && PURGE_OK(event)) {
581
			DEQUEUE(task->events, curr_event, ev_link);
Bob Halley's avatar
Bob Halley committed
582 583 584 585 586 587 588 589 590 591 592 593 594
			break;
		}
	}
	UNLOCK(&task->lock);

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
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)
{
	/*
	 * Remove events from a task's event queue.
	 */

	XTRACE("isc_task_unsendrange");

	return (dequeue_events(task, sender, first, last, tag, events,
			       ISC_FALSE));
}

unsigned int
isc_task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type,
		void *tag, isc_eventlist_t *events)
{
	/*
	 * Remove events from a task's event queue.
	 */

	XTRACE("isc_task_unsend");

	return (dequeue_events(task, sender, type, type, tag, events,
			       ISC_FALSE));
}

624
isc_result_t
David Lawrence's avatar
David Lawrence committed
625 626
isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, const void *arg)
{
627 628 629 630
	isc_boolean_t disallowed = ISC_FALSE;
	isc_result_t result = ISC_R_SUCCESS;
	isc_event_t *event;

631 632 633 634 635
	/*
	 * Send a shutdown event with action 'action' and argument 'arg' when
	 * 'task' is shutdown.
	 */

636
	REQUIRE(VALID_TASK(task));
637
	REQUIRE(action != NULL);
638

639
	event = isc_event_allocate(task->manager->mctx,
640 641 642 643
				   NULL,
				   ISC_TASKEVENT_SHUTDOWN,
				   action,
				   arg,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
644
				   sizeof(*event));
645 646 647 648
	if (event == NULL)
		return (ISC_R_NOMEMORY);

	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
649
	if (TASK_SHUTTINGDOWN(task)) {
650
		disallowed = ISC_TRUE;
651
		result = ISC_R_SHUTTINGDOWN;
652
	} else
653
		ENQUEUE(task->on_shutdown, event, ev_link);
654 655 656
	UNLOCK(&task->lock);

	if (disallowed)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
657
		isc_mem_put(task->manager->mctx, event, sizeof(*event));
658 659 660 661

	return (result);
}

Bob Halley's avatar
Bob Halley committed
662
void
Bob Halley's avatar
Bob Halley committed
663
isc_task_shutdown(isc_task_t *task) {
664
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
665

666 667 668 669
	/*
	 * Shutdown 'task'.
	 */

Bob Halley's avatar
base  
Bob Halley committed
670 671 672
	REQUIRE(VALID_TASK(task));

	LOCK(&task->lock);
673
	was_idle = task_shutdown(task);
Bob Halley's avatar
base  
Bob Halley committed
674 675
	UNLOCK(&task->lock);

676 677
	if (was_idle)
		task_ready(task);
Bob Halley's avatar
Bob Halley committed
678
}
Bob Halley's avatar
base  
Bob Halley committed
679

Bob Halley's avatar
Bob Halley committed
680
void
Bob Halley's avatar
Bob Halley committed
681
isc_task_destroy(isc_task_t **taskp) {
Bob Halley's avatar
Bob Halley committed
682

683 684 685 686
	/*
	 * Destroy '*taskp'.
	 */

Bob Halley's avatar
Bob Halley committed
687 688
	REQUIRE(taskp != NULL);

Bob Halley's avatar
Bob Halley committed
689 690
	isc_task_shutdown(*taskp);
	isc_task_detach(taskp);
Bob Halley's avatar
base  
Bob Halley committed
691 692
}

693
void
David Lawrence's avatar
David Lawrence committed
694
isc_task_setname(isc_task_t *task, const char *name, void *tag) {
695 696 697 698 699 700 701 702 703 704 705 706 707 708

	/*
	 * Name 'task'.
	 */

	REQUIRE(VALID_TASK(task));

#ifdef ISC_TASK_NAMES
	LOCK(&task->lock);
	memset(task->name, 0, sizeof(task->name));
	strncpy(task->name, name, sizeof(task->name) - 1);
	task->tag = tag;
	UNLOCK(&task->lock);
#else
Andreas Gustafsson's avatar
Andreas Gustafsson committed
709 710
	UNUSED(name);
	UNUSED(tag);
711
#endif
Bob Halley's avatar
base  
Bob Halley committed
712

713
}
Bob Halley's avatar
Bob Halley committed
714

715 716 717 718 719 720 721 722 723 724
const char *
isc_task_getname(isc_task_t *task) {
	return (task->name);
}

void *
isc_task_gettag(isc_task_t *task) {
	return (task->tag);
}

725 726 727 728 729 730 731 732 733 734 735
void
isc_task_getcurrenttime(isc_task_t *task, isc_stdtime_t *t) {
	REQUIRE(VALID_TASK(task));
	REQUIRE(t != NULL);

	LOCK(&task->lock);

	*t = task->now;

	UNLOCK(&task->lock);
}
736

Bob Halley's avatar
base  
Bob Halley committed
737 738 739
/***
 *** Task Manager.
 ***/
740 741
static void
dispatch(isc_taskmgr_t *manager) {
Bob Halley's avatar
Bob Halley committed
742
	isc_task_t *task;
743 744
#ifndef ISC_PLATFORM_USETHREADS
	unsigned int total_dispatch_count = 0;
745
	isc_tasklist_t ready_tasks;
746
#endif /* ISC_PLATFORM_USETHREADS */
Bob Halley's avatar
base  
Bob Halley committed
747 748 749 750 751 752

	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.
753
	 *
Bob Halley's avatar
base  
Bob Halley committed
754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
	 * 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.
781
	 *
Bob Halley's avatar
base  
Bob Halley committed
782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799
	 * 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.
	 */

800 801 802
#ifndef ISC_PLATFORM_USETHREADS
	ISC_LIST_INIT(ready_tasks);
#endif
Bob Halley's avatar
base  
Bob Halley committed
803 804
	LOCK(&manager->lock);
	while (!FINISHED(manager)) {
805
#ifdef ISC_PLATFORM_USETHREADS
Bob Halley's avatar
base  
Bob Halley committed
806 807
		/*
		 * For reasons similar to those given in the comment in
808
		 * isc_task_send() above, it is safe for us to dequeue
Bob Halley's avatar
base  
Bob Halley committed
809 810 811 812
		 * the task while only holding the manager lock, and then
		 * change the task to running state while only holding the
		 * task lock.
		 */
813 814 815 816
		while ((EMPTY(manager->ready_tasks) ||
		        manager->exclusive_requested) &&
		  	!FINISHED(manager)) 
	  	{
817 818 819
			XTHREADTRACE(isc_msgcat_get(isc_msgcat,
						    ISC_MSGSET_GENERAL,
						    ISC_MSG_WAIT, "wait"));
Bob Halley's avatar
base  
Bob Halley committed
820
			WAIT(&manager->work_available, &manager->lock);
821 822 823
			XTHREADTRACE(isc_msgcat_get(isc_msgcat,
						    ISC_MSGSET_TASK,
						    ISC_MSG_AWAKE, "awake"));
Bob Halley's avatar
base  
Bob Halley committed
824
		}
825
#else /* ISC_PLATFORM_USETHREADS */
826 827 828
		if (total_dispatch_count >= DEFAULT_TASKMGR_QUANTUM ||
		    EMPTY(manager->ready_tasks))
			break;
829
#endif /* ISC_PLATFORM_USETHREADS */
830 831
		XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK,
					    ISC_MSG_WORKING, "working"));
832

Bob Halley's avatar
base  
Bob Halley committed
833 834
		task = HEAD(manager->ready_tasks);
		if (task != NULL) {
Bob Halley's avatar
Bob Halley committed
835
			unsigned int dispatch_count = 0;
Bob Halley's avatar
Bob Halley committed
836 837
			isc_boolean_t done = ISC_FALSE;
			isc_boolean_t requeue = ISC_FALSE;
838
			isc_boolean_t finished = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
839
			isc_event_t *event;
Bob Halley's avatar
base  
Bob Halley committed
840 841 842 843 844

			INSIST(VALID_TASK(task));

			/*
			 * Note we only unlock the manager lock if we actually
845
			 * have a task to do.  We must reacquire the manager
Bob Halley's avatar
base  
Bob Halley committed
846 847 848
			 * lock before exiting the 'if (task != NULL)' block.
			 */
			DEQUEUE(manager->ready_tasks, task, ready_link);
849
			manager->tasks_running++;
Bob Halley's avatar
base  
Bob Halley committed
850 851 852
			UNLOCK(&manager->lock);

			LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
853
			INSIST(task->state == task_state_ready);
854
			task->state = task_state_running;
855 856
			XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
					      ISC_MSG_RUNNING, "running"));
857
			isc_stdtime_get(&task->now);
858 859 860
			do {
				if (!EMPTY(task->events)) {
					event = HEAD(task->events);
861
					DEQUEUE(task->events, event, ev_link);
Bob Halley's avatar
base  
Bob Halley committed
862

863 864 865
					/*
					 * Execute the event action.
					 */
866 867 868 869
					XTRACE(isc_msgcat_get(isc_msgcat,
							    ISC_MSGSET_TASK,
							    ISC_MSG_EXECUTE,
							    "execute action"));
870
					if (event->ev_action != NULL) {
871
						UNLOCK(&task->lock);
872
						(event->ev_action)(task,event);
873 874 875
						LOCK(&task->lock);
					}
					dispatch_count++;
876 877
#ifndef ISC_PLATFORM_USETHREADS
					total_dispatch_count++;
878
#endif /* ISC_PLATFORM_USETHREADS */
879
				}
880

Bob Halley's avatar
Bob Halley committed
881 882 883 884
				if (task->references == 0 &&
				    EMPTY(task->events) &&
				    !TASK_SHUTTINGDOWN(task)) {
					isc_boolean_t was_idle;
885

Bob Halley's avatar
Bob Halley committed
886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909
					/*
					 * There are no references and no
					 * pending events for this task,
					 * which means it will not become
					 * runnable again via an external
					 * action (such as sending an event
					 * or detaching).
					 *
					 * We initiate shutdown to prevent
					 * it from becoming a zombie.
					 *
					 * We do this here instead of in
					 * the "if EMPTY(task->events)" block
					 * below because:
					 *
					 *	If we post no shutdown events,
					 *	we want the task to finish.
					 *
					 *	If we did post shutdown events,
					 *	will still want the task's
					 *	quantum to be applied.
					 */
					was_idle = task_shutdown(task);
					INSIST(!was_idle);
910 911
				}

912
				if (EMPTY(task->events)) {
Bob Halley's avatar
Bob Halley committed
913
					/*
914
					 * Nothing else to do for this task
Bob Halley's avatar
Bob Halley committed
915
					 * right now.
Bob Halley's avatar
Bob Halley committed
916
					 */
917 918 919 920
					XTRACE(isc_msgcat_get(isc_msgcat,
							      ISC_MSGSET_TASK,
							      ISC_MSG_EMPTY,
							      "empty"));
Bob Halley's avatar
Bob Halley committed
921 922 923 924 925
					if (task->references == 0 &&
					    TASK_SHUTTINGDOWN(task)) {
						/*
						 * The task is done.
						 */
926 927 928 929 930
						XTRACE(isc_msgcat_get(
							       isc_msgcat,
							       ISC_MSGSET_TASK,
							       ISC_MSG_DONE,
							       "done"));
Bob Halley's avatar
Bob Halley committed
931
						finished = ISC_TRUE;
932
						task->state = task_state_done;
933 934
					} else
						task->state = task_state_idle;
Bob Halley's avatar
Bob Halley committed
935
					done = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
936 937 938 939 940 941 942 943 944 945 946
				} else if (dispatch_count >= task->quantum) {
					/*
					 * Our quantum has expired, but
					 * there is more work to be done.
					 * We'll requeue it to the ready
					 * queue later.
					 *
					 * We don't check quantum until
					 * dispatching at least one event,
					 * so the minimum quantum is one.
					 */
947 948 949