task.c 28.2 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.77 2000/12/26 21:12:21 tale 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/event.h>
32
#include <isc/mem.h>
33
#include <isc/msgs.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>
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
#ifdef ISC_PLATFORM_USETHREADS
#include <isc/condition.h>
#include <isc/thread.h>
#else /* ISC_PLATFORM_USETHREADS */
43
#include "task_p.h"
44
#endif /* ISC_PLATFORM_USETHREADS */
45

46 47
#define ISC_TASK_NAMES 1

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

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

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

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

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

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

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

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

130 131
#ifndef ISC_PLATFORM_USETHREADS
static isc_taskmgr_t *taskmgr = NULL;
132
#endif /* ISC_PLATFORM_USETHREADS */
133

Bob Halley's avatar
base  
Bob Halley committed
134 135 136 137 138
/***
 *** Tasks.
 ***/

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

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

	XTRACE("task_finished");
Bob Halley's avatar
base  
Bob Halley committed
148 149 150

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

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

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

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

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

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

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

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

Bob Halley's avatar
Bob Halley committed
224
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
225 226
}

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

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

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

237
	XTTRACE(source, "isc_task_attach");
238

239 240 241 242 243
	LOCK(&source->lock);
	source->references++;
	UNLOCK(&source->lock);

	*targetp = source;
Bob Halley's avatar
base  
Bob Halley committed
244 245
}

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

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

	XTRACE("task_shutdown");

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

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

	UNLOCK(&manager->lock);
}

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

Bob Halley's avatar
Bob Halley committed
328
	return (ISC_FALSE);
329 330
}

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

336 337 338 339
	/*
	 * Detach *taskp from its task.
	 */

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

344 345
	XTRACE("isc_task_detach");

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

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

	*taskp = NULL;
}

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

361
	/*
362
	 * Caller must be holding the task lock.
363 364
	 */

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

371 372
	XTRACE("task_send");

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

Bob Halley's avatar
Bob Halley committed
383
	return (was_idle);
384
}
385

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

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

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

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

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

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

	*taskp = NULL;
Bob Halley's avatar
base  
Bob Halley committed
459 460
}

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

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

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

474
	XTRACE("dequeue_events");
Bob Halley's avatar
Bob Halley committed
475

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

	LOCK(&task->lock);
485 486

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

Bob Halley's avatar
Bob Halley committed
498 499
	UNLOCK(&task->lock);

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

527 528 529 530 531
	/*
	 * Note that purging never changes the state of the task.
	 */

	return (count);
Bob Halley's avatar
Bob Halley committed
532 533
}

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

542 543
	XTRACE("isc_task_purge");

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

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

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

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

589 590 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
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));
}

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

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

630
	REQUIRE(VALID_TASK(task));
631
	REQUIRE(action != NULL);
632

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

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

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

	return (result);
}

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

660 661 662 663
	/*
	 * Shutdown 'task'.
	 */

Bob Halley's avatar
base  
Bob Halley committed
664 665 666
	REQUIRE(VALID_TASK(task));

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

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

Bob Halley's avatar
Bob Halley committed
674
void
Bob Halley's avatar
Bob Halley committed
675
isc_task_destroy(isc_task_t **taskp) {
Bob Halley's avatar
Bob Halley committed
676

677 678 679 680
	/*
	 * Destroy '*taskp'.
	 */

Bob Halley's avatar
Bob Halley committed
681 682
	REQUIRE(taskp != NULL);

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

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

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

707
}
Bob Halley's avatar
Bob Halley committed
708

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

	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.
735
	 *
Bob Halley's avatar
base  
Bob Halley committed
736 737 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
	 * 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.
763
	 *
Bob Halley's avatar
base  
Bob Halley committed
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783
	 * 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)) {
784
#ifdef ISC_PLATFORM_USETHREADS
Bob Halley's avatar
base  
Bob Halley committed
785 786
		/*
		 * For reasons similar to those given in the comment in
787
		 * isc_task_send() above, it is safe for us to dequeue
Bob Halley's avatar
base  
Bob Halley committed
788 789 790 791 792
		 * 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)) {
793 794 795
			XTHREADTRACE(isc_msgcat_get(isc_msgcat,
						    ISC_MSGSET_GENERAL,
						    ISC_MSG_WAIT, "wait"));
Bob Halley's avatar
base  
Bob Halley committed
796
			WAIT(&manager->work_available, &manager->lock);
797 798 799
			XTHREADTRACE(isc_msgcat_get(isc_msgcat,
						    ISC_MSGSET_TASK,
						    ISC_MSG_AWAKE, "awake"));
Bob Halley's avatar
base  
Bob Halley committed
800
		}
801
#else /* ISC_PLATFORM_USETHREADS */
802 803 804
		if (total_dispatch_count >= DEFAULT_TASKMGR_QUANTUM ||
		    EMPTY(manager->ready_tasks))
			break;
805
#endif /* ISC_PLATFORM_USETHREADS */
806 807
		XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK,
					    ISC_MSG_WORKING, "working"));
808

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

			INSIST(VALID_TASK(task));

			/*
			 * Note we only unlock the manager lock if we actually
821
			 * have a task to do.  We must reacquire the manager
Bob Halley's avatar
base  
Bob Halley committed
822 823 824 825 826 827
			 * 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
828
			INSIST(task->state == task_state_ready);
829
			task->state = task_state_running;
830 831
			XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
					      ISC_MSG_RUNNING, "running"));
832 833 834
			do {
				if (!EMPTY(task->events)) {
					event = HEAD(task->events);
835
					DEQUEUE(task->events, event, ev_link);
Bob Halley's avatar
base  
Bob Halley committed
836

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

Bob Halley's avatar
Bob Halley committed
855 856 857 858
				if (task->references == 0 &&
				    EMPTY(task->events) &&
				    !TASK_SHUTTINGDOWN(task)) {
					isc_boolean_t was_idle;
859

Bob Halley's avatar
Bob Halley committed
860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883
					/*
					 * 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);
884 885
				}

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

932 933
			if (finished)
				task_finished(task);
Bob Halley's avatar
base  
Bob Halley committed
934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961

			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);
962 963 964 965 966 967 968 969 970 971
}

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

972 973
	XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
				    ISC_MSG_STARTING, "starting"));
974 975

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

977 978
	XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
				    ISC_MSG_EXITING, "exiting"));
Bob Halley's avatar
base  
Bob Halley committed
979

Bob Halley's avatar
update  
Bob Halley committed
980
	return ((isc_threadresult_t)0);
Bob Halley's avatar
base  
Bob Halley committed
981
}
982
#endif /* ISC_PLATFORM_USETHREADS */
Bob Halley's avatar
base  
Bob Halley committed
983 984

static void
Bob Halley's avatar
Bob Halley committed
985
manager_free(isc_taskmgr_t *manager) {