task.c 24.5 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

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
isc_result_t
David Lawrence's avatar
David Lawrence committed
592 593
isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, const void *arg)
{
594 595 596 597
	isc_boolean_t disallowed = ISC_FALSE;
	isc_result_t result = ISC_R_SUCCESS;
	isc_event_t *event;

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

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

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

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

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

	return (result);
}

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

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

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

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

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

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

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

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

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

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

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

680
}
Bob Halley's avatar
Bob Halley committed
681

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

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

694
	XTHREADTRACE("start");
Bob Halley's avatar
base  
Bob Halley committed
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 751

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

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

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

Bob Halley's avatar
Bob Halley committed
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 831
				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);
832 833
				}

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

870 871
			if (finished)
				task_finished(task);
Bob Halley's avatar
base  
Bob Halley committed
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 900

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

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

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

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

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

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

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

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

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

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

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

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

	*managerp = manager;

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

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

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

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

1010
	XTHREADTRACE("isc_taskmgr_destroy");
Bob Halley's avatar
base  
Bob Halley committed
1011 1012 1013 1014
	/*
	 * 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
1015
	 * isc_taskmgr_destroy(), e.g. by signalling a condition variable
Bob Halley's avatar
base  
Bob Halley committed
1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
	 * 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
1034
	manager->exiting = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
1035 1036

	/*
1037
	 * Post shutdown event(s) to every task (if they haven't already been
Bob Halley's avatar
update  
Bob Halley committed
1038
	 * posted).