task.c 24.7 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.
Bob Halley's avatar
Bob Halley committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16
 * 
 * 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.
 * 
 * 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
base  
Bob Halley committed
17

18
/* $Id: task.c,v 1.66 2000/07/26 17:11:13 gson 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 43 44 45 46 47
#define XTRACE(m)		printf("task %p thread %lu: %s\n", \
				       task, isc_thread_self(), (m))
#define XTTRACE(t, m)		printf("task %p thread %lu: %s\n", \
				       (t), isc_thread_self(), (m))
#define XTHREADTRACE(m)		printf("thread %lu: %s\n", \
				       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

Bob Halley's avatar
Bob Halley committed
143
	(void)isc_mutex_destroy(&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 193
	if (exiting) {
		isc_mutex_destroy(&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 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
static inline isc_boolean_t
task_shutdown(isc_task_t *task) {
	isc_boolean_t was_idle = ISC_FALSE;
	isc_event_t *event, *prev;
	
	/*
	 * 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 343
	REQUIRE(event->ev_sender != NULL);
	REQUIRE(event->ev_type > 0);
Bob Halley's avatar
Bob Halley committed
344
	REQUIRE(task->state != task_state_done);
Bob Halley's avatar
base  
Bob Halley committed
345

346 347
	XTRACE("task_send");

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	LOCK(&task->lock);
460 461

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

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

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

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

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

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

517 518
	XTRACE("isc_task_purge");

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

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

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

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

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

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

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

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

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

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

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

	return (result);
}

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

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

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

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

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

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

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

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

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

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

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

682
}
Bob Halley's avatar
Bob Halley committed
683

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

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

707
	XTHREADTRACE("start");
Bob Halley's avatar
base  
Bob Halley committed
708 709 710 711 712 713 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 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764

	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.
	 * 
	 * 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.
	 * 
	 * 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
765
		 * isc_task_send() above, it is safe for us to dequeue
Bob Halley's avatar
base  
Bob Halley committed
766 767 768 769 770
		 * 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)) {
771
			XTHREADTRACE("wait");
Bob Halley's avatar
base  
Bob Halley committed
772
			WAIT(&manager->work_available, &manager->lock);
773
			XTHREADTRACE("awake");
Bob Halley's avatar
base  
Bob Halley committed
774
		}
775
		XTHREADTRACE("working");
Bob Halley's avatar
base  
Bob Halley committed
776 777 778
		
		task = HEAD(manager->ready_tasks);
		if (task != NULL) {
Bob Halley's avatar
Bob Halley committed
779
			unsigned int dispatch_count = 0;
Bob Halley's avatar
Bob Halley committed
780 781
			isc_boolean_t done = ISC_FALSE;
			isc_boolean_t requeue = ISC_FALSE;
782
			isc_boolean_t finished = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
783
			isc_event_t *event;
Bob Halley's avatar
base  
Bob Halley committed
784 785 786 787 788 789 790 791 792 793 794 795

			INSIST(VALID_TASK(task));

			/*
			 * Note we only unlock the manager lock if we actually
			 * have a task to do.  We must reacquire the manager 
			 * 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
796
			INSIST(task->state == task_state_ready);
797 798 799 800 801
			task->state = task_state_running;
			XTRACE("running");
			do {
				if (!EMPTY(task->events)) {
					event = HEAD(task->events);
802
					DEQUEUE(task->events, event, ev_link);
Bob Halley's avatar
base  
Bob Halley committed
803

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

Bob Halley's avatar
Bob Halley committed
816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
				if (task->references == 0 &&
				    EMPTY(task->events) &&
				    !TASK_SHUTTINGDOWN(task)) {
					isc_boolean_t was_idle;
					
					/*
					 * 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);
845 846
				}

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

883 884
			if (finished)
				task_finished(task);
Bob Halley's avatar
base  
Bob Halley committed
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 913

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

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

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

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

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

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

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

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

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

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

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

	if (started == 0) {
		manager_free(manager);
Bob Halley's avatar
Bob Halley committed
1001
		return (ISC_R_NOTHREADS);
Bob Halley's avatar
base  
Bob Halley committed
1002 1003 1004 1005
	}		

	*managerp = manager;

Bob Halley's avatar
Bob Halley committed
1006
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
1007 1008
}

Bob Halley's avatar
update  
Bob Halley committed
1009
void
Bob Halley's avatar
Bob Halley committed
1010 1011 1012
isc_taskmgr_destroy(isc_taskmgr_t **managerp) {
	isc_taskmgr_t *manager;
	isc_task_t *task;
1013
	unsigned int i;
Bob Halley's avatar
base  
Bob Halley committed
1014

1015 1016 1017 1018
	/*
	 * Destroy '*managerp'.
	 */

Bob Halley's avatar
base  
Bob Halley committed
1019 1020 1021 1022
	REQUIRE(managerp != NULL);
	manager = *managerp;
	REQUIRE(VALID_MANAGER(manager));

1023
	XTHREADTRACE("isc_taskmgr_destroy");
Bob Halley's avatar
base  
Bob Halley committed
1024 1025 1026 1027
	/*
	 * Only one non-worker thread may ever call this routine.
	 * If a worker thread wants to initiate shutdown of the
	 * task manager, it should ask some non-worker thread to call
Bob Halley's avatar
Bob Halley committed
1028
	 * isc_taskmgr_destroy(), e.g. by signalling a condition variable
Bob Halley's avatar
base  
Bob Halley committed
1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039