task.c 30.6 KB
Newer Older
Bob Halley's avatar
Bob Halley committed
1
/*
Mark Andrews's avatar
Mark Andrews committed
2 3
 * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
 * Copyright (C) 1998-2003  Internet Software Consortium.
4
 *
Bob Halley's avatar
Bob Halley committed
5 6 7
 * 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.
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

Mark Andrews's avatar
Mark Andrews committed
18
/* $Id: task.c,v 1.91 2004/03/05 05:10:50 marka 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/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>
Bob Halley's avatar
base  
Bob Halley committed
41

42
#ifndef 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
} task_state_t;

70 71
#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
72

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
	isc_stdtime_t			now;
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

100 101
#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
102

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
	LIST(isc_task_t)		tasks;
115
	isc_tasklist_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;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
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);

Andreas Gustafsson's avatar
Andreas Gustafsson committed
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) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
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
	task->now = 0;
201
#ifdef ISC_TASK_NAMES
Andreas Gustafsson's avatar
Andreas Gustafsson committed
202
	memset(task->name, 0, sizeof(task->name));
203 204
	task->tag = NULL;
#endif
Bob Halley's avatar
base  
Bob Halley committed
205 206 207
	INIT_LINK(task, link);
	INIT_LINK(task, ready_link);

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

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

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

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

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

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

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

240
	XTTRACE(source, "isc_task_attach");
241

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

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

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

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

	XTRACE("task_shutdown");

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

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

	UNLOCK(&manager->lock);
}

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

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

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

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

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

347 348
	XTRACE("isc_task_detach");

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

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

	*taskp = NULL;
}

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

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

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

374 375
	XTRACE("task_send");

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	LOCK(&task->lock);
488 489

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

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

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

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

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

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

545 546
	XTRACE("isc_task_purge");

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

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

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

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

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 620
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));
}

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

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

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

636
	event = isc_event_allocate(task->manager->mctx,
637 638 639 640
				   NULL,
				   ISC_TASKEVENT_SHUTDOWN,
				   action,
				   arg,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
641
				   sizeof(*event));
642 643 644 645
	if (event == NULL)
		return (ISC_R_NOMEMORY);

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

	if (disallowed)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
654
		isc_mem_put(task->manager->mctx, event, sizeof(*event));
655 656 657 658

	return (result);
}

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

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

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

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

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

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

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

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

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

690
void
David Lawrence's avatar
David Lawrence committed
691
isc_task_setname(isc_task_t *task, const char *name, void *tag) {
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
Andreas Gustafsson's avatar
Andreas Gustafsson committed
706 707
	UNUSED(name);
	UNUSED(tag);
708
#endif
Bob Halley's avatar
base  
Bob Halley committed
709

710
}
Bob Halley's avatar
Bob Halley committed
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);
}

722 723 724 725 726 727 728 729 730 731 732
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);
}
733

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

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

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

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

			INSIST(VALID_TASK(task));

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

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

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

Bob Halley's avatar
Bob Halley committed
878 879 880 881
				if (task->references == 0 &&
				    EMPTY(task->events) &&
				    !TASK_SHUTTINGDOWN(task)) {
					isc_boolean_t was_idle;
882

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

909
				if (EMPTY(task->events)) {
Bob Halley's avatar
Bob Halley committed
910
					/*
911
					 * Nothing else to do for this task
Bob Halley's avatar
Bob Halley committed
912
					 * right now.
Bob Halley's avatar
Bob Halley committed
913
					 */
914 915 916 917
					XTRACE(isc_msgcat_get(isc_msgcat,
							      ISC_MSGSET_TASK,
							      ISC_MSG_EMPTY,
							      "empty"));
Bob Halley's avatar
Bob Halley committed
918 919 920 921 922
					if (task->references == 0 &&
					    TASK_SHUTTINGDOWN(task)) {
						/*
						 * The task is done.
						 */
923 924 925 926 927
						XTRACE(isc_msgcat_get(
							       isc_msgcat,
							       ISC_MSGSET_TASK,
							       ISC_MSG_DONE,
							       "done"));
Bob Halley's avatar
Bob Halley committed
928
						finished = ISC_TRUE;
929
						task->state = task_state_done;
930 931
					} else
						task->state = task_state_idle;
Bob Halley's avatar
Bob Halley committed
932
					done = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
933 934 935 936 937 938 939 940 941 942 943
				} 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.
					 */
944 945 946 947
					XTRACE(isc_msgcat_get(isc_msgcat,
							      ISC_MSGSET_TASK,
							      ISC_MSG_QUANTUM,
							      "quantum"));
Bob Halley's avatar
base  
Bob Halley committed
948
					task->state = task_state_ready;
Bob Halley's avatar
Bob Halley committed
949 950
					requeue = ISC_TRUE;
					done = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
951
				}