task.c 25.9 KB
Newer Older
Bob Halley's avatar
Bob Halley committed
1
/*
David Lawrence's avatar
David Lawrence committed
2
 * Copyright (C) 1998-2000  Internet Software Consortium.
3
 *
Bob Halley's avatar
Bob Halley committed
4 5 6
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
7
 *
8 9 10 11 12 13 14 15
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Bob Halley's avatar
Bob Halley committed
16
 */
Bob Halley's avatar
base  
Bob Halley committed
17

18
/* $Id: task.c,v 1.72 2000/08/29 22:30:14 bwelling Exp $ */
David Lawrence's avatar
David Lawrence committed
19

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

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

Bob Halley's avatar
Bob Halley committed
29 30
#include <config.h>

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

39 40 41 42
#ifndef ISC_PLATFORM_USETHREADS
#include "task_p.h"
#endif

43 44
#define ISC_TASK_NAMES 1

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

Bob Halley's avatar
Bob Halley committed
58
/***
Bob Halley's avatar
Bob Halley committed
59
 *** Types.
Bob Halley's avatar
Bob Halley committed
60 61
 ***/

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

Bob Halley's avatar
Bob Halley committed
92
#define TASK_F_SHUTTINGDOWN		0x01
93

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

Bob Halley's avatar
Bob Halley committed
97 98 99 100
#define TASK_MANAGER_MAGIC		0x54534B4DU	/* TSKM. */
#define VALID_MANAGER(m)		((m) != NULL && \
					 (m)->magic == TASK_MANAGER_MAGIC)

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

Bob Halley's avatar
Bob Halley committed
123 124
#define DEFAULT_DEFAULT_QUANTUM		5
#define FINISHED(m)			((m)->exiting && EMPTY((m)->tasks))
Bob Halley's avatar
Bob Halley committed
125

126 127 128 129
#ifndef ISC_PLATFORM_USETHREADS
static isc_taskmgr_t *taskmgr = NULL;
#endif

Bob Halley's avatar
base  
Bob Halley committed
130 131 132 133 134
/***
 *** Tasks.
 ***/

static void
135
task_finished(isc_task_t *task) {
Bob Halley's avatar
Bob Halley committed
136
	isc_taskmgr_t *manager = task->manager;
Bob Halley's avatar
base  
Bob Halley committed
137 138

	REQUIRE(EMPTY(task->events));
139
	REQUIRE(EMPTY(task->on_shutdown));
140 141 142 143
	REQUIRE(task->references == 0);
	REQUIRE(task->state == task_state_done);

	XTRACE("task_finished");
Bob Halley's avatar
base  
Bob Halley committed
144 145 146

	LOCK(&manager->lock);
	UNLINK(manager->tasks, task, link);
147
#ifdef ISC_PLATFORM_USETHREADS
Bob Halley's avatar
base  
Bob Halley committed
148 149 150 151 152 153 154 155 156
	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);
	}
157
#endif
Bob Halley's avatar
base  
Bob Halley committed
158
	UNLOCK(&manager->lock);
159

Brian Wellington's avatar
Brian Wellington committed
160
	DESTROYLOCK(&task->lock);
Bob Halley's avatar
base  
Bob Halley committed
161
	task->magic = 0;
162
	isc_mem_put(manager->mctx, task, sizeof *task);
Bob Halley's avatar
base  
Bob Halley committed
163 164
}

Bob Halley's avatar
Bob Halley committed
165
isc_result_t
166
isc_task_create(isc_taskmgr_t *manager, unsigned int quantum,
167
		isc_task_t **taskp)
Bob Halley's avatar
Bob Halley committed
168
{
Bob Halley's avatar
Bob Halley committed
169
	isc_task_t *task;
170
	isc_boolean_t exiting;
Bob Halley's avatar
base  
Bob Halley committed
171 172 173 174

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

175
	task = isc_mem_get(manager->mctx, sizeof *task);
Bob Halley's avatar
base  
Bob Halley committed
176
	if (task == NULL)
Bob Halley's avatar
Bob Halley committed
177
		return (ISC_R_NOMEMORY);
178
	XTRACE("create");
Bob Halley's avatar
base  
Bob Halley committed
179
	task->manager = manager;
Bob Halley's avatar
Bob Halley committed
180
	if (isc_mutex_init(&task->lock) != ISC_R_SUCCESS) {
181
		isc_mem_put(manager->mctx, task, sizeof *task);
Bob Halley's avatar
Bob Halley committed
182 183 184
		UNEXPECTED_ERROR(__FILE__, __LINE__,
				 "isc_mutex_init() failed");
		return (ISC_R_UNEXPECTED);
Bob Halley's avatar
update  
Bob Halley committed
185
	}
Bob Halley's avatar
base  
Bob Halley committed
186 187 188
	task->state = task_state_idle;
	task->references = 1;
	INIT_LIST(task->events);
189
	INIT_LIST(task->on_shutdown);
Bob Halley's avatar
base  
Bob Halley committed
190
	task->quantum = quantum;
Bob Halley's avatar
Bob Halley committed
191
	task->flags = 0;
192
#ifdef ISC_TASK_NAMES
Mark Andrews's avatar
Mark Andrews committed
193
	memset(task->name, 0, sizeof task->name);
194 195
	task->tag = NULL;
#endif
Bob Halley's avatar
base  
Bob Halley committed
196 197 198
	INIT_LINK(task, link);
	INIT_LINK(task, ready_link);

199
	exiting = ISC_FALSE;
Bob Halley's avatar
base  
Bob Halley committed
200
	LOCK(&manager->lock);
201 202 203 204 205 206
	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
207 208
	UNLOCK(&manager->lock);

209
	if (exiting) {
Brian Wellington's avatar
Brian Wellington committed
210
		DESTROYLOCK(&task->lock);
211
		isc_mem_put(manager->mctx, task, sizeof *task);
212 213 214
		return (ISC_R_SHUTTINGDOWN);
	}

215
	task->magic = TASK_MAGIC;
Bob Halley's avatar
base  
Bob Halley committed
216 217
	*taskp = task;

Bob Halley's avatar
Bob Halley committed
218
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
219 220
}

Bob Halley's avatar
update  
Bob Halley committed
221
void
222
isc_task_attach(isc_task_t *source, isc_task_t **targetp) {
Bob Halley's avatar
base  
Bob Halley committed
223

224 225 226
	/*
	 * Attach *targetp to source.
	 */
Bob Halley's avatar
base  
Bob Halley committed
227

228 229
	REQUIRE(VALID_TASK(source));
	REQUIRE(targetp != NULL && *targetp == NULL);
Bob Halley's avatar
base  
Bob Halley committed
230

231 232
	XTTRACE(source, "attach");

233 234 235 236 237
	LOCK(&source->lock);
	source->references++;
	UNLOCK(&source->lock);

	*targetp = source;
Bob Halley's avatar
base  
Bob Halley committed
238 239
}

240 241 242 243
static inline isc_boolean_t
task_shutdown(isc_task_t *task) {
	isc_boolean_t was_idle = ISC_FALSE;
	isc_event_t *event, *prev;
244

245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
	/*
	 * 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) {
267 268 269
			prev = PREV(event, ev_link);
			DEQUEUE(task->on_shutdown, event, ev_link);
			ENQUEUE(task->events, event, ev_link);
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
		}
	}

	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);
288
#ifdef ISC_PLATFORM_USETHREADS
289
	SIGNAL(&manager->work_available);
290
#endif
291 292 293 294

	UNLOCK(&manager->lock);
}

Bob Halley's avatar
Bob Halley committed
295
static inline isc_boolean_t
296 297 298 299 300 301 302 303 304 305 306
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
307 308 309 310 311 312 313
	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
314 315
		 * make the task ready and allow run() or the event
		 * loop to deal with shutting down and termination.
Bob Halley's avatar
Bob Halley committed
316 317 318
		 */
		task->state = task_state_ready;
		return (ISC_TRUE);
319 320
	}

Bob Halley's avatar
Bob Halley committed
321
	return (ISC_FALSE);
322 323
}

Bob Halley's avatar
Bob Halley committed
324
void
Bob Halley's avatar
Bob Halley committed
325 326
isc_task_detach(isc_task_t **taskp) {
	isc_task_t *task;
Bob Halley's avatar
Bob Halley committed
327
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
328

329 330 331 332
	/*
	 * Detach *taskp from its task.
	 */

Bob Halley's avatar
base  
Bob Halley committed
333 334 335 336
	REQUIRE(taskp != NULL);
	task = *taskp;
	REQUIRE(VALID_TASK(task));

337 338
	XTRACE("isc_task_detach");

Bob Halley's avatar
base  
Bob Halley committed
339
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
340
	was_idle = task_detach(task);
Bob Halley's avatar
base  
Bob Halley committed
341 342
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
343
	if (was_idle)
344
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
345 346 347 348

	*taskp = NULL;
}

Bob Halley's avatar
Bob Halley committed
349 350 351
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
352
	isc_event_t *event;
353

354
	/*
355
	 * Caller must be holding the task lock.
356 357
	 */

Bob Halley's avatar
update  
Bob Halley committed
358 359
	REQUIRE(eventp != NULL);
	event = *eventp;
Bob Halley's avatar
base  
Bob Halley committed
360
	REQUIRE(event != NULL);
361
	REQUIRE(event->ev_type > 0);
Bob Halley's avatar
Bob Halley committed
362
	REQUIRE(task->state != task_state_done);
Bob Halley's avatar
base  
Bob Halley committed
363

364 365
	XTRACE("task_send");

Bob Halley's avatar
Bob Halley committed
366 367 368 369
	if (task->state == task_state_idle) {
		was_idle = ISC_TRUE;
		INSIST(EMPTY(task->events));
		task->state = task_state_ready;
370
	}
Bob Halley's avatar
Bob Halley committed
371 372
	INSIST(task->state == task_state_ready ||
	       task->state == task_state_running);
373
	ENQUEUE(task->events, event, ev_link);
Bob Halley's avatar
Bob Halley committed
374
	*eventp = NULL;
375

Bob Halley's avatar
Bob Halley committed
376
	return (was_idle);
377
}
378

Bob Halley's avatar
Bob Halley committed
379
void
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
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
397
	was_idle = task_send(task, eventp);
Bob Halley's avatar
base  
Bob Halley committed
398 399 400 401 402 403
	UNLOCK(&task->lock);

	if (was_idle) {
		/*
		 * We need to add this task to the ready queue.
		 *
404 405 406
		 * 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
407 408
		 *
		 * We've changed the state to ready, so no one else will
409 410 411
		 * 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,
412
		 * removed, or a shutdown is started in the interval
413 414
		 * 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
415
		 */
416
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
417
	}
418 419
}

Bob Halley's avatar
Bob Halley committed
420
void
421
isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
Bob Halley's avatar
Bob Halley committed
422
	isc_boolean_t idle1, idle2;
423 424 425 426 427 428 429 430 431 432 433 434
	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
435

436
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
437 438
	idle1 = task_send(task, eventp);
	idle2 = task_detach(task);
439 440
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
441 442 443 444 445 446
	/*
	 * 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));
447

Bob Halley's avatar
Bob Halley committed
448 449
	if (idle1 || idle2)
		task_ready(task);
450 451

	*taskp = NULL;
Bob Halley's avatar
base  
Bob Halley committed
452 453
}

454
#define PURGE_OK(event)	(((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0)
455 456 457 458 459

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)
460
{
Bob Halley's avatar
Bob Halley committed
461
	isc_event_t *event, *next_event;
462
	unsigned int count = 0;
463

Bob Halley's avatar
Bob Halley committed
464
	REQUIRE(VALID_TASK(task));
465
	REQUIRE(last >= first);
Bob Halley's avatar
Bob Halley committed
466

467
	XTRACE("dequeue_events");
Bob Halley's avatar
Bob Halley committed
468

Bob Halley's avatar
Bob Halley committed
469
	/*
470 471 472
	 * 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.
473
	 *
474
	 * sender == NULL means "any sender", and tag == NULL means "any tag".
Bob Halley's avatar
Bob Halley committed
475 476 477
	 */

	LOCK(&task->lock);
478 479

	for (event = HEAD(task->events); event != NULL; event = next_event) {
480 481 482 483
		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) &&
484
		    (!purging || PURGE_OK(event))) {
485 486
			DEQUEUE(task->events, event, ev_link);
			ENQUEUE(*events, event, ev_link);
487
			count++;
Bob Halley's avatar
Bob Halley committed
488 489
		}
	}
490

Bob Halley's avatar
Bob Halley committed
491 492
	UNLOCK(&task->lock);

493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
	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) {
516
		next_event = NEXT(event, ev_link);
Bob Halley's avatar
Bob Halley committed
517
		isc_event_free(&event);
Bob Halley's avatar
Bob Halley committed
518
	}
519

520 521 522 523 524
	/*
	 * Note that purging never changes the state of the task.
	 */

	return (count);
Bob Halley's avatar
Bob Halley committed
525 526
}

527
unsigned int
Bob Halley's avatar
Bob Halley committed
528
isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
529
	       void *tag)
Bob Halley's avatar
Bob Halley committed
530
{
531 532 533 534
	/*
	 * Purge events from a task's event queue.
	 */

535 536
	XTRACE("isc_task_purge");

Bob Halley's avatar
Bob Halley committed
537
	return (isc_task_purgerange(task, sender, type, type, tag));
538 539
}

Bob Halley's avatar
Bob Halley committed
540 541 542 543 544 545
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.
546 547
	 *
	 * XXXRTH:  WARNING:  This method may be removed before beta.
Bob Halley's avatar
Bob Halley committed
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
	 */

	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) {
566
		next_event = NEXT(curr_event, ev_link);
567
		if (curr_event == event && PURGE_OK(event)) {
568
			DEQUEUE(task->events, curr_event, ev_link);
Bob Halley's avatar
Bob Halley committed
569 570 571 572 573 574 575 576 577 578 579 580 581
			break;
		}
	}
	UNLOCK(&task->lock);

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
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));
}

611
isc_result_t
David Lawrence's avatar
David Lawrence committed
612 613
isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, const void *arg)
{
614 615 616 617
	isc_boolean_t disallowed = ISC_FALSE;
	isc_result_t result = ISC_R_SUCCESS;
	isc_event_t *event;

618 619 620 621 622
	/*
	 * Send a shutdown event with action 'action' and argument 'arg' when
	 * 'task' is shutdown.
	 */

623
	REQUIRE(VALID_TASK(task));
624
	REQUIRE(action != NULL);
625

626
	event = isc_event_allocate(task->manager->mctx,
627 628 629 630 631
				   NULL,
				   ISC_TASKEVENT_SHUTDOWN,
				   action,
				   arg,
				   sizeof *event);
632 633 634 635
	if (event == NULL)
		return (ISC_R_NOMEMORY);

	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
636
	if (TASK_SHUTTINGDOWN(task)) {
637
		disallowed = ISC_TRUE;
638
		result = ISC_R_SHUTTINGDOWN;
639
	} else
640
		ENQUEUE(task->on_shutdown, event, ev_link);
641 642 643
	UNLOCK(&task->lock);

	if (disallowed)
644
		isc_mem_put(task->manager->mctx, event, sizeof *event);
645 646 647 648

	return (result);
}

Bob Halley's avatar
Bob Halley committed
649
void
Bob Halley's avatar
Bob Halley committed
650
isc_task_shutdown(isc_task_t *task) {
651
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
652

653 654 655 656
	/*
	 * Shutdown 'task'.
	 */

Bob Halley's avatar
base  
Bob Halley committed
657 658 659
	REQUIRE(VALID_TASK(task));

	LOCK(&task->lock);
660
	was_idle = task_shutdown(task);
Bob Halley's avatar
base  
Bob Halley committed
661 662
	UNLOCK(&task->lock);

663 664
	if (was_idle)
		task_ready(task);
Bob Halley's avatar
Bob Halley committed
665
}
Bob Halley's avatar
base  
Bob Halley committed
666

Bob Halley's avatar
Bob Halley committed
667
void
Bob Halley's avatar
Bob Halley committed
668
isc_task_destroy(isc_task_t **taskp) {
Bob Halley's avatar
Bob Halley committed
669

670 671 672 673
	/*
	 * Destroy '*taskp'.
	 */

Bob Halley's avatar
Bob Halley committed
674 675
	REQUIRE(taskp != NULL);

Bob Halley's avatar
Bob Halley committed
676 677
	isc_task_shutdown(*taskp);
	isc_task_detach(taskp);
Bob Halley's avatar
base  
Bob Halley committed
678 679
}

680
void
David Lawrence's avatar
David Lawrence committed
681
isc_task_setname(isc_task_t *task, const char *name, void *tag) {
682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698

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

700
}
Bob Halley's avatar
Bob Halley committed
701

702 703 704 705 706 707 708 709 710 711 712
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
713 714 715
/***
 *** Task Manager.
 ***/
716 717
static void
dispatch(isc_taskmgr_t *manager) {
Bob Halley's avatar
Bob Halley committed
718
	isc_task_t *task;
Bob Halley's avatar
base  
Bob Halley committed
719 720 721 722 723 724

	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.
725
	 *
Bob Halley's avatar
base  
Bob Halley committed
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
	 * 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.
753
	 *
Bob Halley's avatar
base  
Bob Halley committed
754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
	 * 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);
773 774 775
#ifndef ISC_PLATFORM_USETHREADS
	while (!EMPTY(manager->ready_tasks) && !FINISHED(manager)) {
#else
Bob Halley's avatar
base  
Bob Halley committed
776 777 778
	while (!FINISHED(manager)) {
		/*
		 * For reasons similar to those given in the comment in
779
		 * isc_task_send() above, it is safe for us to dequeue
Bob Halley's avatar
base  
Bob Halley committed
780 781 782 783 784
		 * 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)) {
785
			XTHREADTRACE("wait");
Bob Halley's avatar
base  
Bob Halley committed
786
			WAIT(&manager->work_available, &manager->lock);
787
			XTHREADTRACE("awake");
Bob Halley's avatar
base  
Bob Halley committed
788
		}
789
#endif
790
		XTHREADTRACE("working");
791

Bob Halley's avatar
base  
Bob Halley committed
792 793
		task = HEAD(manager->ready_tasks);
		if (task != NULL) {
Bob Halley's avatar
Bob Halley committed
794
			unsigned int dispatch_count = 0;
Bob Halley's avatar
Bob Halley committed
795 796
			isc_boolean_t done = ISC_FALSE;
			isc_boolean_t requeue = ISC_FALSE;
797
			isc_boolean_t finished = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
798
			isc_event_t *event;
Bob Halley's avatar
base  
Bob Halley committed
799 800 801 802 803

			INSIST(VALID_TASK(task));

			/*
			 * Note we only unlock the manager lock if we actually
804
			 * have a task to do.  We must reacquire the manager
Bob Halley's avatar
base  
Bob Halley committed
805 806 807 808 809 810
			 * 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
811
			INSIST(task->state == task_state_ready);
812 813 814 815 816
			task->state = task_state_running;
			XTRACE("running");
			do {
				if (!EMPTY(task->events)) {
					event = HEAD(task->events);
817
					DEQUEUE(task->events, event, ev_link);
Bob Halley's avatar
base  
Bob Halley committed
818

819 820 821 822
					/*
					 * Execute the event action.
					 */
					XTRACE("execute action");
823
					if (event->ev_action != NULL) {
824
						UNLOCK(&task->lock);
825
						(event->ev_action)(task,event);
826 827 828
						LOCK(&task->lock);
					}
					dispatch_count++;
829
				}
830

Bob Halley's avatar
Bob Halley committed
831 832 833 834
				if (task->references == 0 &&
				    EMPTY(task->events) &&
				    !TASK_SHUTTINGDOWN(task)) {
					isc_boolean_t was_idle;
835

Bob Halley's avatar
Bob Halley committed
836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859
					/*
					 * 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);
860 861
				}

862
				if (EMPTY(task->events)) {
Bob Halley's avatar
Bob Halley committed
863
					/*
864
					 * Nothing else to do for this task
Bob Halley's avatar
Bob Halley committed
865
					 * right now.
Bob Halley's avatar
Bob Halley committed
866
					 */
Bob Halley's avatar
Bob Halley committed
867
					XTRACE("empty");
Bob Halley's avatar
Bob Halley committed
868 869 870 871 872
					if (task->references == 0 &&
					    TASK_SHUTTINGDOWN(task)) {
						/*
						 * The task is done.
						 */
873
						XTRACE("done");
Bob Halley's avatar
Bob Halley committed
874
						finished = ISC_TRUE;
875
						task->state = task_state_done;
876 877
					} else
						task->state = task_state_idle;
Bob Halley's avatar
Bob Halley committed
878
					done = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
879 880 881 882 883 884 885 886 887 888 889
				} 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
890
					XTRACE("quantum");
Bob Halley's avatar
base  
Bob Halley committed
891
					task->state = task_state_ready;
Bob Halley's avatar
Bob Halley committed
892 893
					requeue = ISC_TRUE;
					done = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
894
				}
895
			} while (!done);
Bob Halley's avatar
base  
Bob Halley committed
896 897
			UNLOCK(&task->lock);

898 899
			if (finished)
				task_finished(task);
Bob Halley's avatar
base  
Bob Halley committed
900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927

			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);
928 929 930 931 932 933 934 935 936 937 938 939 940
}

#ifdef ISC_PLATFORM_USETHREADS
static isc_threadresult_t
#ifdef _WIN32
WINAPI
#endif
run(void *uap) {
	isc_taskmgr_t *manager = uap;

	XTHREADTRACE("start");

	dispatch(manager);
Bob Halley's avatar
base  
Bob Halley committed
941

942
	XTHREADTRACE("exit");
Bob Halley's avatar
base  
Bob Halley committed
943

Bob Halley's avatar
update  
Bob Halley committed
944
	return ((isc_threadresult_t)0);
Bob Halley's avatar
base  
Bob Halley committed
945
}
946
#endif
Bob Halley's avatar
base  
Bob Halley committed
947 948

static void
Bob Halley's avatar
Bob Halley committed
949
manager_free(isc_taskmgr_t *manager) {
950 951
	isc_mem_t *mctx;

952
#ifdef ISC_PLATFORM_USETHREADS
Bob Halley's avatar
Bob Halley committed
953
	(void)isc_condition_destroy(&manager->work_available);
954 955
	isc_mem_put(manager->mctx, manager->threads,
		    manager->workers * sizeof (isc_thread_t));
956 957
#endif
	DESTROYLOCK(&manager->lock);
Bob Halley's avatar
base  
Bob Halley committed
958
	manager->magic = 0;
959 960 961
	mctx = manager->mctx;
	isc_mem_put(mctx, manager, sizeof *manager);
	isc_mem_detach(&mctx);
Bob Halley's avatar
base  
Bob Halley committed
962 963
}

Bob Halley's avatar
Bob Halley committed
964
isc_result_t
965
isc_taskmgr_create(isc_mem_t *mctx, unsigned int workers,
Bob Halley's avatar
Bob Halley committed
966
		   unsigned int default_quantum, isc_taskmgr_t **managerp)
Bob Halley's avatar
Bob Halley committed
967 968
{
	unsigned int i, started = 0;
Bob Halley's avatar
Bob Halley committed
969
	isc_taskmgr_t *manager;
Bob Halley's avatar
Bob Halley committed
970

971 972 973 974
	/*
	 * Create a new task manager.
	 */

Bob Halley's avatar
Bob Halley committed
975
	REQUIRE(workers > 0);
Bob Halley's avatar
Bob Halley committed
976
	REQUIRE(managerp != NULL && *managerp == NULL);
Bob Halley's avatar
base  
Bob Halley committed
977

978 979 980 981 982 983 984 985 986 987 988
#ifndef ISC_PLATFORM_USETHREADS
	UNUSED(i);
	UNUSED(started);

	if (taskmgr != NULL) {
		taskmgr->refs++;
		*managerp = taskmgr;
		return (ISC_R_SUCCESS);
	}
#endif

Bob Halley's avatar
Bob Halley committed
989
	manager = isc_mem_get(mctx, sizeof *manager);
Bob Halley's avatar
base  
Bob Halley committed
990
	if (manager == NULL)
Bob Halley's avatar
Bob Halley committed
991
		return (ISC_R_NOMEMORY);
Bob Halley's avatar
base  
Bob Halley committed
992
	manager->magic = TASK_MANAGER_MAGIC;
993
	manager->mctx = NULL;