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

Automatic Updater's avatar
Automatic Updater committed
18
/* $Id: task.c,v 1.107 2008/03/27 23:46:57 tbox Exp $ */
David Lawrence's avatar
David Lawrence committed
19

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

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

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

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

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

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

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

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

Mark Andrews's avatar
Mark Andrews committed
69
#ifdef HAVE_LIBXML2
70 71 72
static const char *statenames[] = {
	"idle", "ready", "running", "done",
};
Mark Andrews's avatar
Mark Andrews committed
73
#endif
74

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
	char				name[16];
	void *				tag;
Bob Halley's avatar
Bob Halley committed
93
	/* Locked by task manager lock. */
Bob Halley's avatar
Bob Halley committed
94 95
	LINK(isc_task_t)		link;
	LINK(isc_task_t)		ready_link;
Bob Halley's avatar
Bob Halley committed
96 97
};

Bob Halley's avatar
Bob Halley committed
98
#define TASK_F_SHUTTINGDOWN		0x01
99

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

103 104
#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
105

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

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

135 136
#ifndef ISC_PLATFORM_USETHREADS
static isc_taskmgr_t *taskmgr = NULL;
137
#endif /* ISC_PLATFORM_USETHREADS */
138

Bob Halley's avatar
base  
Bob Halley committed
139 140 141 142 143
/***
 *** Tasks.
 ***/

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

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

	XTRACE("task_finished");
Bob Halley's avatar
base  
Bob Halley committed
153 154 155

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

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

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

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

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

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

217
	if (exiting) {
Brian Wellington's avatar
Brian Wellington committed
218
		DESTROYLOCK(&task->lock);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
219
		isc_mem_put(manager->mctx, task, sizeof(*task));
220 221 222
		return (ISC_R_SHUTTINGDOWN);
	}

223
	task->magic = TASK_MAGIC;
Bob Halley's avatar
base  
Bob Halley committed
224 225
	*taskp = task;

Bob Halley's avatar
Bob Halley committed
226
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
227 228
}

Bob Halley's avatar
update  
Bob Halley committed
229
void
230
isc_task_attach(isc_task_t *source, isc_task_t **targetp) {
Bob Halley's avatar
base  
Bob Halley committed
231

232 233 234
	/*
	 * Attach *targetp to source.
	 */
Bob Halley's avatar
base  
Bob Halley committed
235

236 237
	REQUIRE(VALID_TASK(source));
	REQUIRE(targetp != NULL && *targetp == NULL);
Bob Halley's avatar
base  
Bob Halley committed
238

239
	XTTRACE(source, "isc_task_attach");
240

241 242 243 244 245
	LOCK(&source->lock);
	source->references++;
	UNLOCK(&source->lock);

	*targetp = source;
Bob Halley's avatar
base  
Bob Halley committed
246 247
}

248 249 250 251
static inline isc_boolean_t
task_shutdown(isc_task_t *task) {
	isc_boolean_t was_idle = ISC_FALSE;
	isc_event_t *event, *prev;
252

253 254 255 256 257 258 259
	/*
	 * Caller must be holding the task's lock.
	 */

	XTRACE("task_shutdown");

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

	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);
297
#ifdef ISC_PLATFORM_USETHREADS
298
	SIGNAL(&manager->work_available);
299
#endif /* ISC_PLATFORM_USETHREADS */
300 301 302 303

	UNLOCK(&manager->lock);
}

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

Bob Halley's avatar
Bob Halley committed
330
	return (ISC_FALSE);
331 332
}

Bob Halley's avatar
Bob Halley committed
333
void
Bob Halley's avatar
Bob Halley committed
334 335
isc_task_detach(isc_task_t **taskp) {
	isc_task_t *task;
Bob Halley's avatar
Bob Halley committed
336
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
337

338 339 340 341
	/*
	 * Detach *taskp from its task.
	 */

Bob Halley's avatar
base  
Bob Halley committed
342 343 344 345
	REQUIRE(taskp != NULL);
	task = *taskp;
	REQUIRE(VALID_TASK(task));

346 347
	XTRACE("isc_task_detach");

Bob Halley's avatar
base  
Bob Halley committed
348
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
349
	was_idle = task_detach(task);
Bob Halley's avatar
base  
Bob Halley committed
350 351
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
352
	if (was_idle)
353
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
354 355 356 357

	*taskp = NULL;
}

Bob Halley's avatar
Bob Halley committed
358 359 360
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
361
	isc_event_t *event;
362

363
	/*
364
	 * Caller must be holding the task lock.
365 366
	 */

Bob Halley's avatar
update  
Bob Halley committed
367 368
	REQUIRE(eventp != NULL);
	event = *eventp;
Bob Halley's avatar
base  
Bob Halley committed
369
	REQUIRE(event != NULL);
370
	REQUIRE(event->ev_type > 0);
Bob Halley's avatar
Bob Halley committed
371
	REQUIRE(task->state != task_state_done);
Bob Halley's avatar
base  
Bob Halley committed
372

373 374
	XTRACE("task_send");

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

Bob Halley's avatar
Bob Halley committed
385
	return (was_idle);
386
}
387

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

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

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

445
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
446 447
	idle1 = task_send(task, eventp);
	idle2 = task_detach(task);
448 449
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
450 451 452 453 454 455
	/*
	 * 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));
456

Bob Halley's avatar
Bob Halley committed
457 458
	if (idle1 || idle2)
		task_ready(task);
459 460

	*taskp = NULL;
Bob Halley's avatar
base  
Bob Halley committed
461 462
}

463
#define PURGE_OK(event)	(((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0)
464 465 466 467 468

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)
469
{
Bob Halley's avatar
Bob Halley committed
470
	isc_event_t *event, *next_event;
471
	unsigned int count = 0;
472

Bob Halley's avatar
Bob Halley committed
473
	REQUIRE(VALID_TASK(task));
474
	REQUIRE(last >= first);
Bob Halley's avatar
Bob Halley committed
475

476
	XTRACE("dequeue_events");
Bob Halley's avatar
Bob Halley committed
477

Bob Halley's avatar
Bob Halley committed
478
	/*
479 480 481
	 * 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.
482
	 *
483
	 * sender == NULL means "any sender", and tag == NULL means "any tag".
Bob Halley's avatar
Bob Halley committed
484 485 486
	 */

	LOCK(&task->lock);
487 488

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

Bob Halley's avatar
Bob Halley committed
500 501
	UNLOCK(&task->lock);

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

529 530 531 532 533
	/*
	 * Note that purging never changes the state of the task.
	 */

	return (count);
Bob Halley's avatar
Bob Halley committed
534 535
}

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

544 545
	XTRACE("isc_task_purge");

Bob Halley's avatar
Bob Halley committed
546
	return (isc_task_purgerange(task, sender, type, type, tag));
547 548
}

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

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

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

591 592 593 594 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
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));
}

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

627 628 629 630 631
	/*
	 * Send a shutdown event with action 'action' and argument 'arg' when
	 * 'task' is shutdown.
	 */

632
	REQUIRE(VALID_TASK(task));
633
	REQUIRE(action != NULL);
634

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

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

	if (disallowed)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
653
		isc_mem_put(task->manager->mctx, event, sizeof(*event));
654 655 656 657

	return (result);
}

Bob Halley's avatar
Bob Halley committed
658
void
Bob Halley's avatar
Bob Halley committed
659
isc_task_shutdown(isc_task_t *task) {
660
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
661

662 663 664 665
	/*
	 * Shutdown 'task'.
	 */

Bob Halley's avatar
base  
Bob Halley committed
666 667 668
	REQUIRE(VALID_TASK(task));

	LOCK(&task->lock);
669
	was_idle = task_shutdown(task);
Bob Halley's avatar
base  
Bob Halley committed
670 671
	UNLOCK(&task->lock);

672 673
	if (was_idle)
		task_ready(task);
Bob Halley's avatar
Bob Halley committed
674
}
Bob Halley's avatar
base  
Bob Halley committed
675

Bob Halley's avatar
Bob Halley committed
676
void
Bob Halley's avatar
Bob Halley committed
677
isc_task_destroy(isc_task_t **taskp) {
Bob Halley's avatar
Bob Halley committed
678

679 680 681 682
	/*
	 * Destroy '*taskp'.
	 */

Bob Halley's avatar
Bob Halley committed
683 684
	REQUIRE(taskp != NULL);

Bob Halley's avatar
Bob Halley committed
685 686
	isc_task_shutdown(*taskp);
	isc_task_detach(taskp);
Bob Halley's avatar
base  
Bob Halley committed
687 688
}

689
void
David Lawrence's avatar
David Lawrence committed
690
isc_task_setname(isc_task_t *task, const char *name, void *tag) {
691 692 693 694 695 696 697 698 699 700 701 702 703

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

705 706 707 708 709 710 711 712 713 714
const char *
isc_task_getname(isc_task_t *task) {
	return (task->name);
}

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

715 716 717 718 719 720 721 722 723 724 725
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);
}
726

Bob Halley's avatar
base  
Bob Halley committed
727 728 729
/***
 *** Task Manager.
 ***/
730 731
static void
dispatch(isc_taskmgr_t *manager) {
Bob Halley's avatar
Bob Halley committed
732
	isc_task_t *task;
733 734
#ifndef ISC_PLATFORM_USETHREADS
	unsigned int total_dispatch_count = 0;
735
	isc_tasklist_t ready_tasks;
736
#endif /* ISC_PLATFORM_USETHREADS */
Bob Halley's avatar
base  
Bob Halley committed
737 738 739 740 741 742

	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.
743
	 *
Bob Halley's avatar
base  
Bob Halley committed
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
	 * 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.
771
	 *
Bob Halley's avatar
base  
Bob Halley committed
772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789
	 * 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.
	 */

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

Bob Halley's avatar
base  
Bob Halley committed
823 824
		task = HEAD(manager->ready_tasks);
		if (task != NULL) {
Bob Halley's avatar
Bob Halley committed
825
			unsigned int dispatch_count = 0;
Bob Halley's avatar
Bob Halley committed
826 827
			isc_boolean_t done = ISC_FALSE;
			isc_boolean_t requeue = ISC_FALSE;
828
			isc_boolean_t finished = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
829
			isc_event_t *event;
Bob Halley's avatar
base  
Bob Halley committed
830 831 832 833 834

			INSIST(VALID_TASK(task));

			/*
			 * Note we only unlock the manager lock if we actually
835
			 * have a task to do.  We must reacquire the manager
Bob Halley's avatar
base  
Bob Halley committed
836 837 838
			 * lock before exiting the 'if (task != NULL)' block.
			 */
			DEQUEUE(manager->ready_tasks, task, ready_link);
839
			manager->tasks_running++;
Bob Halley's avatar
base  
Bob Halley committed
840 841 842
			UNLOCK(&manager->lock);

			LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
843
			INSIST(task->state == task_state_ready);
844
			task->state = task_state_running;
845 846
			XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
					      ISC_MSG_RUNNING, "running"));
847
			isc_stdtime_get(&task->now);
848 849 850
			do {
				if (!EMPTY(task->events)) {
					event = HEAD(task->events);
851
					DEQUEUE(task->events, event, ev_link);
Bob Halley's avatar
base  
Bob Halley committed
852

853 854 855
					/*
					 * Execute the event action.
					 */
856 857 858 859
					XTRACE(isc_msgcat_get(isc_msgcat,
							    ISC_MSGSET_TASK,
							    ISC_MSG_EXECUTE,
							    "execute action"));
860
					if (event->ev_action != NULL) {
861
						UNLOCK(&task->lock);
862
						(event->ev_action)(task,event);
863 864 865
						LOCK(&task->lock);
					}
					dispatch_count++;
866 867
#ifndef ISC_PLATFORM_USETHREADS
					total_dispatch_count++;
868
#endif /* ISC_PLATFORM_USETHREADS */
869
				}
870

Bob Halley's avatar
Bob Halley committed
871 872 873 874
				if (task->references == 0 &&
				    EMPTY(task->events) &&
				    !TASK_SHUTTINGDOWN(task)) {
					isc_boolean_t was_idle;
875

Bob Halley's avatar
Bob Halley committed
876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899
					/*
					 * 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);
900 901
				}

902
				if (EMPTY(task->events)) {
Bob Halley's avatar
Bob Halley committed
903
					/*
904
					 * Nothing else to do for this task
Bob Halley's avatar
Bob Halley committed
905
					 * right now.
Bob Halley's avatar
Bob Halley committed
906
					 */
907 908 909 910
					XTRACE(isc_msgcat_get(isc_msgcat,
							      ISC_MSGSET_TASK,
							      ISC_MSG_EMPTY,
							      "empty"));
Bob Halley's avatar
Bob Halley committed
911 912 913 914 915
					if (task->references == 0 &&
					    TASK_SHUTTINGDOWN(task)) {
						/*
						 * The task is done.
						 */
916 917 918 919 920
						XTRACE(isc_msgcat_get(
							       isc_msgcat,
							       ISC_MSGSET_TASK,
							       ISC_MSG_DONE,
							       "done"));
Bob Halley's avatar
Bob Halley committed
921
						finished = ISC_TRUE;
922
						task->state = task_state_done;
923 924
					} else
						task->state = task_state_idle;
Bob Halley's avatar
Bob Halley committed
925
					done = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
926 927 928 929 930 931 932 933 934 935 936
				} 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.
					 */
937 938 939 940
					XTRACE(isc_msgcat_get(isc_msgcat,
							      ISC_MSGSET_TASK,
							      ISC_MSG_QUANTUM,
							      "quantum"));
Bob Halley's avatar
base  
Bob Halley committed
941
					task->state = task_state_ready;
Bob Halley's avatar
Bob Halley committed
942 943
					requeue = ISC_TRUE;
					done = ISC_TRUE;