task.c 30.3 KB
Newer Older
Bob Halley's avatar
Bob Halley committed
1
/*
Brian Wellington's avatar
Brian Wellington committed
2
 * Copyright (C) 1998-2001  Internet Software Consortium.
3
 *
Bob Halley's avatar
Bob Halley committed
4 5 6
 * 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.
7
 *
8 9 10 11 12 13 14 15
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
 * INTERNET SOFTWARE CONSORTIUM 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.88 2002/07/19 03:39:43 marka Exp $ */
David Lawrence's avatar
David Lawrence committed
19

Bob Halley's avatar
Bob Halley committed
20 21 22 23
/*
 * Principal Author: Bob Halley
 */

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 71
#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
72

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

Bob Halley's avatar
Bob Halley committed
94
#define TASK_F_SHUTTINGDOWN		0x01
95

96 97
#define TASK_SHUTTINGDOWN(t)		(((t)->flags & TASK_F_SHUTTINGDOWN) \
					 != 0)
98

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

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

127
#define DEFAULT_TASKMGR_QUANTUM		10
Bob Halley's avatar
Bob Halley committed
128 129
#define DEFAULT_DEFAULT_QUANTUM		5
#define FINISHED(m)			((m)->exiting && EMPTY((m)->tasks))
Bob Halley's avatar
Bob Halley committed
130

131 132
#ifndef ISC_PLATFORM_USETHREADS
static isc_taskmgr_t *taskmgr = NULL;
133
#endif /* ISC_PLATFORM_USETHREADS */
134

Bob Halley's avatar
base  
Bob Halley committed
135 136 137 138 139
/***
 *** Tasks.
 ***/

static void
140
task_finished(isc_task_t *task) {
Bob Halley's avatar
Bob Halley committed
141
	isc_taskmgr_t *manager = task->manager;
Bob Halley's avatar
base  
Bob Halley committed
142 143

	REQUIRE(EMPTY(task->events));
144
	REQUIRE(EMPTY(task->on_shutdown));
145 146 147 148
	REQUIRE(task->references == 0);
	REQUIRE(task->state == task_state_done);

	XTRACE("task_finished");
Bob Halley's avatar
base  
Bob Halley committed
149 150 151

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

Brian Wellington's avatar
Brian Wellington committed
165
	DESTROYLOCK(&task->lock);
Bob Halley's avatar
base  
Bob Halley committed
166
	task->magic = 0;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
167
	isc_mem_put(manager->mctx, task, sizeof(*task));
Bob Halley's avatar
base  
Bob Halley committed
168 169
}

Bob Halley's avatar
Bob Halley committed
170
isc_result_t
171
isc_task_create(isc_taskmgr_t *manager, unsigned int quantum,
172
		isc_task_t **taskp)
Bob Halley's avatar
Bob Halley committed
173
{
Bob Halley's avatar
Bob Halley committed
174
	isc_task_t *task;
175
	isc_boolean_t exiting;
Bob Halley's avatar
base  
Bob Halley committed
176 177 178 179

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

Andreas Gustafsson's avatar
Andreas Gustafsson committed
180
	task = isc_mem_get(manager->mctx, sizeof(*task));
Bob Halley's avatar
base  
Bob Halley committed
181
	if (task == NULL)
Bob Halley's avatar
Bob Halley committed
182
		return (ISC_R_NOMEMORY);
183
	XTRACE("isc_task_create");
Bob Halley's avatar
base  
Bob Halley committed
184
	task->manager = manager;
Bob Halley's avatar
Bob Halley committed
185
	if (isc_mutex_init(&task->lock) != ISC_R_SUCCESS) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
186
		isc_mem_put(manager->mctx, task, sizeof(*task));
Bob Halley's avatar
Bob Halley committed
187
		UNEXPECTED_ERROR(__FILE__, __LINE__,
188 189 190
				 "isc_mutex_init() %s",
				 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
						ISC_MSG_FAILED, "failed"));
Bob Halley's avatar
Bob Halley committed
191
		return (ISC_R_UNEXPECTED);
Bob Halley's avatar
update  
Bob Halley committed
192
	}
Bob Halley's avatar
base  
Bob Halley committed
193 194 195
	task->state = task_state_idle;
	task->references = 1;
	INIT_LIST(task->events);
196
	INIT_LIST(task->on_shutdown);
Bob Halley's avatar
base  
Bob Halley committed
197
	task->quantum = quantum;
Bob Halley's avatar
Bob Halley committed
198
	task->flags = 0;
199
#ifdef ISC_TASK_NAMES
Andreas Gustafsson's avatar
Andreas Gustafsson committed
200
	memset(task->name, 0, sizeof(task->name));
201 202
	task->tag = NULL;
#endif
Bob Halley's avatar
base  
Bob Halley committed
203 204 205
	INIT_LINK(task, link);
	INIT_LINK(task, ready_link);

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

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

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

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

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

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

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

238
	XTTRACE(source, "isc_task_attach");
239

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

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

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

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

	XTRACE("task_shutdown");

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

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

	UNLOCK(&manager->lock);
}

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

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

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

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

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

345 346
	XTRACE("isc_task_detach");

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

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

	*taskp = NULL;
}

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

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

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

372 373
	XTRACE("task_send");

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	LOCK(&task->lock);
486 487

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

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

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

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

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

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

543 544
	XTRACE("isc_task_purge");

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

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

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

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

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

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

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

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

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

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

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

	return (result);
}

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

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

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

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

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

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

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

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

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

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

	/*
	 * 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
704 705
	UNUSED(name);
	UNUSED(tag);
706
#endif
Bob Halley's avatar
base  
Bob Halley committed
707

708
}
Bob Halley's avatar
Bob Halley committed
709

710 711 712 713 714 715 716 717 718 719 720
const char *
isc_task_getname(isc_task_t *task) {
	return (task->name);
}

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


Bob Halley's avatar
base  
Bob Halley committed
721 722 723
/***
 *** Task Manager.
 ***/
724 725
static void
dispatch(isc_taskmgr_t *manager) {
Bob Halley's avatar
Bob Halley committed
726
	isc_task_t *task;
727 728
#ifndef ISC_PLATFORM_USETHREADS
	unsigned int total_dispatch_count = 0;
729
	isc_tasklist_t ready_tasks;
730
#endif /* ISC_PLATFORM_USETHREADS */
Bob Halley's avatar
base  
Bob Halley committed
731 732 733 734 735 736

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

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

Bob Halley's avatar
base  
Bob Halley committed
817 818
		task = HEAD(manager->ready_tasks);
		if (task != NULL) {
Bob Halley's avatar
Bob Halley committed
819
			unsigned int dispatch_count = 0;
Bob Halley's avatar
Bob Halley committed
820 821
			isc_boolean_t done = ISC_FALSE;
			isc_boolean_t requeue = ISC_FALSE;
822
			isc_boolean_t finished = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
823
			isc_event_t *event;
Bob Halley's avatar
base  
Bob Halley committed
824 825 826 827 828

			INSIST(VALID_TASK(task));

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

			LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
837
			INSIST(task->state == task_state_ready);
838
			task->state = task_state_running;
839 840
			XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
					      ISC_MSG_RUNNING, "running"));
841 842 843
			do {
				if (!EMPTY(task->events)) {
					event = HEAD(task->events);
844
					DEQUEUE(task->events, event, ev_link);
Bob Halley's avatar
base  
Bob Halley committed
845

846 847 848
					/*
					 * Execute the event action.
					 */
849 850 851 852
					XTRACE(isc_msgcat_get(isc_msgcat,
							    ISC_MSGSET_TASK,
							    ISC_MSG_EXECUTE,
							    "execute action"));
853
					if (event->ev_action != NULL) {
854
						UNLOCK(&task->lock);
855
						(event->ev_action)(task,event);
856 857 858
						LOCK(&task->lock);
					}
					dispatch_count++;
859 860
#ifndef ISC_PLATFORM_USETHREADS
					total_dispatch_count++;
861
#endif /* ISC_PLATFORM_USETHREADS */
862
				}
863

Bob Halley's avatar
Bob Halley committed
864 865 866 867
				if (task->references == 0 &&
				    EMPTY(task->events) &&
				    !TASK_SHUTTINGDOWN(task)) {
					isc_boolean_t was_idle;
868

Bob Halley's avatar
Bob Halley committed
869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892
					/*
					 * 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);
893 894
				}

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

941 942
			if (finished)
				task_finished(task);
Bob Halley's avatar
base  
Bob Halley committed
943 944

			LOCK(&manager->lock);
945 946 947 948 949 950 951
			manager->tasks_running--;
#ifdef ISC_PLATFORM_USETHREADS
			if