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

Bob Halley's avatar
Bob Halley committed
18 19 20 21
/*
 * Principal Author: Bob Halley
 */

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

Bob Halley's avatar
Bob Halley committed
27 28
#include <config.h>

29
#include <isc/condition.h>
30
#include <isc/event.h>
31
#include <isc/mem.h>
32
#include <isc/string.h>
Bob Halley's avatar
Bob Halley committed
33
#include <isc/task.h>
34
#include <isc/thread.h>
Michael Graff's avatar
Michael Graff committed
35
#include <isc/util.h>
Bob Halley's avatar
base  
Bob Halley committed
36

37 38
#define ISC_TASK_NAMES 1

Bob Halley's avatar
Bob Halley committed
39
#ifdef ISC_TASK_TRACE
40 41 42 43 44 45
#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
46 47
#else
#define XTRACE(m)
48
#define XTTRACE(t, m)
49
#define XTHREADTRACE(m)
Bob Halley's avatar
Bob Halley committed
50
#endif
Bob Halley's avatar
base  
Bob Halley committed
51

Bob Halley's avatar
Bob Halley committed
52
/***
Bob Halley's avatar
Bob Halley committed
53
 *** Types.
Bob Halley's avatar
Bob Halley committed
54 55
 ***/

Bob Halley's avatar
Bob Halley committed
56 57
typedef enum {
	task_state_idle, task_state_ready, task_state_running,
58
	task_state_done
Bob Halley's avatar
Bob Halley committed
59 60 61 62 63 64
} 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
65
struct isc_task {
Bob Halley's avatar
Bob Halley committed
66 67
	/* Not locked. */
	unsigned int			magic;
Bob Halley's avatar
Bob Halley committed
68
	isc_taskmgr_t *			manager;
Bob Halley's avatar
Bob Halley committed
69
	isc_mutex_t			lock;
Bob Halley's avatar
Bob Halley committed
70 71 72
	/* Locked by task lock. */
	task_state_t			state;
	unsigned int			references;
Bob Halley's avatar
Bob Halley committed
73
	isc_eventlist_t			events;
74
	isc_eventlist_t			on_shutdown;
Bob Halley's avatar
Bob Halley committed
75
	unsigned int			quantum;
76
	unsigned int			flags;
77 78 79 80
#ifdef ISC_TASK_NAMES
	char				name[16];
	void *				tag;
#endif
Bob Halley's avatar
Bob Halley committed
81
	/* Locked by task manager lock. */
Bob Halley's avatar
Bob Halley committed
82 83
	LINK(isc_task_t)		link;
	LINK(isc_task_t)		ready_link;
Bob Halley's avatar
Bob Halley committed
84 85
};

Bob Halley's avatar
Bob Halley committed
86
#define TASK_F_SHUTTINGDOWN		0x01
87

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

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

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

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

Bob Halley's avatar
base  
Bob Halley committed
113 114 115 116 117
/***
 *** Tasks.
 ***/

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

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

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

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

Bob Halley's avatar
Bob Halley committed
141
	(void)isc_mutex_destroy(&task->lock);
Bob Halley's avatar
base  
Bob Halley committed
142
	task->magic = 0;
143
	isc_mem_put(manager->mctx, task, sizeof *task);
Bob Halley's avatar
base  
Bob Halley committed
144 145
}

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

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

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

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

190 191
	if (exiting) {
		isc_mutex_destroy(&task->lock);
192
		isc_mem_put(manager->mctx, task, sizeof *task);
193 194 195
		return (ISC_R_SHUTTINGDOWN);
	}

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

Bob Halley's avatar
Bob Halley committed
199
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
200 201
}

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

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

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

212 213
	XTTRACE(source, "attach");

214 215 216 217 218
	LOCK(&source->lock);
	source->references++;
	UNLOCK(&source->lock);

	*targetp = source;
Bob Halley's avatar
base  
Bob Halley committed
219 220
}

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

	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
274
static inline isc_boolean_t
275 276 277 278 279 280 281 282 283 284 285
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
286 287 288 289 290 291 292 293 294 295 296 297
	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);
298 299
	}

Bob Halley's avatar
Bob Halley committed
300
	return (ISC_FALSE);
301 302
}

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

308 309 310 311
	/*
	 * Detach *taskp from its task.
	 */

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

316 317
	XTRACE("isc_task_detach");

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

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

	*taskp = NULL;
}

Bob Halley's avatar
Bob Halley committed
328 329 330
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
331
	isc_event_t *event;
332
	
333
	/*
334
	 * Caller must be holding the task lock.
335 336
	 */

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

344 345
	XTRACE("task_send");

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	LOCK(&task->lock);
458 459

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

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

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

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

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

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

515 516
	XTRACE("isc_task_purge");

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

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

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

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

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

591 592 593 594 595 596
isc_result_t
isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, void *arg) {
	isc_boolean_t disallowed = ISC_FALSE;
	isc_result_t result = ISC_R_SUCCESS;
	isc_event_t *event;

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

602
	REQUIRE(VALID_TASK(task));
603
	REQUIRE(action != NULL);
604

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

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

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

	return (result);
}

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

632 633 634 635
	/*
	 * Shutdown 'task'.
	 */

Bob Halley's avatar
base  
Bob Halley committed
636 637 638
	REQUIRE(VALID_TASK(task));

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

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

Bob Halley's avatar
Bob Halley committed
646
void
Bob Halley's avatar
Bob Halley committed
647
isc_task_destroy(isc_task_t **taskp) {
Bob Halley's avatar
Bob Halley committed
648

649 650 651 652
	/*
	 * Destroy '*taskp'.
	 */

Bob Halley's avatar
Bob Halley committed
653 654
	REQUIRE(taskp != NULL);

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

659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
void
isc_task_setname(isc_task_t *task, char *name, void *tag) {

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

679
}
Bob Halley's avatar
Bob Halley committed
680

Bob Halley's avatar
base  
Bob Halley committed
681 682 683 684
/***
 *** Task Manager.
 ***/

Bob Halley's avatar
update  
Bob Halley committed
685
static isc_threadresult_t
Bob Halley's avatar
Bob Halley committed
686 687 688
#ifdef _WIN32
WINAPI
#endif
Bob Halley's avatar
update  
Bob Halley committed
689
run(void *uap) {
Bob Halley's avatar
Bob Halley committed
690 691
	isc_taskmgr_t *manager = uap;
	isc_task_t *task;
Bob Halley's avatar
base  
Bob Halley committed
692

693
	XTHREADTRACE("start");
Bob Halley's avatar
base  
Bob Halley committed
694 695 696 697 698 699 700 701 702 703 704 705 706 707 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

	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
751
		 * isc_task_send() above, it is safe for us to dequeue
Bob Halley's avatar
base  
Bob Halley committed
752 753 754 755 756
		 * 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)) {
757
			XTHREADTRACE("wait");
Bob Halley's avatar
base  
Bob Halley committed
758
			WAIT(&manager->work_available, &manager->lock);
759
			XTHREADTRACE("awake");
Bob Halley's avatar
base  
Bob Halley committed
760
		}
761
		XTHREADTRACE("working");
Bob Halley's avatar
base  
Bob Halley committed
762 763 764
		
		task = HEAD(manager->ready_tasks);
		if (task != NULL) {
Bob Halley's avatar
Bob Halley committed
765
			unsigned int dispatch_count = 0;
Bob Halley's avatar
Bob Halley committed
766 767
			isc_boolean_t done = ISC_FALSE;
			isc_boolean_t requeue = ISC_FALSE;
768
			isc_boolean_t finished = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
769
			isc_event_t *event;
Bob Halley's avatar
base  
Bob Halley committed
770 771 772 773 774 775 776 777 778 779 780 781

			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
782
			INSIST(task->state == task_state_ready);
783 784 785 786 787
			task->state = task_state_running;
			XTRACE("running");
			do {
				if (!EMPTY(task->events)) {
					event = HEAD(task->events);
788
					DEQUEUE(task->events, event, ev_link);
Bob Halley's avatar
base  
Bob Halley committed
789

790 791 792 793
					/*
					 * Execute the event action.
					 */
					XTRACE("execute action");
794
					if (event->ev_action != NULL) {
795
						UNLOCK(&task->lock);
796
						(event->ev_action)(task,event);
797 798 799
						LOCK(&task->lock);
					}
					dispatch_count++;
800
				}
801

Bob Halley's avatar
Bob Halley committed
802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830
				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);
831 832
				}

833
				if (EMPTY(task->events)) {
Bob Halley's avatar
Bob Halley committed
834
					/*
835
					 * Nothing else to do for this task
Bob Halley's avatar
Bob Halley committed
836
					 * right now.
Bob Halley's avatar
Bob Halley committed
837
					 */
Bob Halley's avatar
Bob Halley committed
838
					XTRACE("empty");
Bob Halley's avatar
Bob Halley committed
839 840 841 842 843
					if (task->references == 0 &&
					    TASK_SHUTTINGDOWN(task)) {
						/*
						 * The task is done.
						 */
844
						XTRACE("done");
Bob Halley's avatar
Bob Halley committed
845
						finished = ISC_TRUE;
846
						task->state = task_state_done;
847 848
					} else
						task->state = task_state_idle;
Bob Halley's avatar
Bob Halley committed
849
					done = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
850 851 852 853 854 855 856 857 858 859 860
				} 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
861
					XTRACE("quantum");
Bob Halley's avatar
base  
Bob Halley committed
862
					task->state = task_state_ready;
Bob Halley's avatar
Bob Halley committed
863 864
					requeue = ISC_TRUE;
					done = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
865
				}
866
			} while (!done);
Bob Halley's avatar
base  
Bob Halley committed
867 868
			UNLOCK(&task->lock);

869 870
			if (finished)
				task_finished(task);
Bob Halley's avatar
base  
Bob Halley committed
871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899

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

900
	XTHREADTRACE("exit");
Bob Halley's avatar
base  
Bob Halley committed
901

Bob Halley's avatar
update  
Bob Halley committed
902
	return ((isc_threadresult_t)0);
Bob Halley's avatar
base  
Bob Halley committed
903 904 905
}

static void
Bob Halley's avatar
Bob Halley committed
906
manager_free(isc_taskmgr_t *manager) {
907 908
	isc_mem_t *mctx;

Bob Halley's avatar
Bob Halley committed
909 910
	(void)isc_condition_destroy(&manager->work_available);
	(void)isc_mutex_destroy(&manager->lock);
911 912
	isc_mem_put(manager->mctx, manager->threads,
		    manager->workers * sizeof (isc_thread_t));
Bob Halley's avatar
base  
Bob Halley committed
913
	manager->magic = 0;
914 915 916
	mctx = manager->mctx;
	isc_mem_put(mctx, manager, sizeof *manager);
	isc_mem_detach(&mctx);
Bob Halley's avatar
base  
Bob Halley committed
917 918
}

Bob Halley's avatar
Bob Halley committed
919
isc_result_t
Bob Halley's avatar
Bob Halley committed
920
isc_taskmgr_create(isc_mem_t *mctx, unsigned int workers, 
Bob Halley's avatar
Bob Halley committed
921
		   unsigned int default_quantum, isc_taskmgr_t **managerp)
Bob Halley's avatar
Bob Halley committed
922 923
{
	unsigned int i, started = 0;
Bob Halley's avatar
Bob Halley committed
924
	isc_taskmgr_t *manager;
925
	isc_thread_t *threads;
Bob Halley's avatar
Bob Halley committed
926

927 928 929 930
	/*
	 * Create a new task manager.
	 */

Bob Halley's avatar
Bob Halley committed
931
	REQUIRE(workers > 0);
Bob Halley's avatar
Bob Halley committed
932
	REQUIRE(managerp != NULL && *managerp == NULL);
Bob Halley's avatar
base  
Bob Halley committed
933

Bob Halley's avatar
Bob Halley committed
934
	manager = isc_mem_get(mctx, sizeof *manager);
Bob Halley's avatar
base  
Bob Halley committed
935
	if (manager == NULL)
Bob Halley's avatar
Bob Halley committed
936
		return (ISC_R_NOMEMORY);
Bob Halley's avatar
base  
Bob Halley committed
937
	manager->magic = TASK_MANAGER_MAGIC;
938
	manager->mctx = NULL;
939 940 941 942 943 944 945
	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
946
	if (isc_mutex_init(&manager->lock) != ISC_R_SUCCESS) {
947
		isc_mem_put(mctx, threads, workers * sizeof (isc_thread_t));
Bob Halley's avatar
Bob Halley committed
948
		isc_mem_put(mctx, manager, sizeof *manager);
Bob Halley's avatar
Bob Halley committed
949 950 951
		UNEXPECTED_ERROR(__FILE__, __LINE__,
				 "isc_mutex_init() failed");
		return (ISC_R_UNEXPECTED);
Bob Halley's avatar
update  
Bob Halley committed
952
	}
Bob Halley's avatar
base  
Bob Halley committed
953 954 955 956 957
	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
958 959
	if (isc_condition_init(&manager->work_available) != ISC_R_SUCCESS) {
		(void)isc_mutex_destroy(&manager->lock);
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_condition_init() failed");
		return (ISC_R_UNEXPECTED);
Bob Halley's avatar
update  
Bob Halley committed
965
	}
Bob Halley's avatar
Bob Halley committed
966
	manager->exiting = ISC_FALSE;
Bob Halley's avatar
base  
Bob Halley committed
967 968
	manager->workers = 0;

969 970
	isc_mem_attach(mctx, &manager->mctx);

Bob Halley's avatar
base  
Bob Halley committed
971 972 973 974 975
	LOCK(&manager->lock);
	/*
	 * Start workers.
	 */
	for (i = 0; i < workers; i++) {
976 977
		if (isc_thread_create(run, manager,
				      &manager->threads[manager->workers]) == 
Bob Halley's avatar
Bob Halley committed
978
		    ISC_R_SUCCESS) {
Bob Halley's avatar
base  
Bob Halley committed
979 980 981 982 983 984 985 986
			manager->workers++;
			started++;
		}
	}
	UNLOCK(&manager->lock);

	if (started == 0) {
		manager_free(manager);
Bob Halley's avatar
Bob Halley committed
987
		return (ISC_R_NOTHREADS);
Bob Halley's avatar
base  
Bob Halley committed
988 989 990 991
	}		

	*managerp = manager;

Bob Halley's avatar
Bob Halley committed
992
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
993 994
}

Bob Halley's avatar
update  
Bob Halley committed
995
void
Bob Halley's avatar
Bob Halley committed
996 997 998
isc_taskmgr_destroy(isc_taskmgr_t **managerp) {
	isc_taskmgr_t *manager;
	isc_task_t *task;
999
	unsigned int i;
Bob Halley's avatar
base  
Bob Halley committed
1000

1001 1002 1003 1004
	/*
	 * Destroy '*managerp'.
	 */

Bob Halley's avatar
base  
Bob Halley committed
1005 1006 1007 1008
	REQUIRE(managerp != NULL);
	manager = *managerp;
	REQUIRE(VALID_MANAGER(manager));

1009
	XTHREADTRACE("isc_taskmgr_destroy");
Bob Halley's avatar
base  
Bob Halley committed
1010 1011 1012 1013
	/*
	 * 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
1014
	 * isc_taskmgr_destroy(), e.g. by signalling a condition variable
Bob Halley's avatar
base  
Bob Halley committed
1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
	 * that the startup thread is sleeping on.
	 */

	/*
	 * Unlike elsewhere, we're going to hold this lock a long time.
	 * We need to do so, because otherwise the list of tasks could
	 * change while we were traversing it.
	 *
	 * This is also the only function where we will hold both the
	 * task manager lock and a task lock at the same time.
	 */

	LOCK(&manager->lock);

	/*
	 * Make sure we only get called once.
	 */
	INSIST(!manager->exiting);
Bob Halley's avatar
Bob Halley committed
1033
	manager->exiting = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
1034 1035

	/*
1036
	 * Post shutdown event(s) to every task (if they haven't already been
Bob Halley's avatar
update  
Bob Halley committed
1037
	 * posted).
Bob Halley's avatar
base  
Bob Halley committed
1038 1039 1040 1041 1042
	 */
	for (task = HEAD(manager->tasks);
	     task != NULL;
	     task = NEXT(task, link)) {
		LOCK(&task->lock);
1043 1044
		if (task_shutdown(task))
			ENQUEUE(manager->ready_tasks, task, ready_link);
Bob Halley's avatar
base  
Bob Halley committed