task.c 24.6 KB
Newer Older
Bob Halley's avatar
Bob Halley committed
1
/*
David Lawrence's avatar
David Lawrence committed
2
 * Copyright (C) 1998-2000  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

Brian Wellington's avatar
Brian Wellington committed
18
/* $Id: task.c,v 1.71 2000/08/26 01:31:51 bwelling 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/mem.h>
34
#include <isc/string.h>
Bob Halley's avatar
Bob Halley committed
35
#include <isc/task.h>
36
#include <isc/thread.h>
Michael Graff's avatar
Michael Graff committed
37
#include <isc/util.h>
Bob Halley's avatar
base  
Bob Halley committed
38

39 40
#define ISC_TASK_NAMES 1

Bob Halley's avatar
Bob Halley committed
41
#ifdef ISC_TASK_TRACE
42
#define XTRACE(m)		fprintf(stderr, "task %p thread %lu: %s\n", \
43
				       task, isc_thread_self(), (m))
44
#define XTTRACE(t, m)		fprintf(stderr, "task %p thread %lu: %s\n", \
45
				       (t), isc_thread_self(), (m))
46
#define XTHREADTRACE(m)		fprintf(stderr, "thread %lu: %s\n", \
47
				       isc_thread_self(), (m))
Bob Halley's avatar
Bob Halley committed
48 49
#else
#define XTRACE(m)
50
#define XTTRACE(t, m)
51
#define XTHREADTRACE(m)
Bob Halley's avatar
Bob Halley committed
52
#endif
Bob Halley's avatar
base  
Bob Halley committed
53

Bob Halley's avatar
Bob Halley committed
54
/***
Bob Halley's avatar
Bob Halley committed
55
 *** Types.
Bob Halley's avatar
Bob Halley committed
56 57
 ***/

Bob Halley's avatar
Bob Halley committed
58 59
typedef enum {
	task_state_idle, task_state_ready, task_state_running,
60
	task_state_done
Bob Halley's avatar
Bob Halley committed
61 62 63 64 65 66
} task_state_t;

#define TASK_MAGIC			0x5441534BU	/* TASK. */
#define VALID_TASK(t)			((t) != NULL && \
					 (t)->magic == TASK_MAGIC)

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

Bob Halley's avatar
Bob Halley committed
88
#define TASK_F_SHUTTINGDOWN		0x01
89

90 91
#define TASK_SHUTTINGDOWN(t)		(((t)->flags & TASK_F_SHUTTINGDOWN) \
					 != 0)
92

Bob Halley's avatar
Bob Halley committed
93 94 95 96
#define TASK_MANAGER_MAGIC		0x54534B4DU	/* TSKM. */
#define VALID_MANAGER(m)		((m) != NULL && \
					 (m)->magic == TASK_MANAGER_MAGIC)

Bob Halley's avatar
Bob Halley committed
97
struct isc_taskmgr {
Bob Halley's avatar
Bob Halley committed
98 99
	/* Not locked. */
	unsigned int			magic;
Bob Halley's avatar
Bob Halley committed
100
	isc_mem_t *			mctx;
Bob Halley's avatar
Bob Halley committed
101
	isc_mutex_t			lock;
102 103
	unsigned int			workers;
	isc_thread_t *			threads;
Bob Halley's avatar
Bob Halley committed
104 105
	/* Locked by task manager lock. */
	unsigned int			default_quantum;
Bob Halley's avatar
Bob Halley committed
106 107
	LIST(isc_task_t)		tasks;
	LIST(isc_task_t)		ready_tasks;
Bob Halley's avatar
Bob Halley committed
108
	isc_condition_t			work_available;
Bob Halley's avatar
Bob Halley committed
109
	isc_boolean_t			exiting;
Bob Halley's avatar
Bob Halley committed
110
};
Bob Halley's avatar
Bob Halley committed
111

Bob Halley's avatar
Bob Halley committed
112 113
#define DEFAULT_DEFAULT_QUANTUM		5
#define FINISHED(m)			((m)->exiting && EMPTY((m)->tasks))
Bob Halley's avatar
Bob Halley committed
114

Bob Halley's avatar
base  
Bob Halley committed
115 116 117 118 119
/***
 *** Tasks.
 ***/

static void
120
task_finished(isc_task_t *task) {
Bob Halley's avatar
Bob Halley committed
121
	isc_taskmgr_t *manager = task->manager;
Bob Halley's avatar
base  
Bob Halley committed
122 123

	REQUIRE(EMPTY(task->events));
124
	REQUIRE(EMPTY(task->on_shutdown));
125 126 127 128
	REQUIRE(task->references == 0);
	REQUIRE(task->state == task_state_done);

	XTRACE("task_finished");
Bob Halley's avatar
base  
Bob Halley committed
129 130 131 132 133 134 135 136 137 138 139 140 141

	LOCK(&manager->lock);
	UNLINK(manager->tasks, task, link);
	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);
	}
	UNLOCK(&manager->lock);
142

Brian Wellington's avatar
Brian Wellington committed
143
	DESTROYLOCK(&task->lock);
Bob Halley's avatar
base  
Bob Halley committed
144
	task->magic = 0;
145
	isc_mem_put(manager->mctx, task, sizeof *task);
Bob Halley's avatar
base  
Bob Halley committed
146 147
}

Bob Halley's avatar
Bob Halley committed
148
isc_result_t
149
isc_task_create(isc_taskmgr_t *manager, unsigned int quantum,
150
		isc_task_t **taskp)
Bob Halley's avatar
Bob Halley committed
151
{
Bob Halley's avatar
Bob Halley committed
152
	isc_task_t *task;
153
	isc_boolean_t exiting;
Bob Halley's avatar
base  
Bob Halley committed
154 155 156 157

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

158
	task = isc_mem_get(manager->mctx, sizeof *task);
Bob Halley's avatar
base  
Bob Halley committed
159
	if (task == NULL)
Bob Halley's avatar
Bob Halley committed
160
		return (ISC_R_NOMEMORY);
161
	XTRACE("create");
Bob Halley's avatar
base  
Bob Halley committed
162
	task->manager = manager;
Bob Halley's avatar
Bob Halley committed
163
	if (isc_mutex_init(&task->lock) != ISC_R_SUCCESS) {
164
		isc_mem_put(manager->mctx, task, sizeof *task);
Bob Halley's avatar
Bob Halley committed
165 166 167
		UNEXPECTED_ERROR(__FILE__, __LINE__,
				 "isc_mutex_init() failed");
		return (ISC_R_UNEXPECTED);
Bob Halley's avatar
update  
Bob Halley committed
168
	}
Bob Halley's avatar
base  
Bob Halley committed
169 170 171
	task->state = task_state_idle;
	task->references = 1;
	INIT_LIST(task->events);
172
	INIT_LIST(task->on_shutdown);
Bob Halley's avatar
base  
Bob Halley committed
173
	task->quantum = quantum;
Bob Halley's avatar
Bob Halley committed
174
	task->flags = 0;
175
#ifdef ISC_TASK_NAMES
Mark Andrews's avatar
Mark Andrews committed
176
	memset(task->name, 0, sizeof task->name);
177 178
	task->tag = NULL;
#endif
Bob Halley's avatar
base  
Bob Halley committed
179 180 181
	INIT_LINK(task, link);
	INIT_LINK(task, ready_link);

182
	exiting = ISC_FALSE;
Bob Halley's avatar
base  
Bob Halley committed
183
	LOCK(&manager->lock);
184 185 186 187 188 189
	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
190 191
	UNLOCK(&manager->lock);

192
	if (exiting) {
Brian Wellington's avatar
Brian Wellington committed
193
		DESTROYLOCK(&task->lock);
194
		isc_mem_put(manager->mctx, task, sizeof *task);
195 196 197
		return (ISC_R_SHUTTINGDOWN);
	}

198
	task->magic = TASK_MAGIC;
Bob Halley's avatar
base  
Bob Halley committed
199 200
	*taskp = task;

Bob Halley's avatar
Bob Halley committed
201
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
202 203
}

Bob Halley's avatar
update  
Bob Halley committed
204
void
205
isc_task_attach(isc_task_t *source, isc_task_t **targetp) {
Bob Halley's avatar
base  
Bob Halley committed
206

207 208 209
	/*
	 * Attach *targetp to source.
	 */
Bob Halley's avatar
base  
Bob Halley committed
210

211 212
	REQUIRE(VALID_TASK(source));
	REQUIRE(targetp != NULL && *targetp == NULL);
Bob Halley's avatar
base  
Bob Halley committed
213

214 215
	XTTRACE(source, "attach");

216 217 218 219 220
	LOCK(&source->lock);
	source->references++;
	UNLOCK(&source->lock);

	*targetp = source;
Bob Halley's avatar
base  
Bob Halley committed
221 222
}

223 224 225 226
static inline isc_boolean_t
task_shutdown(isc_task_t *task) {
	isc_boolean_t was_idle = ISC_FALSE;
	isc_event_t *event, *prev;
227

228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
	/*
	 * Caller must be holding the task's lock.
	 */

	XTRACE("task_shutdown");

	if (! TASK_SHUTTINGDOWN(task)) {
		XTRACE("shutting down");
		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) {
250 251 252
			prev = PREV(event, ev_link);
			DEQUEUE(task->on_shutdown, event, ev_link);
			ENQUEUE(task->events, event, ev_link);
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
		}
	}

	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);
	SIGNAL(&manager->work_available);

	UNLOCK(&manager->lock);
}

Bob Halley's avatar
Bob Halley committed
276
static inline isc_boolean_t
277 278 279 280 281 282 283 284 285 286 287
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
288 289 290 291 292 293 294 295 296 297 298 299
	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
		 * make the task ready and allow run() to deal with
		 * shutting down and termination.
		 */
		task->state = task_state_ready;
		return (ISC_TRUE);
300 301
	}

Bob Halley's avatar
Bob Halley committed
302
	return (ISC_FALSE);
303 304
}

Bob Halley's avatar
Bob Halley committed
305
void
Bob Halley's avatar
Bob Halley committed
306 307
isc_task_detach(isc_task_t **taskp) {
	isc_task_t *task;
Bob Halley's avatar
Bob Halley committed
308
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
309

310 311 312 313
	/*
	 * Detach *taskp from its task.
	 */

Bob Halley's avatar
base  
Bob Halley committed
314 315 316 317
	REQUIRE(taskp != NULL);
	task = *taskp;
	REQUIRE(VALID_TASK(task));

318 319
	XTRACE("isc_task_detach");

Bob Halley's avatar
base  
Bob Halley committed
320
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
321
	was_idle = task_detach(task);
Bob Halley's avatar
base  
Bob Halley committed
322 323
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
324
	if (was_idle)
325
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
326 327 328 329

	*taskp = NULL;
}

Bob Halley's avatar
Bob Halley committed
330 331 332
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
333
	isc_event_t *event;
334

335
	/*
336
	 * Caller must be holding the task lock.
337 338
	 */

Bob Halley's avatar
update  
Bob Halley committed
339 340
	REQUIRE(eventp != NULL);
	event = *eventp;
Bob Halley's avatar
base  
Bob Halley committed
341
	REQUIRE(event != NULL);
342
	REQUIRE(event->ev_type > 0);
Bob Halley's avatar
Bob Halley committed
343
	REQUIRE(task->state != task_state_done);
Bob Halley's avatar
base  
Bob Halley committed
344

345 346
	XTRACE("task_send");

Bob Halley's avatar
Bob Halley committed
347 348 349 350
	if (task->state == task_state_idle) {
		was_idle = ISC_TRUE;
		INSIST(EMPTY(task->events));
		task->state = task_state_ready;
351
	}
Bob Halley's avatar
Bob Halley committed
352 353
	INSIST(task->state == task_state_ready ||
	       task->state == task_state_running);
354
	ENQUEUE(task->events, event, ev_link);
Bob Halley's avatar
Bob Halley committed
355
	*eventp = NULL;
356

Bob Halley's avatar
Bob Halley committed
357
	return (was_idle);
358
}
359

Bob Halley's avatar
Bob Halley committed
360
void
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
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
378
	was_idle = task_send(task, eventp);
Bob Halley's avatar
base  
Bob Halley committed
379 380 381 382 383 384
	UNLOCK(&task->lock);

	if (was_idle) {
		/*
		 * We need to add this task to the ready queue.
		 *
385 386 387
		 * 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
388 389
		 *
		 * We've changed the state to ready, so no one else will
390 391 392
		 * 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,
393
		 * removed, or a shutdown is started in the interval
394 395
		 * 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
396
		 */
397
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
398
	}
399 400
}

Bob Halley's avatar
Bob Halley committed
401
void
402
isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
Bob Halley's avatar
Bob Halley committed
403
	isc_boolean_t idle1, idle2;
404 405 406 407 408 409 410 411 412 413 414 415
	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
416

417
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
418 419
	idle1 = task_send(task, eventp);
	idle2 = task_detach(task);
420 421
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
422 423 424 425 426 427
	/*
	 * 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));
428

Bob Halley's avatar
Bob Halley committed
429 430
	if (idle1 || idle2)
		task_ready(task);
431 432

	*taskp = NULL;
Bob Halley's avatar
base  
Bob Halley committed
433 434
}

435
#define PURGE_OK(event)	(((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0)
436 437 438 439 440

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)
441
{
Bob Halley's avatar
Bob Halley committed
442
	isc_event_t *event, *next_event;
443
	unsigned int count = 0;
444

Bob Halley's avatar
Bob Halley committed
445
	REQUIRE(VALID_TASK(task));
446
	REQUIRE(last >= first);
Bob Halley's avatar
Bob Halley committed
447

448
	XTRACE("dequeue_events");
Bob Halley's avatar
Bob Halley committed
449

Bob Halley's avatar
Bob Halley committed
450
	/*
451 452 453
	 * 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.
454
	 *
455
	 * sender == NULL means "any sender", and tag == NULL means "any tag".
Bob Halley's avatar
Bob Halley committed
456 457 458
	 */

	LOCK(&task->lock);
459 460

	for (event = HEAD(task->events); event != NULL; event = next_event) {
461 462 463 464
		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) &&
465
		    (!purging || PURGE_OK(event))) {
466 467
			DEQUEUE(task->events, event, ev_link);
			ENQUEUE(*events, event, ev_link);
468
			count++;
Bob Halley's avatar
Bob Halley committed
469 470
		}
	}
471

Bob Halley's avatar
Bob Halley committed
472 473
	UNLOCK(&task->lock);

474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
	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) {
497
		next_event = NEXT(event, ev_link);
Bob Halley's avatar
Bob Halley committed
498
		isc_event_free(&event);
Bob Halley's avatar
Bob Halley committed
499
	}
500

501 502 503 504 505
	/*
	 * Note that purging never changes the state of the task.
	 */

	return (count);
Bob Halley's avatar
Bob Halley committed
506 507
}

508
unsigned int
Bob Halley's avatar
Bob Halley committed
509
isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
510
	       void *tag)
Bob Halley's avatar
Bob Halley committed
511
{
512 513 514 515
	/*
	 * Purge events from a task's event queue.
	 */

516 517
	XTRACE("isc_task_purge");

Bob Halley's avatar
Bob Halley committed
518
	return (isc_task_purgerange(task, sender, type, type, tag));
519 520
}

Bob Halley's avatar
Bob Halley committed
521 522 523 524 525 526
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.
527 528
	 *
	 * XXXRTH:  WARNING:  This method may be removed before beta.
Bob Halley's avatar
Bob Halley committed
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
	 */

	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) {
547
		next_event = NEXT(curr_event, ev_link);
548
		if (curr_event == event && PURGE_OK(event)) {
549
			DEQUEUE(task->events, curr_event, ev_link);
Bob Halley's avatar
Bob Halley committed
550 551 552 553 554 555 556 557 558 559 560 561 562
			break;
		}
	}
	UNLOCK(&task->lock);

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
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));
}

592
isc_result_t
David Lawrence's avatar
David Lawrence committed
593 594
isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, const void *arg)
{
595 596 597 598
	isc_boolean_t disallowed = ISC_FALSE;
	isc_result_t result = ISC_R_SUCCESS;
	isc_event_t *event;

599 600 601 602 603
	/*
	 * Send a shutdown event with action 'action' and argument 'arg' when
	 * 'task' is shutdown.
	 */

604
	REQUIRE(VALID_TASK(task));
605
	REQUIRE(action != NULL);
606

607
	event = isc_event_allocate(task->manager->mctx,
608 609 610 611 612
				   NULL,
				   ISC_TASKEVENT_SHUTDOWN,
				   action,
				   arg,
				   sizeof *event);
613 614 615 616
	if (event == NULL)
		return (ISC_R_NOMEMORY);

	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
617
	if (TASK_SHUTTINGDOWN(task)) {
618
		disallowed = ISC_TRUE;
619
		result = ISC_R_SHUTTINGDOWN;
620
	} else
621
		ENQUEUE(task->on_shutdown, event, ev_link);
622 623 624
	UNLOCK(&task->lock);

	if (disallowed)
625
		isc_mem_put(task->manager->mctx, event, sizeof *event);
626 627 628 629

	return (result);
}

Bob Halley's avatar
Bob Halley committed
630
void
Bob Halley's avatar
Bob Halley committed
631
isc_task_shutdown(isc_task_t *task) {
632
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
633

634 635 636 637
	/*
	 * Shutdown 'task'.
	 */

Bob Halley's avatar
base  
Bob Halley committed
638 639 640
	REQUIRE(VALID_TASK(task));

	LOCK(&task->lock);
641
	was_idle = task_shutdown(task);
Bob Halley's avatar
base  
Bob Halley committed
642 643
	UNLOCK(&task->lock);

644 645
	if (was_idle)
		task_ready(task);
Bob Halley's avatar
Bob Halley committed
646
}
Bob Halley's avatar
base  
Bob Halley committed
647

Bob Halley's avatar
Bob Halley committed
648
void
Bob Halley's avatar
Bob Halley committed
649
isc_task_destroy(isc_task_t **taskp) {
Bob Halley's avatar
Bob Halley committed
650

651 652 653 654
	/*
	 * Destroy '*taskp'.
	 */

Bob Halley's avatar
Bob Halley committed
655 656
	REQUIRE(taskp != NULL);

Bob Halley's avatar
Bob Halley committed
657 658
	isc_task_shutdown(*taskp);
	isc_task_detach(taskp);
Bob Halley's avatar
base  
Bob Halley committed
659 660
}

661
void
David Lawrence's avatar
David Lawrence committed
662
isc_task_setname(isc_task_t *task, const char *name, void *tag) {
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679

	/*
	 * 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
	(void)name;
	(void)tag;
#endif
Bob Halley's avatar
base  
Bob Halley committed
680

681
}
Bob Halley's avatar
Bob Halley committed
682

683 684 685 686 687 688 689 690 691 692 693
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
694 695 696 697
/***
 *** Task Manager.
 ***/

Bob Halley's avatar
update  
Bob Halley committed
698
static isc_threadresult_t
Bob Halley's avatar
Bob Halley committed
699 700 701
#ifdef _WIN32
WINAPI
#endif
Bob Halley's avatar
update  
Bob Halley committed
702
run(void *uap) {
Bob Halley's avatar
Bob Halley committed
703 704
	isc_taskmgr_t *manager = uap;
	isc_task_t *task;
Bob Halley's avatar
base  
Bob Halley committed
705

706
	XTHREADTRACE("start");
Bob Halley's avatar
base  
Bob Halley committed
707 708 709 710 711 712

	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.
713
	 *
Bob Halley's avatar
base  
Bob Halley committed
714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740
	 * 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.
741
	 *
Bob Halley's avatar
base  
Bob Halley committed
742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763
	 * 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.
	 */

	LOCK(&manager->lock);
	while (!FINISHED(manager)) {
		/*
		 * For reasons similar to those given in the comment in
764
		 * isc_task_send() above, it is safe for us to dequeue
Bob Halley's avatar
base  
Bob Halley committed
765 766 767 768 769
		 * the task while only holding the manager lock, and then
		 * change the task to running state while only holding the
		 * task lock.
		 */
		while (EMPTY(manager->ready_tasks) && !FINISHED(manager)) {
770
			XTHREADTRACE("wait");
Bob Halley's avatar
base  
Bob Halley committed
771
			WAIT(&manager->work_available, &manager->lock);
772
			XTHREADTRACE("awake");
Bob Halley's avatar
base  
Bob Halley committed
773
		}
774
		XTHREADTRACE("working");
775

Bob Halley's avatar
base  
Bob Halley committed
776 777
		task = HEAD(manager->ready_tasks);
		if (task != NULL) {
Bob Halley's avatar
Bob Halley committed
778
			unsigned int dispatch_count = 0;
Bob Halley's avatar
Bob Halley committed
779 780
			isc_boolean_t done = ISC_FALSE;
			isc_boolean_t requeue = ISC_FALSE;
781
			isc_boolean_t finished = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
782
			isc_event_t *event;
Bob Halley's avatar
base  
Bob Halley committed
783 784 785 786 787

			INSIST(VALID_TASK(task));

			/*
			 * Note we only unlock the manager lock if we actually
788
			 * have a task to do.  We must reacquire the manager
Bob Halley's avatar
base  
Bob Halley committed
789 790 791 792 793 794
			 * lock before exiting the 'if (task != NULL)' block.
			 */
			DEQUEUE(manager->ready_tasks, task, ready_link);
			UNLOCK(&manager->lock);

			LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
795
			INSIST(task->state == task_state_ready);
796 797 798 799 800
			task->state = task_state_running;
			XTRACE("running");
			do {
				if (!EMPTY(task->events)) {
					event = HEAD(task->events);
801
					DEQUEUE(task->events, event, ev_link);
Bob Halley's avatar
base  
Bob Halley committed
802

803 804 805 806
					/*
					 * Execute the event action.
					 */
					XTRACE("execute action");
807
					if (event->ev_action != NULL) {
808
						UNLOCK(&task->lock);
809
						(event->ev_action)(task,event);
810 811 812
						LOCK(&task->lock);
					}
					dispatch_count++;
813
				}
814

Bob Halley's avatar
Bob Halley committed
815 816 817 818
				if (task->references == 0 &&
				    EMPTY(task->events) &&
				    !TASK_SHUTTINGDOWN(task)) {
					isc_boolean_t was_idle;
819

Bob Halley's avatar
Bob Halley committed
820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
					/*
					 * 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);
844 845
				}

846
				if (EMPTY(task->events)) {
Bob Halley's avatar
Bob Halley committed
847
					/*
848
					 * Nothing else to do for this task
Bob Halley's avatar
Bob Halley committed
849
					 * right now.
Bob Halley's avatar
Bob Halley committed
850
					 */
Bob Halley's avatar
Bob Halley committed
851
					XTRACE("empty");
Bob Halley's avatar
Bob Halley committed
852 853 854 855 856
					if (task->references == 0 &&
					    TASK_SHUTTINGDOWN(task)) {
						/*
						 * The task is done.
						 */
857
						XTRACE("done");
Bob Halley's avatar
Bob Halley committed
858
						finished = ISC_TRUE;
859
						task->state = task_state_done;
860 861
					} else
						task->state = task_state_idle;
Bob Halley's avatar
Bob Halley committed
862
					done = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
863 864 865 866 867 868 869 870 871 872 873
				} 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.
					 */
Bob Halley's avatar
Bob Halley committed
874
					XTRACE("quantum");
Bob Halley's avatar
base  
Bob Halley committed
875
					task->state = task_state_ready;
Bob Halley's avatar
Bob Halley committed
876 877
					requeue = ISC_TRUE;
					done = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
878
				}
879
			} while (!done);
Bob Halley's avatar
base  
Bob Halley committed
880 881
			UNLOCK(&task->lock);

882 883
			if (finished)
				task_finished(task);
Bob Halley's avatar
base  
Bob Halley committed
884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912

			LOCK(&manager->lock);
			if (requeue) {
				/*
				 * We know we're awake, so we don't have
				 * to wakeup any sleeping threads if the
				 * ready queue is empty before we requeue.
				 *
				 * A possible optimization if the queue is
				 * empty is to 'goto' the 'if (task != NULL)'
				 * block, avoiding the ENQUEUE of the task
				 * and the subsequent immediate DEQUEUE
				 * (since it is the only executable task).
				 * We don't do this because then we'd be
				 * skipping the exit_requested check.  The
				 * cost of ENQUEUE is low anyway, especially
				 * when you consider that we'd have to do
				 * an extra EMPTY check to see if we could
				 * do the optimization.  If the ready queue
				 * were usually nonempty, the 'optimization'
				 * might even hurt rather than help.
				 */
				ENQUEUE(manager->ready_tasks, task,
					ready_link);
			}
		}
	}
	UNLOCK(&manager->lock);

913
	XTHREADTRACE("exit");
Bob Halley's avatar
base  
Bob Halley committed
914

Bob Halley's avatar
update  
Bob Halley committed
915
	return ((isc_threadresult_t)0);
Bob Halley's avatar
base  
Bob Halley committed
916 917 918
}

static void
Bob Halley's avatar
Bob Halley committed
919
manager_free(isc_taskmgr_t *manager) {
920 921
	isc_mem_t *mctx;

Bob Halley's avatar
Bob Halley committed
922
	(void)isc_condition_destroy(&manager->work_available);
Brian Wellington's avatar
Brian Wellington committed
923
	DESTROYLOCK(&manager->lock);
924 925
	isc_mem_put(manager->mctx, manager->threads,
		    manager->workers * sizeof (isc_thread_t));
Bob Halley's avatar
base  
Bob Halley committed
926
	manager->magic = 0;
927 928 929
	mctx = manager->mctx;
	isc_mem_put(mctx, manager, sizeof *manager);
	isc_mem_detach(&mctx);
Bob Halley's avatar
base  
Bob Halley committed
930 931
}

Bob Halley's avatar
Bob Halley committed
932
isc_result_t
933
isc_taskmgr_create(isc_mem_t *mctx, unsigned int workers,
Bob Halley's avatar
Bob Halley committed
934
		   unsigned int default_quantum, isc_taskmgr_t **managerp)
Bob Halley's avatar
Bob Halley committed
935 936
{
	unsigned int i, started = 0;
Bob Halley's avatar
Bob Halley committed
937
	isc_taskmgr_t *manager;
938
	isc_thread_t *threads;
Bob Halley's avatar
Bob Halley committed
939

940 941 942 943
	/*
	 * Create a new task manager.
	 */

Bob Halley's avatar
Bob Halley committed
944
	REQUIRE(workers > 0);
Bob Halley's avatar
Bob Halley committed
945
	REQUIRE(managerp != NULL && *managerp == NULL);
Bob Halley's avatar
base  
Bob Halley committed
946

Bob Halley's avatar
Bob Halley committed
947
	manager = isc_mem_get(mctx, sizeof *manager);
Bob Halley's avatar
base  
Bob Halley committed
948
	if (manager == NULL)
Bob Halley's avatar
Bob Halley committed
949
		return (ISC_R_NOMEMORY);
Bob Halley's avatar
base  
Bob Halley committed
950
	manager->magic = TASK_MANAGER_MAGIC;
951
	manager->mctx = NULL;
952 953 954 955 956 957 958
	threads = isc_mem_get(mctx, workers * sizeof (isc_thread_t));
	if (threads == NULL) {
		isc_mem_put(mctx, manager, sizeof *manager);
		return (ISC_R_NOMEMORY);
	}
	manager->threads = threads;
	manager->workers = 0;
Bob Halley's avatar
Bob Halley committed
959
	if (isc_mutex_init(&manager->lock) != ISC_R_SUCCESS) {
960
		isc_mem_put(mctx, threads, workers * sizeof (isc_thread_t));
Bob Halley's avatar
Bob Halley committed
961
		isc_mem_put(mctx, manager, sizeof *manager);
Bob Halley's avatar
Bob Halley committed
962 963 964
		UNEXPECTED_ERROR(__FILE__, __LINE__,
				 "isc_mutex_init() failed");
		return (ISC_R_UNEXPECTED);
Bob Halley's avatar
update  
Bob Halley committed
965
	}
Bob Halley's avatar
base  
Bob Halley committed
966 967 968 969 970
	if (default_quantum == 0)
		default_quantum = DEFAULT_DEFAULT_QUANTUM;
	manager->default_quantum = default_quantum;
	INIT_LIST(manager->tasks);
	INIT_LIST(manager->ready_tasks);
Bob Halley's avatar
Bob Halley committed
971
	if (isc_condition_init(&manager->work_available) != ISC_R_SUCCESS) {
Brian Wellington's avatar
Brian Wellington committed
972
		DESTROYLOCK(&manager->lock);
973
		isc_mem_put(mctx, threads, workers * sizeof (isc_thread_t));
Bob Halley's avatar
Bob Halley committed
974
		isc_mem_put(mctx, manager, sizeof *manager);
Bob Halley's avatar
Bob Halley committed
975 976 977
		UNEXPECTED_ERROR(__FILE__, __LINE__,
				 "isc_condition_init() failed");
		return (ISC_R_UNEXPECTED);
Bob Halley's avatar
update  
Bob Halley committed
978
	}
Bob Halley's avatar
Bob Halley committed
979
	manager->exiting = ISC_FALSE;
Bob Halley's avatar
base  
Bob Halley committed
980 981
	manager->workers = 0;

982 983
	isc_mem_attach(mctx, &manager->mctx);

Bob Halley's avatar
base  
Bob Halley committed
984 985 986 987 988
	LOCK(&manager->lock);
	/*
	 * Start workers.
	 */
	for (i = 0; i < workers; i++) {
989
		if (isc_thread_create(run, manager,
990
				      &manager->threads[manager->workers]) ==
Bob Halley's avatar
Bob Halley committed
991
		    ISC_R_SUCCESS) {
Bob Halley's avatar
base  
Bob Halley committed
992 993 994 995 996