task.c 33.2 KB
Newer Older
Bob Halley's avatar
Bob Halley committed
1
/*
Mark Andrews's avatar
Mark Andrews committed
2
 * Copyright (C) 2004-2007  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Copyright (C) 1998-2003  Internet Software Consortium.
4
 *
Automatic Updater's avatar
Automatic Updater committed
5
 * Permission to use, copy, modify, and/or distribute this software for any
Bob Halley's avatar
Bob Halley committed
6 7
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
8
 *
Mark Andrews's avatar
Mark Andrews committed
9 10 11 12 13 14 15
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC 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

Automatic Updater's avatar
Automatic Updater committed
18
/* $Id: task.c,v 1.105 2007/06/18 23:47:44 tbox Exp $ */
David Lawrence's avatar
David Lawrence committed
19

20 21
/*! \file
 * \author Principal Author: Bob Halley
Bob Halley's avatar
Bob Halley committed
22 23
 */

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/magic.h>
34
#include <isc/mem.h>
35
#include <isc/msgs.h>
Brian Wellington's avatar
Brian Wellington committed
36
#include <isc/platform.h>
37
#include <isc/string.h>
Bob Halley's avatar
Bob Halley committed
38
#include <isc/task.h>
39
#include <isc/thread.h>
Michael Graff's avatar
Michael Graff committed
40
#include <isc/util.h>
41
#include <isc/xml.h>
Bob Halley's avatar
base  
Bob Halley committed
42

43
#ifndef ISC_PLATFORM_USETHREADS
44
#include "task_p.h"
45
#endif /* ISC_PLATFORM_USETHREADS */
46

47 48
#define ISC_TASK_NAMES 1

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

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

Bob Halley's avatar
Bob Halley committed
66 67
typedef enum {
	task_state_idle, task_state_ready, task_state_running,
68
	task_state_done
Bob Halley's avatar
Bob Halley committed
69 70
} task_state_t;

Mark Andrews's avatar
Mark Andrews committed
71
#ifdef HAVE_LIBXML2
72 73 74
static const char *statenames[] = {
	"idle", "ready", "running", "done",
};
Mark Andrews's avatar
Mark Andrews committed
75
#endif
76

77 78
#define TASK_MAGIC			ISC_MAGIC('T', 'A', 'S', 'K')
#define VALID_TASK(t)			ISC_MAGIC_VALID(t, TASK_MAGIC)
Bob Halley's avatar
Bob Halley committed
79

Bob Halley's avatar
Bob Halley committed
80
struct isc_task {
Bob Halley's avatar
Bob Halley committed
81 82
	/* Not locked. */
	unsigned int			magic;
Bob Halley's avatar
Bob Halley committed
83
	isc_taskmgr_t *			manager;
Bob Halley's avatar
Bob Halley committed
84
	isc_mutex_t			lock;
Bob Halley's avatar
Bob Halley committed
85 86 87
	/* Locked by task lock. */
	task_state_t			state;
	unsigned int			references;
Bob Halley's avatar
Bob Halley committed
88
	isc_eventlist_t			events;
89
	isc_eventlist_t			on_shutdown;
Bob Halley's avatar
Bob Halley committed
90
	unsigned int			quantum;
91
	unsigned int			flags;
92
	isc_stdtime_t			now;
93 94 95 96
#ifdef ISC_TASK_NAMES
	char				name[16];
	void *				tag;
#endif
Bob Halley's avatar
Bob Halley committed
97
	/* Locked by task manager lock. */
Bob Halley's avatar
Bob Halley committed
98 99
	LINK(isc_task_t)		link;
	LINK(isc_task_t)		ready_link;
Bob Halley's avatar
Bob Halley committed
100 101
};

Bob Halley's avatar
Bob Halley committed
102
#define TASK_F_SHUTTINGDOWN		0x01
103

104 105
#define TASK_SHUTTINGDOWN(t)		(((t)->flags & TASK_F_SHUTTINGDOWN) \
					 != 0)
106

107 108
#define TASK_MANAGER_MAGIC		ISC_MAGIC('T', 'S', 'K', 'M')
#define VALID_MANAGER(m)		ISC_MAGIC_VALID(m, TASK_MANAGER_MAGIC)
Bob Halley's avatar
Bob Halley committed
109

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

135
#define DEFAULT_TASKMGR_QUANTUM		10
Bob Halley's avatar
Bob Halley committed
136 137
#define DEFAULT_DEFAULT_QUANTUM		5
#define FINISHED(m)			((m)->exiting && EMPTY((m)->tasks))
Bob Halley's avatar
Bob Halley committed
138

139 140
#ifndef ISC_PLATFORM_USETHREADS
static isc_taskmgr_t *taskmgr = NULL;
141
#endif /* ISC_PLATFORM_USETHREADS */
142

Bob Halley's avatar
base  
Bob Halley committed
143 144 145 146 147
/***
 *** Tasks.
 ***/

static void
148
task_finished(isc_task_t *task) {
Bob Halley's avatar
Bob Halley committed
149
	isc_taskmgr_t *manager = task->manager;
Bob Halley's avatar
base  
Bob Halley committed
150 151

	REQUIRE(EMPTY(task->events));
152
	REQUIRE(EMPTY(task->on_shutdown));
153 154 155 156
	REQUIRE(task->references == 0);
	REQUIRE(task->state == task_state_done);

	XTRACE("task_finished");
Bob Halley's avatar
base  
Bob Halley committed
157 158 159

	LOCK(&manager->lock);
	UNLINK(manager->tasks, task, link);
160
#ifdef ISC_PLATFORM_USETHREADS
Bob Halley's avatar
base  
Bob Halley committed
161 162 163 164 165 166 167 168 169
	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);
	}
170
#endif /* ISC_PLATFORM_USETHREADS */
Bob Halley's avatar
base  
Bob Halley committed
171
	UNLOCK(&manager->lock);
172

Brian Wellington's avatar
Brian Wellington committed
173
	DESTROYLOCK(&task->lock);
Bob Halley's avatar
base  
Bob Halley committed
174
	task->magic = 0;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
175
	isc_mem_put(manager->mctx, task, sizeof(*task));
Bob Halley's avatar
base  
Bob Halley committed
176 177
}

Bob Halley's avatar
Bob Halley committed
178
isc_result_t
179
isc_task_create(isc_taskmgr_t *manager, unsigned int quantum,
180
		isc_task_t **taskp)
Bob Halley's avatar
Bob Halley committed
181
{
Bob Halley's avatar
Bob Halley committed
182
	isc_task_t *task;
183
	isc_boolean_t exiting;
184
	isc_result_t result;
Bob Halley's avatar
base  
Bob Halley committed
185 186 187 188

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

Andreas Gustafsson's avatar
Andreas Gustafsson committed
189
	task = isc_mem_get(manager->mctx, sizeof(*task));
Bob Halley's avatar
base  
Bob Halley committed
190
	if (task == NULL)
Bob Halley's avatar
Bob Halley committed
191
		return (ISC_R_NOMEMORY);
192
	XTRACE("isc_task_create");
Bob Halley's avatar
base  
Bob Halley committed
193
	task->manager = manager;
194 195
	result = isc_mutex_init(&task->lock);
	if (result != ISC_R_SUCCESS) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
196
		isc_mem_put(manager->mctx, task, sizeof(*task));
197
		return (result);
Bob Halley's avatar
update  
Bob Halley committed
198
	}
Bob Halley's avatar
base  
Bob Halley committed
199 200 201
	task->state = task_state_idle;
	task->references = 1;
	INIT_LIST(task->events);
202
	INIT_LIST(task->on_shutdown);
Bob Halley's avatar
base  
Bob Halley committed
203
	task->quantum = quantum;
Bob Halley's avatar
Bob Halley committed
204
	task->flags = 0;
205
	task->now = 0;
206
#ifdef ISC_TASK_NAMES
Andreas Gustafsson's avatar
Andreas Gustafsson committed
207
	memset(task->name, 0, sizeof(task->name));
208 209
	task->tag = NULL;
#endif
Bob Halley's avatar
base  
Bob Halley committed
210 211 212
	INIT_LINK(task, link);
	INIT_LINK(task, ready_link);

213
	exiting = ISC_FALSE;
Bob Halley's avatar
base  
Bob Halley committed
214
	LOCK(&manager->lock);
215 216 217 218 219 220
	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
221 222
	UNLOCK(&manager->lock);

223
	if (exiting) {
Brian Wellington's avatar
Brian Wellington committed
224
		DESTROYLOCK(&task->lock);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
225
		isc_mem_put(manager->mctx, task, sizeof(*task));
226 227 228
		return (ISC_R_SHUTTINGDOWN);
	}

229
	task->magic = TASK_MAGIC;
Bob Halley's avatar
base  
Bob Halley committed
230 231
	*taskp = task;

Bob Halley's avatar
Bob Halley committed
232
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
233 234
}

Bob Halley's avatar
update  
Bob Halley committed
235
void
236
isc_task_attach(isc_task_t *source, isc_task_t **targetp) {
Bob Halley's avatar
base  
Bob Halley committed
237

238 239 240
	/*
	 * Attach *targetp to source.
	 */
Bob Halley's avatar
base  
Bob Halley committed
241

242 243
	REQUIRE(VALID_TASK(source));
	REQUIRE(targetp != NULL && *targetp == NULL);
Bob Halley's avatar
base  
Bob Halley committed
244

245
	XTTRACE(source, "isc_task_attach");
246

247 248 249 250 251
	LOCK(&source->lock);
	source->references++;
	UNLOCK(&source->lock);

	*targetp = source;
Bob Halley's avatar
base  
Bob Halley committed
252 253
}

254 255 256 257
static inline isc_boolean_t
task_shutdown(isc_task_t *task) {
	isc_boolean_t was_idle = ISC_FALSE;
	isc_event_t *event, *prev;
258

259 260 261 262 263 264 265
	/*
	 * Caller must be holding the task's lock.
	 */

	XTRACE("task_shutdown");

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

	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);
303
#ifdef ISC_PLATFORM_USETHREADS
304
	SIGNAL(&manager->work_available);
305
#endif /* ISC_PLATFORM_USETHREADS */
306 307 308 309

	UNLOCK(&manager->lock);
}

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

Bob Halley's avatar
Bob Halley committed
336
	return (ISC_FALSE);
337 338
}

Bob Halley's avatar
Bob Halley committed
339
void
Bob Halley's avatar
Bob Halley committed
340 341
isc_task_detach(isc_task_t **taskp) {
	isc_task_t *task;
Bob Halley's avatar
Bob Halley committed
342
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
343

344 345 346 347
	/*
	 * Detach *taskp from its task.
	 */

Bob Halley's avatar
base  
Bob Halley committed
348 349 350 351
	REQUIRE(taskp != NULL);
	task = *taskp;
	REQUIRE(VALID_TASK(task));

352 353
	XTRACE("isc_task_detach");

Bob Halley's avatar
base  
Bob Halley committed
354
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
355
	was_idle = task_detach(task);
Bob Halley's avatar
base  
Bob Halley committed
356 357
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
358
	if (was_idle)
359
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
360 361 362 363

	*taskp = NULL;
}

Bob Halley's avatar
Bob Halley committed
364 365 366
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
367
	isc_event_t *event;
368

369
	/*
370
	 * Caller must be holding the task lock.
371 372
	 */

Bob Halley's avatar
update  
Bob Halley committed
373 374
	REQUIRE(eventp != NULL);
	event = *eventp;
Bob Halley's avatar
base  
Bob Halley committed
375
	REQUIRE(event != NULL);
376
	REQUIRE(event->ev_type > 0);
Bob Halley's avatar
Bob Halley committed
377
	REQUIRE(task->state != task_state_done);
Bob Halley's avatar
base  
Bob Halley committed
378

379 380
	XTRACE("task_send");

Bob Halley's avatar
Bob Halley committed
381 382 383 384
	if (task->state == task_state_idle) {
		was_idle = ISC_TRUE;
		INSIST(EMPTY(task->events));
		task->state = task_state_ready;
385
	}
Bob Halley's avatar
Bob Halley committed
386 387
	INSIST(task->state == task_state_ready ||
	       task->state == task_state_running);
388
	ENQUEUE(task->events, event, ev_link);
Bob Halley's avatar
Bob Halley committed
389
	*eventp = NULL;
390

Bob Halley's avatar
Bob Halley committed
391
	return (was_idle);
392
}
393

Bob Halley's avatar
Bob Halley committed
394
void
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
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
412
	was_idle = task_send(task, eventp);
Bob Halley's avatar
base  
Bob Halley committed
413 414 415 416 417 418
	UNLOCK(&task->lock);

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

Bob Halley's avatar
Bob Halley committed
435
void
436
isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
Bob Halley's avatar
Bob Halley committed
437
	isc_boolean_t idle1, idle2;
438 439 440 441 442 443 444 445 446 447 448 449
	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
450

451
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
452 453
	idle1 = task_send(task, eventp);
	idle2 = task_detach(task);
454 455
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
456 457 458 459 460 461
	/*
	 * 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));
462

Bob Halley's avatar
Bob Halley committed
463 464
	if (idle1 || idle2)
		task_ready(task);
465 466

	*taskp = NULL;
Bob Halley's avatar
base  
Bob Halley committed
467 468
}

469
#define PURGE_OK(event)	(((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0)
470 471 472 473 474

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)
475
{
Bob Halley's avatar
Bob Halley committed
476
	isc_event_t *event, *next_event;
477
	unsigned int count = 0;
478

Bob Halley's avatar
Bob Halley committed
479
	REQUIRE(VALID_TASK(task));
480
	REQUIRE(last >= first);
Bob Halley's avatar
Bob Halley committed
481

482
	XTRACE("dequeue_events");
Bob Halley's avatar
Bob Halley committed
483

Bob Halley's avatar
Bob Halley committed
484
	/*
485 486 487
	 * 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.
488
	 *
489
	 * sender == NULL means "any sender", and tag == NULL means "any tag".
Bob Halley's avatar
Bob Halley committed
490 491 492
	 */

	LOCK(&task->lock);
493 494

	for (event = HEAD(task->events); event != NULL; event = next_event) {
495 496 497 498
		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) &&
499
		    (!purging || PURGE_OK(event))) {
500 501
			DEQUEUE(task->events, event, ev_link);
			ENQUEUE(*events, event, ev_link);
502
			count++;
Bob Halley's avatar
Bob Halley committed
503 504
		}
	}
505

Bob Halley's avatar
Bob Halley committed
506 507
	UNLOCK(&task->lock);

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

535 536 537 538 539
	/*
	 * Note that purging never changes the state of the task.
	 */

	return (count);
Bob Halley's avatar
Bob Halley committed
540 541
}

542
unsigned int
Bob Halley's avatar
Bob Halley committed
543
isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
544
	       void *tag)
Bob Halley's avatar
Bob Halley committed
545
{
546 547 548 549
	/*
	 * Purge events from a task's event queue.
	 */

550 551
	XTRACE("isc_task_purge");

Bob Halley's avatar
Bob Halley committed
552
	return (isc_task_purgerange(task, sender, type, type, tag));
553 554
}

Bob Halley's avatar
Bob Halley committed
555 556 557 558 559 560
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.
561 562
	 *
	 * XXXRTH:  WARNING:  This method may be removed before beta.
Bob Halley's avatar
Bob Halley committed
563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
	 */

	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) {
581
		next_event = NEXT(curr_event, ev_link);
582
		if (curr_event == event && PURGE_OK(event)) {
583
			DEQUEUE(task->events, curr_event, ev_link);
Bob Halley's avatar
Bob Halley committed
584 585 586 587 588 589 590 591 592 593 594 595 596
			break;
		}
	}
	UNLOCK(&task->lock);

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
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));
}

626
isc_result_t
David Lawrence's avatar
David Lawrence committed
627 628
isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, const void *arg)
{
629 630 631 632
	isc_boolean_t disallowed = ISC_FALSE;
	isc_result_t result = ISC_R_SUCCESS;
	isc_event_t *event;

633 634 635 636 637
	/*
	 * Send a shutdown event with action 'action' and argument 'arg' when
	 * 'task' is shutdown.
	 */

638
	REQUIRE(VALID_TASK(task));
639
	REQUIRE(action != NULL);
640

641
	event = isc_event_allocate(task->manager->mctx,
642 643 644 645
				   NULL,
				   ISC_TASKEVENT_SHUTDOWN,
				   action,
				   arg,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
646
				   sizeof(*event));
647 648 649 650
	if (event == NULL)
		return (ISC_R_NOMEMORY);

	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
651
	if (TASK_SHUTTINGDOWN(task)) {
652
		disallowed = ISC_TRUE;
653
		result = ISC_R_SHUTTINGDOWN;
654
	} else
655
		ENQUEUE(task->on_shutdown, event, ev_link);
656 657 658
	UNLOCK(&task->lock);

	if (disallowed)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
659
		isc_mem_put(task->manager->mctx, event, sizeof(*event));
660 661 662 663

	return (result);
}

Bob Halley's avatar
Bob Halley committed
664
void
Bob Halley's avatar
Bob Halley committed
665
isc_task_shutdown(isc_task_t *task) {
666
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
667

668 669 670 671
	/*
	 * Shutdown 'task'.
	 */

Bob Halley's avatar
base  
Bob Halley committed
672 673 674
	REQUIRE(VALID_TASK(task));

	LOCK(&task->lock);
675
	was_idle = task_shutdown(task);
Bob Halley's avatar
base  
Bob Halley committed
676 677
	UNLOCK(&task->lock);

678 679
	if (was_idle)
		task_ready(task);
Bob Halley's avatar
Bob Halley committed
680
}
Bob Halley's avatar
base  
Bob Halley committed
681

Bob Halley's avatar
Bob Halley committed
682
void
Bob Halley's avatar
Bob Halley committed
683
isc_task_destroy(isc_task_t **taskp) {
Bob Halley's avatar
Bob Halley committed
684

685 686 687 688
	/*
	 * Destroy '*taskp'.
	 */

Bob Halley's avatar
Bob Halley committed
689 690
	REQUIRE(taskp != NULL);

Bob Halley's avatar
Bob Halley committed
691 692
	isc_task_shutdown(*taskp);
	isc_task_detach(taskp);
Bob Halley's avatar
base  
Bob Halley committed
693 694
}

695
void
David Lawrence's avatar
David Lawrence committed
696
isc_task_setname(isc_task_t *task, const char *name, void *tag) {
697 698 699 700 701 702 703 704 705 706 707 708 709 710

	/*
	 * 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
711 712
	UNUSED(name);
	UNUSED(tag);
713
#endif
Bob Halley's avatar
base  
Bob Halley committed
714

715
}
Bob Halley's avatar
Bob Halley committed
716

717 718 719 720 721 722 723 724 725 726
const char *
isc_task_getname(isc_task_t *task) {
	return (task->name);
}

void *
isc_task_gettag(isc_task_t *task) {
	return (task->tag);
}

727 728 729 730 731 732 733 734 735 736 737
void
isc_task_getcurrenttime(isc_task_t *task, isc_stdtime_t *t) {
	REQUIRE(VALID_TASK(task));
	REQUIRE(t != NULL);

	LOCK(&task->lock);

	*t = task->now;

	UNLOCK(&task->lock);
}
738

Bob Halley's avatar
base  
Bob Halley committed
739 740 741
/***
 *** Task Manager.
 ***/
742 743
static void
dispatch(isc_taskmgr_t *manager) {
Bob Halley's avatar
Bob Halley committed
744
	isc_task_t *task;
745 746
#ifndef ISC_PLATFORM_USETHREADS
	unsigned int total_dispatch_count = 0;
747
	isc_tasklist_t ready_tasks;
748
#endif /* ISC_PLATFORM_USETHREADS */
Bob Halley's avatar
base  
Bob Halley committed
749 750 751 752 753 754

	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.
755
	 *
Bob Halley's avatar
base  
Bob Halley committed
756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
	 * 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.
783
	 *
Bob Halley's avatar
base  
Bob Halley committed
784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
	 * 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.
	 */

802 803 804
#ifndef ISC_PLATFORM_USETHREADS
	ISC_LIST_INIT(ready_tasks);
#endif
Bob Halley's avatar
base  
Bob Halley committed
805 806
	LOCK(&manager->lock);
	while (!FINISHED(manager)) {
807
#ifdef ISC_PLATFORM_USETHREADS
Bob Halley's avatar
base  
Bob Halley committed
808 809
		/*
		 * For reasons similar to those given in the comment in
810
		 * isc_task_send() above, it is safe for us to dequeue
Bob Halley's avatar
base  
Bob Halley committed
811 812 813 814
		 * the task while only holding the manager lock, and then
		 * change the task to running state while only holding the
		 * task lock.
		 */
815 816 817 818
		while ((EMPTY(manager->ready_tasks) ||
		        manager->exclusive_requested) &&
		  	!FINISHED(manager)) 
	  	{
819 820 821
			XTHREADTRACE(isc_msgcat_get(isc_msgcat,
						    ISC_MSGSET_GENERAL,
						    ISC_MSG_WAIT, "wait"));
Bob Halley's avatar
base  
Bob Halley committed
822
			WAIT(&manager->work_available, &manager->lock);
823 824 825
			XTHREADTRACE(isc_msgcat_get(isc_msgcat,
						    ISC_MSGSET_TASK,
						    ISC_MSG_AWAKE, "awake"));
Bob Halley's avatar
base  
Bob Halley committed
826
		}
827
#else /* ISC_PLATFORM_USETHREADS */
828 829 830
		if (total_dispatch_count >= DEFAULT_TASKMGR_QUANTUM ||
		    EMPTY(manager->ready_tasks))
			break;
831
#endif /* ISC_PLATFORM_USETHREADS */
832 833
		XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK,
					    ISC_MSG_WORKING, "working"));
834

Bob Halley's avatar
base  
Bob Halley committed
835 836
		task = HEAD(manager->ready_tasks);
		if (task != NULL) {
Bob Halley's avatar
Bob Halley committed
837
			unsigned int dispatch_count = 0;
Bob Halley's avatar
Bob Halley committed
838 839
			isc_boolean_t done = ISC_FALSE;
			isc_boolean_t requeue = ISC_FALSE;
840
			isc_boolean_t finished = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
841
			isc_event_t *event;
Bob Halley's avatar
base  
Bob Halley committed
842 843 844 845 846

			INSIST(VALID_TASK(task));

			/*
			 * Note we only unlock the manager lock if we actually
847
			 * have a task to do.  We must reacquire the manager
Bob Halley's avatar
base  
Bob Halley committed
848 849 850
			 * lock before exiting the 'if (task != NULL)' block.
			 */
			DEQUEUE(manager->ready_tasks, task, ready_link);
851
			manager->tasks_running++;
Bob Halley's avatar
base  
Bob Halley committed
852 853 854
			UNLOCK(&manager->lock);

			LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
855
			INSIST(task->state == task_state_ready);
856
			task->state = task_state_running;
857 858
			XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
					      ISC_MSG_RUNNING, "running"));
859
			isc_stdtime_get(&task->now);
860 861 862
			do {
				if (!EMPTY(task->events)) {
					event = HEAD(task->events);
863
					DEQUEUE(task->events, event, ev_link);
Bob Halley's avatar
base  
Bob Halley committed
864

865 866 867
					/*
					 * Execute the event action.
					 */
868 869 870 871
					XTRACE(isc_msgcat_get(isc_msgcat,
							    ISC_MSGSET_TASK,
							    ISC_MSG_EXECUTE,
							    "execute action"));
872
					if (event->ev_action != NULL) {
873
						UNLOCK(&task->lock);
874
						(event->ev_action)(task,event);
875 876 877
						LOCK(&task->lock);
					}
					dispatch_count++;
878 879
#ifndef ISC_PLATFORM_USETHREADS
					total_dispatch_count++;
880
#endif /* ISC_PLATFORM_USETHREADS */
881
				}
882

Bob Halley's avatar
Bob Halley committed
883 884 885 886
				if (task->references == 0 &&
				    EMPTY(task->events) &&
				    !TASK_SHUTTINGDOWN(task)) {
					isc_boolean_t was_idle;
887

Bob Halley's avatar
Bob Halley committed
888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911
					/*
					 * 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);
912 913
				}

914
				if (EMPTY(task->events)) {
Bob Halley's avatar
Bob Halley committed
915
					/*
916
					 * Nothing else to do for this task
Bob Halley's avatar
Bob Halley committed
917
					 * right now.
Bob Halley's avatar
Bob Halley committed
918
					 */
919 920 921 922
					XTRACE(isc_msgcat_get(isc_msgcat,
							      ISC_MSGSET_TASK,
							      ISC_MSG_EMPTY,
							      "empty"));
Bob Halley's avatar
Bob Halley committed
923 924 925 926 927
					if (task->references == 0 &&
					    TASK_SHUTTINGDOWN(task)) {
						/*
						 * The task is done.
						 */
928 929 930 931 932
						XTRACE(isc_msgcat_get(
							       isc_msgcat,
							       ISC_MSGSET_TASK,
							       ISC_MSG_DONE,
							       "done"));
Bob Halley's avatar
Bob Halley committed
933
						finished = ISC_TRUE;
934
						task->state = task_state_done;
935 936
					} else
						task->state = task_state_idle;