task.c 33 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.100 2007/01/05 07:24:51 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>
Bob Halley's avatar
base  
Bob Halley committed
41

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

46 47
#define ISC_TASK_NAMES 1

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

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

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

70
#ifdef HAVE_LIBXML2
71 72 73
static const char *statenames[] = {
	"idle", "ready", "running", "done",
};
74
#endif
75

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

244
	XTTRACE(source, "isc_task_attach");
245

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

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

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

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

	XTRACE("task_shutdown");

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

	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);
302
#ifdef ISC_PLATFORM_USETHREADS
303
	SIGNAL(&manager->work_available);
304
#endif /* ISC_PLATFORM_USETHREADS */
305 306 307 308

	UNLOCK(&manager->lock);
}

Bob Halley's avatar
Bob Halley committed
309
static inline isc_boolean_t
310 311 312 313 314 315 316 317 318 319 320
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
321 322 323 324 325 326 327
	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
328 329
		 * 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
330 331 332
		 */
		task->state = task_state_ready;
		return (ISC_TRUE);
333 334
	}

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

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

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

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

351 352
	XTRACE("isc_task_detach");

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

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

	*taskp = NULL;
}

Bob Halley's avatar
Bob Halley committed
363 364 365
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
366
	isc_event_t *event;
367

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

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

378 379
	XTRACE("task_send");

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

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

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

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

Bob Halley's avatar
Bob Halley committed
434
void
435
isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
Bob Halley's avatar
Bob Halley committed
436
	isc_boolean_t idle1, idle2;
437 438 439 440 441 442 443 444 445 446 447 448
	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
449

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

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

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

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

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

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)
474
{
Bob Halley's avatar
Bob Halley committed
475
	isc_event_t *event, *next_event;
476
	unsigned int count = 0;
477

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

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

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

	LOCK(&task->lock);
492 493

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

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

507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
	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) {
530
		next_event = NEXT(event, ev_link);
Bob Halley's avatar
Bob Halley committed
531
		isc_event_free(&event);
Bob Halley's avatar
Bob Halley committed
532
	}
533

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

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

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

549 550
	XTRACE("isc_task_purge");

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

Bob Halley's avatar
Bob Halley committed
554 555 556 557 558 559
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.
560 561
	 *
	 * XXXRTH:  WARNING:  This method may be removed before beta.
Bob Halley's avatar
Bob Halley committed
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
	 */

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

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

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 624
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));
}

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

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

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

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

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

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

	return (result);
}

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

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

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

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

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

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

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

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

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

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

	/*
	 * 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
710 711
	UNUSED(name);
	UNUSED(tag);
712
#endif
Bob Halley's avatar
base  
Bob Halley committed
713

714
}
Bob Halley's avatar
Bob Halley committed
715

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

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

726 727 728 729 730 731 732 733 734 735 736
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);
}
737

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

	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.
754
	 *
Bob Halley's avatar
base  
Bob Halley committed
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 781
	 * 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.
782
	 *
Bob Halley's avatar
base  
Bob Halley committed
783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
	 * 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.
	 */

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

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

			INSIST(VALID_TASK(task));

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

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

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

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

Bob Halley's avatar
Bob Halley committed
887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910
					/*
					 * 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);
911 912
				}

913
				if (EMPTY(task->events)) {
Bob Halley's avatar
Bob Halley committed
914
					/*
915
					 * Nothing else to do for this task
Bob Halley's avatar
Bob Halley committed
916
					 * right now.
Bob Halley's avatar
Bob Halley committed
917
					 */
918 919 920 921
					XTRACE(isc_msgcat_get(isc_msgcat,
							      ISC_MSGSET_TASK,
							      ISC_MSG_EMPTY,
							      "empty"));
Bob Halley's avatar
Bob Halley committed
922 923 924 925 926
					if (task->references == 0 &&
					    TASK_SHUTTINGDOWN(task)) {
						/*
						 * The task is done.
						 */
927 928 929 930 931
						XTRACE(isc_msgcat_get(
							       isc_msgcat,
							       ISC_MSGSET_TASK,
							       ISC_MSG_DONE,
							       "done"));
Bob Halley's avatar
Bob Halley committed
932
						finished = ISC_TRUE;
933
						task->state = task_state_done;
934 935
					} else
						task->state = task_state_idle;
Bob Halley's avatar
Bob Halley committed
936
					done = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
937 938 939 940 941 942