task.c 26.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.75 2000/09/28 21:31:04 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>
Brian Wellington's avatar
Brian Wellington committed
34
#include <isc/platform.h>
35
#include <isc/string.h>
Bob Halley's avatar
Bob Halley committed
36
#include <isc/task.h>
37
#include <isc/thread.h>
Michael Graff's avatar
Michael Graff committed
38
#include <isc/util.h>
Bob Halley's avatar
base  
Bob Halley committed
39

40 41
#ifndef ISC_PLATFORM_USETHREADS
#include "task_p.h"
42
#endif /* ISC_PLATFORM_USETHREADS */
43

44 45
#define ISC_TASK_NAMES 1

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

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

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

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

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

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

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

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

128 129
#ifndef ISC_PLATFORM_USETHREADS
static isc_taskmgr_t *taskmgr = NULL;
130
#endif /* ISC_PLATFORM_USETHREADS */
131

Bob Halley's avatar
base  
Bob Halley committed
132 133 134 135 136
/***
 *** Tasks.
 ***/

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

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

	XTRACE("task_finished");
Bob Halley's avatar
base  
Bob Halley committed
146 147 148

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

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

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

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

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

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

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

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

Bob Halley's avatar
Bob Halley committed
220
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
221 222
}

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

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

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

233 234
	XTTRACE(source, "attach");

235 236 237 238 239
	LOCK(&source->lock);
	source->references++;
	UNLOCK(&source->lock);

	*targetp = source;
Bob Halley's avatar
base  
Bob Halley committed
240 241
}

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

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

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

	UNLOCK(&manager->lock);
}

Bob Halley's avatar
Bob Halley committed
297
static inline isc_boolean_t
298 299 300 301 302 303 304 305 306 307 308
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
309 310 311 312 313 314 315
	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
316 317
		 * 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
318 319 320
		 */
		task->state = task_state_ready;
		return (ISC_TRUE);
321 322
	}

Bob Halley's avatar
Bob Halley committed
323
	return (ISC_FALSE);
324 325
}

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

331 332 333 334
	/*
	 * Detach *taskp from its task.
	 */

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

339 340
	XTRACE("isc_task_detach");

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

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

	*taskp = NULL;
}

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

356
	/*
357
	 * Caller must be holding the task lock.
358 359
	 */

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

366 367
	XTRACE("task_send");

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

Bob Halley's avatar
Bob Halley committed
378
	return (was_idle);
379
}
380

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

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

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

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

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

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

	*taskp = NULL;
Bob Halley's avatar
base  
Bob Halley committed
454 455
}

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

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

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

469
	XTRACE("dequeue_events");
Bob Halley's avatar
Bob Halley committed
470

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

	LOCK(&task->lock);
480 481

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

Bob Halley's avatar
Bob Halley committed
493 494
	UNLOCK(&task->lock);

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

522 523 524 525 526
	/*
	 * Note that purging never changes the state of the task.
	 */

	return (count);
Bob Halley's avatar
Bob Halley committed
527 528
}

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

537 538
	XTRACE("isc_task_purge");

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

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

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

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

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 611 612
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));
}

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

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

625
	REQUIRE(VALID_TASK(task));
626
	REQUIRE(action != NULL);
627

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

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

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

	return (result);
}

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

655 656 657 658
	/*
	 * Shutdown 'task'.
	 */

Bob Halley's avatar
base  
Bob Halley committed
659 660 661
	REQUIRE(VALID_TASK(task));

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

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

Bob Halley's avatar
Bob Halley committed
669
void
Bob Halley's avatar
Bob Halley committed
670
isc_task_destroy(isc_task_t **taskp) {
Bob Halley's avatar
Bob Halley committed
671

672 673 674 675
	/*
	 * Destroy '*taskp'.
	 */

Bob Halley's avatar
Bob Halley committed
676 677
	REQUIRE(taskp != NULL);

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

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

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

702
}
Bob Halley's avatar
Bob Halley committed
703

704 705 706 707 708 709 710 711 712 713 714
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
715 716 717
/***
 *** Task Manager.
 ***/
718 719
static void
dispatch(isc_taskmgr_t *manager) {
Bob Halley's avatar
Bob Halley committed
720
	isc_task_t *task;
721 722
#ifndef ISC_PLATFORM_USETHREADS
	unsigned int total_dispatch_count = 0;
723
#endif /* ISC_PLATFORM_USETHREADS */
Bob Halley's avatar
base  
Bob Halley committed
724 725 726 727 728 729

	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.
730
	 *
Bob Halley's avatar
base  
Bob Halley committed
731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757
	 * 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.
758
	 *
Bob Halley's avatar
base  
Bob Halley committed
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
	 * 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)) {
779
#ifdef ISC_PLATFORM_USETHREADS
Bob Halley's avatar
base  
Bob Halley committed
780 781
		/*
		 * For reasons similar to those given in the comment in
782
		 * isc_task_send() above, it is safe for us to dequeue
Bob Halley's avatar
base  
Bob Halley committed
783 784 785 786 787
		 * 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)) {
788
			XTHREADTRACE("wait");
Bob Halley's avatar
base  
Bob Halley committed
789
			WAIT(&manager->work_available, &manager->lock);
790
			XTHREADTRACE("awake");
Bob Halley's avatar
base  
Bob Halley committed
791
		}
792
#else /* ISC_PLATFORM_USETHREADS */
793 794 795
		if (total_dispatch_count >= DEFAULT_TASKMGR_QUANTUM ||
		    EMPTY(manager->ready_tasks))
			break;
796
#endif /* ISC_PLATFORM_USETHREADS */
797
		XTHREADTRACE("working");
798

Bob Halley's avatar
base  
Bob Halley committed
799 800
		task = HEAD(manager->ready_tasks);
		if (task != NULL) {
Bob Halley's avatar
Bob Halley committed
801
			unsigned int dispatch_count = 0;
Bob Halley's avatar
Bob Halley committed
802 803
			isc_boolean_t done = ISC_FALSE;
			isc_boolean_t requeue = ISC_FALSE;
804
			isc_boolean_t finished = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
805
			isc_event_t *event;
Bob Halley's avatar
base  
Bob Halley committed
806 807 808 809 810

			INSIST(VALID_TASK(task));

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

826 827 828 829
					/*
					 * Execute the event action.
					 */
					XTRACE("execute action");
830
					if (event->ev_action != NULL) {
831
						UNLOCK(&task->lock);
832
						(event->ev_action)(task,event);
833 834 835
						LOCK(&task->lock);
					}
					dispatch_count++;
836 837
#ifndef ISC_PLATFORM_USETHREADS
					total_dispatch_count++;
838
#endif /* ISC_PLATFORM_USETHREADS */
839
				}
840

Bob Halley's avatar
Bob Halley committed
841 842 843 844
				if (task->references == 0 &&
				    EMPTY(task->events) &&
				    !TASK_SHUTTINGDOWN(task)) {
					isc_boolean_t was_idle;
845

Bob Halley's avatar
Bob Halley committed
846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869
					/*
					 * 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);
870 871
				}

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

908 909
			if (finished)
				task_finished(task);
Bob Halley's avatar
base  
Bob Halley committed
910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937

			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);
938 939 940 941 942 943 944 945 946 947 948 949 950
}

#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
951

952
	XTHREADTRACE("exit");
Bob Halley's avatar
base  
Bob Halley committed
953

Bob Halley's avatar
update  
Bob Halley committed
954
	return ((isc_threadresult_t)0);
Bob Halley's avatar
base  
Bob Halley committed
955
}
956
#endif /* ISC_PLATFORM_USETHREADS */
Bob Halley's avatar
base  
Bob Halley committed
957 958

static void
Bob Halley's avatar
Bob Halley committed
959
manager_free(isc_taskmgr_t *manager) {
960 961
	isc_mem_t *mctx;

962
#ifdef ISC_PLATFORM_USETHREADS
Bob Halley's avatar
Bob Halley committed
963
	(void)isc_condition_destroy(&manager->work_available);
964 965
	isc_mem_put(manager->mctx, manager->threads,
		    manager->workers * sizeof (isc_thread_t));
966
#endif /* ISC_PLATFORM_USETHREADS */
967
	DESTROYLOCK(&manager->lock);
Bob Halley's avatar
base  
Bob Halley committed
968
	manager->magic = 0;
969 970 971
	mctx = manager->mctx;
	isc_mem_put(mctx, manager, sizeof *manager);
	isc_mem_detach(&mctx);
Bob Halley's avatar
base  
Bob Halley committed
972 973
}

Bob Halley's avatar
Bob Halley committed
974
isc_result_t
975
isc_taskmgr_create(isc_mem_t *mctx, unsigned int workers,
Bob Halley's avatar
Bob Halley committed
976
		   unsigned int default_quantum, isc_taskmgr_t **managerp)
Bob Halley's avatar
Bob Halley committed
977 978
{
	unsigned int i, started = 0;
Bob Halley's avatar
Bob Halley committed