task.c 30 KB
Newer Older
Bob Halley's avatar
Bob Halley committed
1
/*
Brian Wellington's avatar
Brian Wellington committed
2
 * Copyright (C) 1998-2001  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.84 2001/02/13 04:11:41 gson Exp $ */
David Lawrence's avatar
David Lawrence committed
19

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

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

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

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

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

45 46
#define ISC_TASK_NAMES 1

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

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

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

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

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

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

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

128
#define DEFAULT_TASKMGR_QUANTUM		10
Bob Halley's avatar
Bob Halley committed
129 130
#define DEFAULT_DEFAULT_QUANTUM		5
#define FINISHED(m)			((m)->exiting && EMPTY((m)->tasks))
Bob Halley's avatar
Bob Halley committed
131

132 133
#ifndef ISC_PLATFORM_USETHREADS
static isc_taskmgr_t *taskmgr = NULL;
134
#endif /* ISC_PLATFORM_USETHREADS */
135

Bob Halley's avatar
base  
Bob Halley committed
136 137 138 139 140
/***
 *** Tasks.
 ***/

static void
141
task_finished(isc_task_t *task) {
Bob Halley's avatar
Bob Halley committed
142
	isc_taskmgr_t *manager = task->manager;
Bob Halley's avatar
base  
Bob Halley committed
143 144

	REQUIRE(EMPTY(task->events));
145
	REQUIRE(EMPTY(task->on_shutdown));
146 147 148 149
	REQUIRE(task->references == 0);
	REQUIRE(task->state == task_state_done);

	XTRACE("task_finished");
Bob Halley's avatar
base  
Bob Halley committed
150 151 152

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

Brian Wellington's avatar
Brian Wellington committed
166
	DESTROYLOCK(&task->lock);
Bob Halley's avatar
base  
Bob Halley committed
167
	task->magic = 0;
168
	isc_mem_put(manager->mctx, task, sizeof *task);
Bob Halley's avatar
base  
Bob Halley committed
169 170
}

Bob Halley's avatar
Bob Halley committed
171
isc_result_t
172
isc_task_create(isc_taskmgr_t *manager, unsigned int quantum,
173
		isc_task_t **taskp)
Bob Halley's avatar
Bob Halley committed
174
{
Bob Halley's avatar
Bob Halley committed
175
	isc_task_t *task;
176
	isc_boolean_t exiting;
Bob Halley's avatar
base  
Bob Halley committed
177 178 179 180

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

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

207
	exiting = ISC_FALSE;
Bob Halley's avatar
base  
Bob Halley committed
208
	LOCK(&manager->lock);
209 210 211 212 213 214
	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
215 216
	UNLOCK(&manager->lock);

217
	if (exiting) {
Brian Wellington's avatar
Brian Wellington committed
218
		DESTROYLOCK(&task->lock);
219
		isc_mem_put(manager->mctx, task, sizeof *task);
220 221 222
		return (ISC_R_SHUTTINGDOWN);
	}

223
	task->magic = TASK_MAGIC;
Bob Halley's avatar
base  
Bob Halley committed
224 225
	*taskp = task;

Bob Halley's avatar
Bob Halley committed
226
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
227 228
}

Bob Halley's avatar
update  
Bob Halley committed
229
void
230
isc_task_attach(isc_task_t *source, isc_task_t **targetp) {
Bob Halley's avatar
base  
Bob Halley committed
231

232 233 234
	/*
	 * Attach *targetp to source.
	 */
Bob Halley's avatar
base  
Bob Halley committed
235

236 237
	REQUIRE(VALID_TASK(source));
	REQUIRE(targetp != NULL && *targetp == NULL);
Bob Halley's avatar
base  
Bob Halley committed
238

239
	XTTRACE(source, "isc_task_attach");
240

241 242 243 244 245
	LOCK(&source->lock);
	source->references++;
	UNLOCK(&source->lock);

	*targetp = source;
Bob Halley's avatar
base  
Bob Halley committed
246 247
}

248 249 250 251
static inline isc_boolean_t
task_shutdown(isc_task_t *task) {
	isc_boolean_t was_idle = ISC_FALSE;
	isc_event_t *event, *prev;
252

253 254 255 256 257 258 259
	/*
	 * Caller must be holding the task's lock.
	 */

	XTRACE("task_shutdown");

	if (! TASK_SHUTTINGDOWN(task)) {
260 261
		XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
				      ISC_MSG_SHUTTINGDOWN, "shutting down"));
262 263 264 265 266 267 268 269 270 271 272 273 274 275
		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) {
276 277 278
			prev = PREV(event, ev_link);
			DEQUEUE(task->on_shutdown, event, ev_link);
			ENQUEUE(task->events, event, ev_link);
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
		}
	}

	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);
297
#ifdef ISC_PLATFORM_USETHREADS
298
	SIGNAL(&manager->work_available);
299
#endif /* ISC_PLATFORM_USETHREADS */
300 301 302 303

	UNLOCK(&manager->lock);
}

Bob Halley's avatar
Bob Halley committed
304
static inline isc_boolean_t
305 306 307 308 309 310 311 312 313 314 315
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
316 317 318 319 320 321 322
	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
323 324
		 * 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
325 326 327
		 */
		task->state = task_state_ready;
		return (ISC_TRUE);
328 329
	}

Bob Halley's avatar
Bob Halley committed
330
	return (ISC_FALSE);
331 332
}

Bob Halley's avatar
Bob Halley committed
333
void
Bob Halley's avatar
Bob Halley committed
334 335
isc_task_detach(isc_task_t **taskp) {
	isc_task_t *task;
Bob Halley's avatar
Bob Halley committed
336
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
337

338 339 340 341
	/*
	 * Detach *taskp from its task.
	 */

Bob Halley's avatar
base  
Bob Halley committed
342 343 344 345
	REQUIRE(taskp != NULL);
	task = *taskp;
	REQUIRE(VALID_TASK(task));

346 347
	XTRACE("isc_task_detach");

Bob Halley's avatar
base  
Bob Halley committed
348
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
349
	was_idle = task_detach(task);
Bob Halley's avatar
base  
Bob Halley committed
350 351
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
352
	if (was_idle)
353
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
354 355 356 357

	*taskp = NULL;
}

Bob Halley's avatar
Bob Halley committed
358 359 360
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
361
	isc_event_t *event;
362

363
	/*
364
	 * Caller must be holding the task lock.
365 366
	 */

Bob Halley's avatar
update  
Bob Halley committed
367 368
	REQUIRE(eventp != NULL);
	event = *eventp;
Bob Halley's avatar
base  
Bob Halley committed
369
	REQUIRE(event != NULL);
370
	REQUIRE(event->ev_type > 0);
Bob Halley's avatar
Bob Halley committed
371
	REQUIRE(task->state != task_state_done);
Bob Halley's avatar
base  
Bob Halley committed
372

373 374
	XTRACE("task_send");

Bob Halley's avatar
Bob Halley committed
375 376 377 378
	if (task->state == task_state_idle) {
		was_idle = ISC_TRUE;
		INSIST(EMPTY(task->events));
		task->state = task_state_ready;
379
	}
Bob Halley's avatar
Bob Halley committed
380 381
	INSIST(task->state == task_state_ready ||
	       task->state == task_state_running);
382
	ENQUEUE(task->events, event, ev_link);
Bob Halley's avatar
Bob Halley committed
383
	*eventp = NULL;
384

Bob Halley's avatar
Bob Halley committed
385
	return (was_idle);
386
}
387

Bob Halley's avatar
Bob Halley committed
388
void
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
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
406
	was_idle = task_send(task, eventp);
Bob Halley's avatar
base  
Bob Halley committed
407 408 409 410 411 412
	UNLOCK(&task->lock);

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

Bob Halley's avatar
Bob Halley committed
429
void
430
isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
Bob Halley's avatar
Bob Halley committed
431
	isc_boolean_t idle1, idle2;
432 433 434 435 436 437 438 439 440 441 442 443
	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
444

445
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
446 447
	idle1 = task_send(task, eventp);
	idle2 = task_detach(task);
448 449
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
450 451 452 453 454 455
	/*
	 * 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));
456

Bob Halley's avatar
Bob Halley committed
457 458
	if (idle1 || idle2)
		task_ready(task);
459 460

	*taskp = NULL;
Bob Halley's avatar
base  
Bob Halley committed
461 462
}

463
#define PURGE_OK(event)	(((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0)
464 465 466 467 468

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)
469
{
Bob Halley's avatar
Bob Halley committed
470
	isc_event_t *event, *next_event;
471
	unsigned int count = 0;
472

Bob Halley's avatar
Bob Halley committed
473
	REQUIRE(VALID_TASK(task));
474
	REQUIRE(last >= first);
Bob Halley's avatar
Bob Halley committed
475

476
	XTRACE("dequeue_events");
Bob Halley's avatar
Bob Halley committed
477

Bob Halley's avatar
Bob Halley committed
478
	/*
479 480 481
	 * 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.
482
	 *
483
	 * sender == NULL means "any sender", and tag == NULL means "any tag".
Bob Halley's avatar
Bob Halley committed
484 485 486
	 */

	LOCK(&task->lock);
487 488

	for (event = HEAD(task->events); event != NULL; event = next_event) {
489 490 491 492
		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) &&
493
		    (!purging || PURGE_OK(event))) {
494 495
			DEQUEUE(task->events, event, ev_link);
			ENQUEUE(*events, event, ev_link);
496
			count++;
Bob Halley's avatar
Bob Halley committed
497 498
		}
	}
499

Bob Halley's avatar
Bob Halley committed
500 501
	UNLOCK(&task->lock);

502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
	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) {
525
		next_event = NEXT(event, ev_link);
Bob Halley's avatar
Bob Halley committed
526
		isc_event_free(&event);
Bob Halley's avatar
Bob Halley committed
527
	}
528

529 530 531 532 533
	/*
	 * Note that purging never changes the state of the task.
	 */

	return (count);
Bob Halley's avatar
Bob Halley committed
534 535
}

536
unsigned int
Bob Halley's avatar
Bob Halley committed
537
isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
538
	       void *tag)
Bob Halley's avatar
Bob Halley committed
539
{
540 541 542 543
	/*
	 * Purge events from a task's event queue.
	 */

544 545
	XTRACE("isc_task_purge");

Bob Halley's avatar
Bob Halley committed
546
	return (isc_task_purgerange(task, sender, type, type, tag));
547 548
}

Bob Halley's avatar
Bob Halley committed
549 550 551 552 553 554
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.
555 556
	 *
	 * XXXRTH:  WARNING:  This method may be removed before beta.
Bob Halley's avatar
Bob Halley committed
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
	 */

	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) {
575
		next_event = NEXT(curr_event, ev_link);
576
		if (curr_event == event && PURGE_OK(event)) {
577
			DEQUEUE(task->events, curr_event, ev_link);
Bob Halley's avatar
Bob Halley committed
578 579 580 581 582 583 584 585 586 587 588 589 590
			break;
		}
	}
	UNLOCK(&task->lock);

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619
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));
}

620
isc_result_t
David Lawrence's avatar
David Lawrence committed
621 622
isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, const void *arg)
{
623 624 625 626
	isc_boolean_t disallowed = ISC_FALSE;
	isc_result_t result = ISC_R_SUCCESS;
	isc_event_t *event;

627 628 629 630 631
	/*
	 * Send a shutdown event with action 'action' and argument 'arg' when
	 * 'task' is shutdown.
	 */

632
	REQUIRE(VALID_TASK(task));
633
	REQUIRE(action != NULL);
634

635
	event = isc_event_allocate(task->manager->mctx,
636 637 638 639 640
				   NULL,
				   ISC_TASKEVENT_SHUTDOWN,
				   action,
				   arg,
				   sizeof *event);
641 642 643 644
	if (event == NULL)
		return (ISC_R_NOMEMORY);

	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
645
	if (TASK_SHUTTINGDOWN(task)) {
646
		disallowed = ISC_TRUE;
647
		result = ISC_R_SHUTTINGDOWN;
648
	} else
649
		ENQUEUE(task->on_shutdown, event, ev_link);
650 651 652
	UNLOCK(&task->lock);

	if (disallowed)
653
		isc_mem_put(task->manager->mctx, event, sizeof *event);
654 655 656 657

	return (result);
}

Bob Halley's avatar
Bob Halley committed
658
void
Bob Halley's avatar
Bob Halley committed
659
isc_task_shutdown(isc_task_t *task) {
660
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
661

662 663 664 665
	/*
	 * Shutdown 'task'.
	 */

Bob Halley's avatar
base  
Bob Halley committed
666 667 668
	REQUIRE(VALID_TASK(task));

	LOCK(&task->lock);
669
	was_idle = task_shutdown(task);
Bob Halley's avatar
base  
Bob Halley committed
670 671
	UNLOCK(&task->lock);

672 673
	if (was_idle)
		task_ready(task);
Bob Halley's avatar
Bob Halley committed
674
}
Bob Halley's avatar
base  
Bob Halley committed
675

Bob Halley's avatar
Bob Halley committed
676
void
Bob Halley's avatar
Bob Halley committed
677
isc_task_destroy(isc_task_t **taskp) {
Bob Halley's avatar
Bob Halley committed
678

679 680 681 682
	/*
	 * Destroy '*taskp'.
	 */

Bob Halley's avatar
Bob Halley committed
683 684
	REQUIRE(taskp != NULL);

Bob Halley's avatar
Bob Halley committed
685 686
	isc_task_shutdown(*taskp);
	isc_task_detach(taskp);
Bob Halley's avatar
base  
Bob Halley committed
687 688
}

689
void
David Lawrence's avatar
David Lawrence committed
690
isc_task_setname(isc_task_t *task, const char *name, void *tag) {
691 692 693 694 695 696 697 698 699 700 701 702 703 704

	/*
	 * 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
Andreas Gustafsson's avatar
Andreas Gustafsson committed
705 706
	UNUSED(name);
	UNUSED(tag);
707
#endif
Bob Halley's avatar
base  
Bob Halley committed
708

709
}
Bob Halley's avatar
Bob Halley committed
710

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

	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.
737
	 *
Bob Halley's avatar
base  
Bob Halley committed
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764
	 * 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.
765
	 *
Bob Halley's avatar
base  
Bob Halley committed
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785
	 * 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)) {
786
#ifdef ISC_PLATFORM_USETHREADS
Bob Halley's avatar
base  
Bob Halley committed
787 788
		/*
		 * For reasons similar to those given in the comment in
789
		 * isc_task_send() above, it is safe for us to dequeue
Bob Halley's avatar
base  
Bob Halley committed
790 791 792 793
		 * the task while only holding the manager lock, and then
		 * change the task to running state while only holding the
		 * task lock.
		 */
794 795 796 797
		while ((EMPTY(manager->ready_tasks) ||
		        manager->exclusive_requested) &&
		  	!FINISHED(manager)) 
	  	{
798 799 800
			XTHREADTRACE(isc_msgcat_get(isc_msgcat,
						    ISC_MSGSET_GENERAL,
						    ISC_MSG_WAIT, "wait"));
Bob Halley's avatar
base  
Bob Halley committed
801
			WAIT(&manager->work_available, &manager->lock);
802 803 804
			XTHREADTRACE(isc_msgcat_get(isc_msgcat,
						    ISC_MSGSET_TASK,
						    ISC_MSG_AWAKE, "awake"));
Bob Halley's avatar
base  
Bob Halley committed
805
		}
806
#else /* ISC_PLATFORM_USETHREADS */
807 808 809
		if (total_dispatch_count >= DEFAULT_TASKMGR_QUANTUM ||
		    EMPTY(manager->ready_tasks))
			break;
810
#endif /* ISC_PLATFORM_USETHREADS */
811 812
		XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK,
					    ISC_MSG_WORKING, "working"));
813

Bob Halley's avatar
base  
Bob Halley committed
814 815
		task = HEAD(manager->ready_tasks);
		if (task != NULL) {
Bob Halley's avatar
Bob Halley committed
816
			unsigned int dispatch_count = 0;
Bob Halley's avatar
Bob Halley committed
817 818
			isc_boolean_t done = ISC_FALSE;
			isc_boolean_t requeue = ISC_FALSE;
819
			isc_boolean_t finished = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
820
			isc_event_t *event;
Bob Halley's avatar
base  
Bob Halley committed
821 822 823 824 825

			INSIST(VALID_TASK(task));

			/*
			 * Note we only unlock the manager lock if we actually
826
			 * have a task to do.  We must reacquire the manager
Bob Halley's avatar
base  
Bob Halley committed
827 828 829
			 * lock before exiting the 'if (task != NULL)' block.
			 */
			DEQUEUE(manager->ready_tasks, task, ready_link);
830
			manager->tasks_running++;
Bob Halley's avatar
base  
Bob Halley committed
831 832 833
			UNLOCK(&manager->lock);

			LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
834
			INSIST(task->state == task_state_ready);
835
			task->state = task_state_running;
836 837
			XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
					      ISC_MSG_RUNNING, "running"));
838 839 840
			do {
				if (!EMPTY(task->events)) {
					event = HEAD(task->events);
841
					DEQUEUE(task->events, event, ev_link);
Bob Halley's avatar
base  
Bob Halley committed
842

843 844 845
					/*
					 * Execute the event action.
					 */
846 847 848 849
					XTRACE(isc_msgcat_get(isc_msgcat,
							    ISC_MSGSET_TASK,
							    ISC_MSG_EXECUTE,
							    "execute action"));
850
					if (event->ev_action != NULL) {
851
						UNLOCK(&task->lock);
852
						(event->ev_action)(task,event);
853 854 855
						LOCK(&task->lock);
					}
					dispatch_count++;
856 857
#ifndef ISC_PLATFORM_USETHREADS
					total_dispatch_count++;
858
#endif /* ISC_PLATFORM_USETHREADS */
859
				}
860

Bob Halley's avatar
Bob Halley committed
861 862 863 864
				if (task->references == 0 &&
				    EMPTY(task->events) &&
				    !TASK_SHUTTINGDOWN(task)) {
					isc_boolean_t was_idle;
865

Bob Halley's avatar
Bob Halley committed
866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889
					/*
					 * 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);
890 891
				}

892
				if (EMPTY(task->events)) {
Bob Halley's avatar
Bob Halley committed
893
					/*
894
					 * Nothing else to do for this task
Bob Halley's avatar
Bob Halley committed
895
					 * right now.
Bob Halley's avatar
Bob Halley committed
896
					 */
897 898 899 900
					XTRACE(isc_msgcat_get(isc_msgcat,
							      ISC_MSGSET_TASK,
							      ISC_MSG_EMPTY,
							      "empty"));
Bob Halley's avatar
Bob Halley committed
901 902 903 904 905
					if (task->references == 0 &&
					    TASK_SHUTTINGDOWN(task)) {
						/*
						 * The task is done.
						 */
906 907 908 909 910
						XTRACE(isc_msgcat_get(
							       isc_msgcat,
							       ISC_MSGSET_TASK,
							       ISC_MSG_DONE,
							       "done"));
Bob Halley's avatar
Bob Halley committed
911
						finished = ISC_TRUE;
912
						task->state = task_state_done;
913 914
					} else
						task->state = task_state_idle;
Bob Halley's avatar
Bob Halley committed
915
					done = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
916 917 918 919 920 921 922 923 924 925 926
				} 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.
					 */
927 928 929 930
					XTRACE(isc_msgcat_get(isc_msgcat,
							      ISC_MSGSET_TASK,
							      ISC_MSG_QUANTUM,
							      "quantum"));
Bob Halley's avatar
base  
Bob Halley committed
931
					task->state = task_state_ready;
Bob Halley's avatar
Bob Halley committed
932 933
					requeue = ISC_TRUE;
					done = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
934
				}
935
			} while (!done);
Bob Halley's avatar
base  
Bob Halley committed
936 937
			UNLOCK(&task->lock);

938 939
			if (finished)
				task_finished(task);
Bob Halley's avatar
base  
Bob Halley committed
940 941

			LOCK(&manager->lock);
942 943 944 945 946 947 948
			manager->tasks_running--;
#ifdef ISC_PLATFORM_USETHREADS
			if (manager->exclusive_requested &&
			    manager->tasks_running == 1) {
				SIGNAL(&manager->exclusive_granted);
			}
#endif /* ISC_PLATFORM_USETHREADS */
Bob Halley's avatar
base  
Bob Halley committed
949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974
			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);
975 976 977 978 979 980 981 982