task.c 23.5 KB
Newer Older
Bob Halley's avatar
Bob Halley committed
1
/*
Bob Halley's avatar
Bob Halley committed
2
 * Copyright (C) 1998, 1999  Internet Software Consortium.
Bob Halley's avatar
Bob Halley committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16
 * 
 * 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.
 * 
 * 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
base  
Bob Halley committed
17

Bob Halley's avatar
Bob Halley committed
18 19 20 21
/*
 * Principal Author: Bob Halley
 */

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

Bob Halley's avatar
Bob Halley committed
27 28
#include <config.h>

Bob Halley's avatar
base  
Bob Halley committed
29
#include <isc/assertions.h>
30
#include <isc/boolean.h>
Bob Halley's avatar
Bob Halley committed
31
#include <isc/thread.h>
32 33
#include <isc/mutex.h>
#include <isc/condition.h>
34
#include <isc/error.h>
35
#include <isc/event.h>
Bob Halley's avatar
Bob Halley committed
36
#include <isc/task.h>
Bob Halley's avatar
base  
Bob Halley committed
37

38
#include "util.h"
Bob Halley's avatar
base  
Bob Halley committed
39

Bob Halley's avatar
Bob Halley committed
40
#ifdef ISC_TASK_TRACE
Bob Halley's avatar
update  
Bob Halley committed
41
#define XTRACE(m)		printf("%s task %p thread %lu\n", (m), \
Bob Halley's avatar
Bob Halley committed
42
				       task, isc_thread_self())
43 44
#define XTHREADTRACE(m)		printf("%s thread %lu\n", (m), \
				       isc_thread_self())
Bob Halley's avatar
Bob Halley committed
45 46
#else
#define XTRACE(m)
47
#define XTHREADTRACE(m)
Bob Halley's avatar
Bob Halley committed
48
#endif
Bob Halley's avatar
base  
Bob Halley committed
49

Bob Halley's avatar
Bob Halley committed
50
/***
Bob Halley's avatar
Bob Halley committed
51
 *** Types.
Bob Halley's avatar
Bob Halley committed
52 53
 ***/

Bob Halley's avatar
Bob Halley committed
54 55
typedef enum {
	task_state_idle, task_state_ready, task_state_running,
56
	task_state_done
Bob Halley's avatar
Bob Halley committed
57 58 59 60 61 62
} 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
63
struct isc_task {
Bob Halley's avatar
Bob Halley committed
64 65
	/* Not locked. */
	unsigned int			magic;
Bob Halley's avatar
Bob Halley committed
66
	isc_taskmgr_t *			manager;
Bob Halley's avatar
Bob Halley committed
67
	isc_mutex_t			lock;
Bob Halley's avatar
Bob Halley committed
68
	isc_mem_t *			mctx;
Bob Halley's avatar
Bob Halley committed
69 70 71
	/* Locked by task lock. */
	task_state_t			state;
	unsigned int			references;
Bob Halley's avatar
Bob Halley committed
72
	isc_eventlist_t			events;
73
	isc_eventlist_t			on_shutdown;
Bob Halley's avatar
Bob Halley committed
74
	unsigned int			quantum;
75
	unsigned int			flags;
Bob Halley's avatar
Bob Halley committed
76
	/* Locked by task manager lock. */
Bob Halley's avatar
Bob Halley committed
77 78
	LINK(isc_task_t)		link;
	LINK(isc_task_t)		ready_link;
Bob Halley's avatar
Bob Halley committed
79 80
};

81 82 83 84 85 86 87 88
#define TASK_F_DONEOK			0x01
#define TASK_F_SENDOK			0x02
#define TASK_F_SHUTTINGDOWN		0x04

#define DONE_FLAGS			(TASK_F_DONEOK|TASK_F_SHUTTINGDOWN)
#define TASK_DONE(t)			(((t)->flags & DONE_FLAGS) == \
					 DONE_FLAGS)

Bob Halley's avatar
Bob Halley committed
89 90 91 92
#define TASK_MANAGER_MAGIC		0x54534B4DU	/* TSKM. */
#define VALID_MANAGER(m)		((m) != NULL && \
					 (m)->magic == TASK_MANAGER_MAGIC)

Bob Halley's avatar
Bob Halley committed
93
struct isc_taskmgr {
Bob Halley's avatar
Bob Halley committed
94 95
	/* Not locked. */
	unsigned int			magic;
Bob Halley's avatar
Bob Halley committed
96
	isc_mem_t *			mctx;
Bob Halley's avatar
Bob Halley committed
97
	isc_mutex_t			lock;
98 99
	unsigned int			workers;
	isc_thread_t *			threads;
Bob Halley's avatar
Bob Halley committed
100 101
	/* Locked by task manager lock. */
	unsigned int			default_quantum;
Bob Halley's avatar
Bob Halley committed
102 103
	LIST(isc_task_t)		tasks;
	LIST(isc_task_t)		ready_tasks;
Bob Halley's avatar
Bob Halley committed
104
	isc_condition_t			work_available;
Bob Halley's avatar
Bob Halley committed
105
	isc_boolean_t			exiting;
Bob Halley's avatar
Bob Halley committed
106
};
Bob Halley's avatar
Bob Halley committed
107

Bob Halley's avatar
Bob Halley committed
108 109
#define DEFAULT_DEFAULT_QUANTUM		5
#define FINISHED(m)			((m)->exiting && EMPTY((m)->tasks))
Bob Halley's avatar
Bob Halley committed
110

Bob Halley's avatar
update  
Bob Halley committed
111

Bob Halley's avatar
base  
Bob Halley committed
112 113 114 115 116
/***
 *** Tasks.
 ***/

static void
Bob Halley's avatar
Bob Halley committed
117 118
task_free(isc_task_t *task) {
	isc_taskmgr_t *manager = task->manager;
Bob Halley's avatar
base  
Bob Halley committed
119

Bob Halley's avatar
Bob Halley committed
120
	XTRACE("free task");
Bob Halley's avatar
base  
Bob Halley committed
121
	REQUIRE(EMPTY(task->events));
122
	REQUIRE(EMPTY(task->on_shutdown));
Bob Halley's avatar
base  
Bob Halley committed
123 124 125 126 127 128 129 130 131 132 133 134 135

	LOCK(&manager->lock);
	UNLINK(manager->tasks, task, link);
	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);
	}
	UNLOCK(&manager->lock);
Bob Halley's avatar
Bob Halley committed
136
	(void)isc_mutex_destroy(&task->lock);
Bob Halley's avatar
base  
Bob Halley committed
137
	task->magic = 0;
138
	isc_mem_put(task->mctx, task, sizeof *task);
Bob Halley's avatar
base  
Bob Halley committed
139 140
}

Bob Halley's avatar
Bob Halley committed
141
isc_result_t
Bob Halley's avatar
Bob Halley committed
142
isc_task_create(isc_taskmgr_t *manager, isc_mem_t *mctx, unsigned int quantum,
143
		isc_task_t **taskp)
Bob Halley's avatar
Bob Halley committed
144
{
Bob Halley's avatar
Bob Halley committed
145
	isc_task_t *task;
Bob Halley's avatar
base  
Bob Halley committed
146 147 148 149

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

Bob Halley's avatar
Bob Halley committed
150 151 152
	if (mctx == NULL)
		mctx = manager->mctx;
	task = isc_mem_get(mctx, sizeof *task);
Bob Halley's avatar
base  
Bob Halley committed
153
	if (task == NULL)
Bob Halley's avatar
Bob Halley committed
154
		return (ISC_R_NOMEMORY);
Bob Halley's avatar
base  
Bob Halley committed
155
	task->manager = manager;
Bob Halley's avatar
Bob Halley committed
156
	task->mctx = mctx;
Bob Halley's avatar
Bob Halley committed
157
	if (isc_mutex_init(&task->lock) != ISC_R_SUCCESS) {
Bob Halley's avatar
Bob Halley committed
158
		isc_mem_put(mctx, task, sizeof *task);
Bob Halley's avatar
Bob Halley committed
159 160 161
		UNEXPECTED_ERROR(__FILE__, __LINE__,
				 "isc_mutex_init() failed");
		return (ISC_R_UNEXPECTED);
Bob Halley's avatar
update  
Bob Halley committed
162
	}
Bob Halley's avatar
base  
Bob Halley committed
163 164 165
	task->state = task_state_idle;
	task->references = 1;
	INIT_LIST(task->events);
166
	INIT_LIST(task->on_shutdown);
Bob Halley's avatar
base  
Bob Halley committed
167
	task->quantum = quantum;
168
	task->flags = (TASK_F_DONEOK|TASK_F_SENDOK);
Bob Halley's avatar
base  
Bob Halley committed
169 170 171 172
	INIT_LINK(task, link);
	INIT_LINK(task, ready_link);

	LOCK(&manager->lock);
Bob Halley's avatar
Bob Halley committed
173
	/* XXX Should disallow if task manager is exiting. */
Bob Halley's avatar
base  
Bob Halley committed
174 175 176 177 178
	if (task->quantum == 0)
		task->quantum = manager->default_quantum;
	APPEND(manager->tasks, task, link);
	UNLOCK(&manager->lock);

179
	task->magic = TASK_MAGIC;
Bob Halley's avatar
base  
Bob Halley committed
180 181
	*taskp = task;

Bob Halley's avatar
Bob Halley committed
182
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
183 184
}

Bob Halley's avatar
update  
Bob Halley committed
185
void
186
isc_task_attach(isc_task_t *source, isc_task_t **targetp) {
Bob Halley's avatar
base  
Bob Halley committed
187

188 189 190
	/*
	 * Attach *targetp to source.
	 */
Bob Halley's avatar
base  
Bob Halley committed
191

192 193
	REQUIRE(VALID_TASK(source));
	REQUIRE(targetp != NULL && *targetp == NULL);
Bob Halley's avatar
base  
Bob Halley committed
194

195 196 197 198 199
	LOCK(&source->lock);
	source->references++;
	UNLOCK(&source->lock);

	*targetp = source;
Bob Halley's avatar
base  
Bob Halley committed
200 201
}

Bob Halley's avatar
Bob Halley committed
202
void
Bob Halley's avatar
Bob Halley committed
203
isc_task_detach(isc_task_t **taskp) {
Bob Halley's avatar
Bob Halley committed
204
	isc_boolean_t free_task = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
205
	isc_task_t *task;
Bob Halley's avatar
base  
Bob Halley committed
206

207 208 209 210
	/*
	 * Detach *taskp from its task.
	 */

Bob Halley's avatar
base  
Bob Halley committed
211 212 213 214
	REQUIRE(taskp != NULL);
	task = *taskp;
	REQUIRE(VALID_TASK(task));

215 216
	XTRACE("isc_task_detach");

Bob Halley's avatar
base  
Bob Halley committed
217 218 219
	LOCK(&task->lock);
	REQUIRE(task->references > 0);
	task->references--;
220
	if (task->state == task_state_done && task->references == 0)
Bob Halley's avatar
Bob Halley committed
221
		free_task = ISC_TRUE;
222 223 224 225 226 227 228
	/*
	 * XXXRTH  It is currently possible to detach the last
	 * reference from a task that has not been shutdown.  This
	 * will prevent the task from being shutdown until the
	 * task manager is destroyed.  Should there be an
	 * automatic shutdown on last detach?
	 */
Bob Halley's avatar
base  
Bob Halley committed
229 230 231 232 233 234 235 236
	UNLOCK(&task->lock);

	if (free_task)
		task_free(task);

	*taskp = NULL;
}

Bob Halley's avatar
Bob Halley committed
237 238 239
isc_mem_t *
isc_task_mem(isc_task_t *task) {

240 241 242 243
	/*
	 * Get the task's memory context.
	 */

Bob Halley's avatar
Bob Halley committed
244 245 246 247 248
	REQUIRE(VALID_TASK(task));
	
	return (task->mctx);
}

249
isc_result_t
Bob Halley's avatar
Bob Halley committed
250
isc_task_send(isc_task_t *task, isc_event_t **eventp) {
Bob Halley's avatar
Bob Halley committed
251
	isc_boolean_t was_idle = ISC_FALSE;
252 253
	isc_boolean_t disallowed = ISC_FALSE;
	isc_result_t result = ISC_R_SUCCESS;
Bob Halley's avatar
Bob Halley committed
254
	isc_event_t *event;
Bob Halley's avatar
base  
Bob Halley committed
255

256 257 258 259
	/*
	 * Send '*event' to 'task'.
	 */

Bob Halley's avatar
base  
Bob Halley committed
260
	REQUIRE(VALID_TASK(task));
Bob Halley's avatar
update  
Bob Halley committed
261 262
	REQUIRE(eventp != NULL);
	event = *eventp;
Bob Halley's avatar
base  
Bob Halley committed
263
	REQUIRE(event != NULL);
Bob Halley's avatar
Bob Halley committed
264 265
	REQUIRE(event->sender != NULL);
	REQUIRE(event->type > 0);
Bob Halley's avatar
base  
Bob Halley committed
266

Bob Halley's avatar
Bob Halley committed
267
	XTRACE("sending");
Bob Halley's avatar
base  
Bob Halley committed
268 269 270 271 272 273
	/*
	 * 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 a lock is released.
	 */
	LOCK(&task->lock);
274
	/*
275 276
	 * Note: we require that task->state == task_state_done implies
	 * (task->flags & TASK_F_SENDOK) == 0.
277
	 */
278
	if ((task->flags & TASK_F_SENDOK) != 0) {
Bob Halley's avatar
base  
Bob Halley committed
279
		if (task->state == task_state_idle) {
Bob Halley's avatar
Bob Halley committed
280
			was_idle = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
281 282 283 284 285 286
			INSIST(EMPTY(task->events));
			task->state = task_state_ready;
		}
		INSIST(task->state == task_state_ready ||
		       task->state == task_state_running);
		ENQUEUE(task->events, event, link);
287 288
	} else {
		disallowed = ISC_TRUE;
289 290
		if (task->state == task_state_done)
			result = ISC_R_TASKDONE;
291 292 293
		else
			result = ISC_R_TASKNOSEND;
	}
Bob Halley's avatar
base  
Bob Halley committed
294 295
	UNLOCK(&task->lock);

296 297
	if (disallowed)
		return (result);
Bob Halley's avatar
base  
Bob Halley committed
298 299

	if (was_idle) {
Bob Halley's avatar
Bob Halley committed
300
		isc_taskmgr_t *manager;
Bob Halley's avatar
base  
Bob Halley committed
301 302 303 304 305 306 307 308 309

		/*
		 * We need to add this task to the ready queue.
		 *
		 * We've waited until now to do it, rather than doing it
		 * while holding the task lock, because we don't want to
		 * block while holding the task lock.
		 *
		 * We've changed the state to ready, so no one else will
310 311 312 313 314 315
		 * 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,
		 * removed, or shutting_down is started in the interval
		 * 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
316 317 318 319 320
		 */
		manager = task->manager;
		INSIST(VALID_MANAGER(manager));
		LOCK(&manager->lock);
		ENQUEUE(manager->ready_tasks, task, ready_link);
321
		SIGNAL(&manager->work_available);
Bob Halley's avatar
base  
Bob Halley committed
322 323 324
		UNLOCK(&manager->lock);
	}

Bob Halley's avatar
update  
Bob Halley committed
325 326
	*eventp = NULL;

Bob Halley's avatar
Bob Halley committed
327
	XTRACE("sent");
328 329

	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
330 331
}

332 333
unsigned int
isc_task_purgerange(isc_task_t *task, void *sender, isc_eventtype_t first,
Bob Halley's avatar
Bob Halley committed
334
		    isc_eventtype_t last, unsigned int tag)
335
{
Bob Halley's avatar
Bob Halley committed
336
	isc_event_t *event, *next_event;
Bob Halley's avatar
Bob Halley committed
337
	isc_eventlist_t purgeable;
338
	unsigned int purge_count;
Bob Halley's avatar
Bob Halley committed
339

340 341 342 343
	/*
	 * Purge events from a task's event queue.
	 */

Bob Halley's avatar
Bob Halley committed
344
	REQUIRE(VALID_TASK(task));
345
	REQUIRE(last >= first);
Bob Halley's avatar
Bob Halley committed
346

Bob Halley's avatar
Bob Halley committed
347 348 349

	XTRACE("purgerange");

Bob Halley's avatar
Bob Halley committed
350
	/*
351
	 * Events matching 'sender' and whose type is >= first and
Bob Halley's avatar
Bob Halley committed
352 353
	 * <= last will be purged, unless they are marked as unpurgable.
	 * sender == NULL means "any sender".
354 355
	 *
	 * Purging never changes the state of the task.
Bob Halley's avatar
Bob Halley committed
356 357 358
	 */

	INIT_LIST(purgeable);
359
	purge_count = 0;
Bob Halley's avatar
Bob Halley committed
360 361 362 363 364 365

	LOCK(&task->lock);
	for (event = HEAD(task->events);
	     event != NULL;
	     event = next_event) {
		next_event = NEXT(event, link);
Bob Halley's avatar
Bob Halley committed
366 367 368
		if (event->type >= first && event->type <= last &&
		    (sender == NULL || event->sender == sender) &&
		    (tag == 0 || event->tag == tag) &&
Bob Halley's avatar
Bob Halley committed
369
		    (event->attributes & ISC_EVENTATTR_NOPURGE) == 0) {
Bob Halley's avatar
Bob Halley committed
370 371 372 373 374 375 376 377 378 379
			DEQUEUE(task->events, event, link);
			ENQUEUE(purgeable, event, link);
		}
	}
	UNLOCK(&task->lock);

	for (event = HEAD(purgeable);
	     event != NULL;
	     event = next_event) {
		next_event = NEXT(event, link);
Bob Halley's avatar
Bob Halley committed
380
		isc_event_free(&event);
381
		purge_count++;
Bob Halley's avatar
Bob Halley committed
382
	}
383 384

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

387
unsigned int
Bob Halley's avatar
Bob Halley committed
388 389 390
isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
	       unsigned int tag)
{
391 392 393 394
	/*
	 * Purge events from a task's event queue.
	 */

Bob Halley's avatar
Bob Halley committed
395
	return (isc_task_purgerange(task, sender, type, type, tag));
396 397
}

Bob Halley's avatar
Bob Halley committed
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
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.
	 */

	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) {
		next_event = NEXT(curr_event, link);
		if (curr_event == event &&
		    (event->attributes & ISC_EVENTATTR_NOPURGE) == 0) {
			DEQUEUE(task->events, curr_event, link);
			break;
		}
	}
	UNLOCK(&task->lock);

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

439
isc_result_t
440
isc_task_allowsend(isc_task_t *task, isc_boolean_t allowed) {
441 442
	isc_result_t result = ISC_R_SUCCESS;

443 444 445 446
	/*
	 * Allow or disallow sending events to 'task'.
	 */

447 448 449
	REQUIRE(VALID_TASK(task));

	LOCK(&task->lock);
450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
	if (task->state == task_state_done)
		result = ISC_R_TASKDONE;
	else {
		if (allowed)
			task->flags |= TASK_F_SENDOK;
		else
			task->flags &= ~TASK_F_SENDOK;
	}
	UNLOCK(&task->lock);

	return (result);
}

isc_result_t
isc_task_allowdone(isc_task_t *task, isc_boolean_t allowed) {
	isc_result_t result = ISC_R_SUCCESS;

467 468 469 470
	/*
	 * Allow or disallow automatic termination of 'task'.
	 */

471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
	REQUIRE(VALID_TASK(task));

	LOCK(&task->lock);
	if (task->state == task_state_done)
		result = ISC_R_TASKDONE;
	else if (allowed &&
		 (task->flags & TASK_F_SHUTTINGDOWN) != 0 &&
		 task->state == task_state_idle) {
		task->flags &= ~TASK_F_SENDOK;
		task->state = task_state_done;
	} else {
		if (allowed)
			task->flags |= TASK_F_DONEOK;
		else
			task->flags &= ~TASK_F_DONEOK;
	}
487 488 489 490 491 492 493 494 495 496 497
	UNLOCK(&task->lock);

	return (result);
}

isc_result_t
isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, void *arg) {
	isc_boolean_t disallowed = ISC_FALSE;
	isc_result_t result = ISC_R_SUCCESS;
	isc_event_t *event;

498 499 500 501 502
	/*
	 * Send a shutdown event with action 'action' and argument 'arg' when
	 * 'task' is shutdown.
	 */

503
	REQUIRE(VALID_TASK(task));
504
	REQUIRE(action != NULL);
505

506 507 508 509 510 511
	event = isc_event_allocate(task->mctx,
				   NULL,
				   ISC_TASKEVENT_SHUTDOWN,
				   action,
				   arg,
				   sizeof *event);
512 513 514 515
	if (event == NULL)
		return (ISC_R_NOMEMORY);

	LOCK(&task->lock);
516
	if (task->state == task_state_done) {
517
		disallowed = ISC_TRUE;
518 519
		result = ISC_R_TASKDONE;
	} else if ((task->flags & TASK_F_SHUTTINGDOWN) != 0) {
520 521 522 523 524 525 526
		disallowed = ISC_TRUE;
		result = ISC_R_TASKSHUTTINGDOWN;
	} else
		ENQUEUE(task->on_shutdown, event, link);
	UNLOCK(&task->lock);

	if (disallowed)
Bob Halley's avatar
Bob Halley committed
527
		isc_mem_put(task->mctx, event, sizeof *event);
528 529 530 531

	return (result);
}

Bob Halley's avatar
Bob Halley committed
532
void
Bob Halley's avatar
Bob Halley committed
533
isc_task_shutdown(isc_task_t *task) {
Bob Halley's avatar
Bob Halley committed
534
	isc_boolean_t was_idle = ISC_FALSE;
535 536
	isc_boolean_t queued_something = ISC_FALSE;
	isc_event_t *event, *prev;
Bob Halley's avatar
base  
Bob Halley committed
537

538 539 540 541
	/*
	 * Shutdown 'task'.
	 */

Bob Halley's avatar
base  
Bob Halley committed
542 543 544
	REQUIRE(VALID_TASK(task));

	/*
545
	 * This routine is very similar to isc_task_send() above.
Bob Halley's avatar
base  
Bob Halley committed
546 547 548
	 */

	LOCK(&task->lock);
549 550 551
	if ((task->flags & TASK_F_SHUTTINGDOWN) == 0) {
		XTRACE("shutting down");
		task->flags |= TASK_F_SHUTTINGDOWN;
Bob Halley's avatar
base  
Bob Halley committed
552
		if (task->state == task_state_idle) {
Bob Halley's avatar
Bob Halley committed
553
			was_idle = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
554
			INSIST(EMPTY(task->events));
555 556 557 558 559
			if (EMPTY(task->on_shutdown) && TASK_DONE(task)) {
				task->flags &= ~TASK_F_SENDOK;
				task->state = task_state_done;
			} else
				task->state = task_state_ready;
Bob Halley's avatar
base  
Bob Halley committed
560 561
		}
		INSIST(task->state == task_state_ready ||
562 563 564 565 566 567 568 569 570 571 572 573 574 575
		       task->state == task_state_running ||
		       task->state == task_state_done);
		if (task->state != task_state_done) {
			/*
			 * Note that we post shutdown events LIFO.
			 */
			for (event = TAIL(task->on_shutdown);
			     event != NULL;
			     event = prev) {
				prev = PREV(event, link);
				DEQUEUE(task->on_shutdown, event, link);
				ENQUEUE(task->events, event, link);
				queued_something = ISC_TRUE;
			}
576 577
		}
	}
Bob Halley's avatar
base  
Bob Halley committed
578 579
	UNLOCK(&task->lock);

580
	if (was_idle && queued_something) {
Bob Halley's avatar
Bob Halley committed
581
		isc_taskmgr_t *manager;
Bob Halley's avatar
base  
Bob Halley committed
582 583 584 585 586

		manager = task->manager;
		INSIST(VALID_MANAGER(manager));
		LOCK(&manager->lock);
		ENQUEUE(manager->ready_tasks, task, ready_link);
587
		SIGNAL(&manager->work_available);
Bob Halley's avatar
base  
Bob Halley committed
588 589
		UNLOCK(&manager->lock);
	}
Bob Halley's avatar
Bob Halley committed
590
}
Bob Halley's avatar
base  
Bob Halley committed
591

Bob Halley's avatar
Bob Halley committed
592
void
Bob Halley's avatar
Bob Halley committed
593
isc_task_destroy(isc_task_t **taskp) {
Bob Halley's avatar
Bob Halley committed
594

595 596 597 598
	/*
	 * Destroy '*taskp'.
	 */

Bob Halley's avatar
Bob Halley committed
599 600
	REQUIRE(taskp != NULL);

Bob Halley's avatar
Bob Halley committed
601 602
	isc_task_shutdown(*taskp);
	isc_task_detach(taskp);
Bob Halley's avatar
base  
Bob Halley committed
603 604 605
}


Bob Halley's avatar
Bob Halley committed
606

Bob Halley's avatar
base  
Bob Halley committed
607 608 609 610
/***
 *** Task Manager.
 ***/

Bob Halley's avatar
update  
Bob Halley committed
611
static isc_threadresult_t
Bob Halley's avatar
Bob Halley committed
612 613 614
#ifdef _WIN32
WINAPI
#endif
Bob Halley's avatar
update  
Bob Halley committed
615
run(void *uap) {
Bob Halley's avatar
Bob Halley committed
616 617
	isc_taskmgr_t *manager = uap;
	isc_task_t *task;
Bob Halley's avatar
base  
Bob Halley committed
618

619
	XTHREADTRACE("start");
Bob Halley's avatar
base  
Bob Halley committed
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676

	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.
	 * 
	 * 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.
	 * 
	 * 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)) {
		/*
		 * For reasons similar to those given in the comment in
677
		 * isc_task_send() above, it is safe for us to dequeue
Bob Halley's avatar
base  
Bob Halley committed
678 679 680 681 682
		 * 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)) {
683
			XTHREADTRACE("wait");
Bob Halley's avatar
base  
Bob Halley committed
684
			WAIT(&manager->work_available, &manager->lock);
685
			XTHREADTRACE("awake");
Bob Halley's avatar
base  
Bob Halley committed
686
		}
687
		XTHREADTRACE("working");
Bob Halley's avatar
base  
Bob Halley committed
688 689 690
		
		task = HEAD(manager->ready_tasks);
		if (task != NULL) {
Bob Halley's avatar
Bob Halley committed
691
			unsigned int dispatch_count = 0;
Bob Halley's avatar
Bob Halley committed
692 693 694
			isc_boolean_t done = ISC_FALSE;
			isc_boolean_t requeue = ISC_FALSE;
			isc_boolean_t free_task = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
695
			isc_event_t *event;
Bob Halley's avatar
base  
Bob Halley committed
696 697 698 699 700 701 702 703 704 705 706 707

			INSIST(VALID_TASK(task));

			/*
			 * Note we only unlock the manager lock if we actually
			 * have a task to do.  We must reacquire the manager 
			 * 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
708 709 710 711 712 713 714 715
			INSIST(task->state == task_state_ready);
			if (EMPTY(task->events)) {
				/*
				 * The task became runnable, but all events
				 * in the run queue were subsequently purged.
				 * Put the task to sleep.
				 */
				task->state = task_state_idle;
Bob Halley's avatar
Bob Halley committed
716
				done = ISC_TRUE;
Bob Halley's avatar
Bob Halley committed
717 718 719
				XTRACE("ready but empty");
			} else
				task->state = task_state_running;
Bob Halley's avatar
base  
Bob Halley committed
720
			while (!done) {
Bob Halley's avatar
update  
Bob Halley committed
721 722 723
				INSIST(!EMPTY(task->events));
				event = HEAD(task->events);
				DEQUEUE(task->events, event, link);
Bob Halley's avatar
base  
Bob Halley committed
724 725 726 727

				/*
				 * Execute the event action.
				 */
Bob Halley's avatar
Bob Halley committed
728
				XTRACE("execute action");
729 730
				if (event->action != NULL) {
					UNLOCK(&task->lock);
731
					(event->action)(task, event);
732 733
					LOCK(&task->lock);
				}
Bob Halley's avatar
Bob Halley committed
734
				dispatch_count++;
Bob Halley's avatar
update  
Bob Halley committed
735
				
736
				if (EMPTY(task->events)) {
Bob Halley's avatar
Bob Halley committed
737
					/*
738 739 740 741
					 * Nothing else to do for this task
					 * right now.  If it is shutting down,
					 * then it is done, otherwise we just
					 * put it to sleep.
Bob Halley's avatar
Bob Halley committed
742
					 */
Bob Halley's avatar
Bob Halley committed
743
					XTRACE("empty");
744 745
					if (TASK_DONE(task)) {
						XTRACE("done");
746 747
						if (task->references == 0)
							free_task = ISC_TRUE;
748 749 750
						task->flags &=
							~TASK_F_SENDOK;
						task->state = task_state_done;
751 752
					} else
						task->state = task_state_idle;
Bob Halley's avatar
Bob Halley committed
753
					done = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
754 755 756 757 758 759 760 761 762 763 764
				} else if (dispatch_count >= task->quantum) {
					/*
					 * Our quantum has expired, but
					 * there is more work to be done.
					 * We'll requeue it to the ready
					 * queue later.
					 *
					 * We don't check quantum until
					 * dispatching at least one event,
					 * so the minimum quantum is one.
					 */
Bob Halley's avatar
Bob Halley committed
765
					XTRACE("quantum");
Bob Halley's avatar
base  
Bob Halley committed
766
					task->state = task_state_ready;
Bob Halley's avatar
Bob Halley committed
767 768
					requeue = ISC_TRUE;
					done = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803
				}
			}
			UNLOCK(&task->lock);

			if (free_task)
				task_free(task);

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

804
	XTHREADTRACE("exit");
Bob Halley's avatar
base  
Bob Halley committed
805

Bob Halley's avatar
update  
Bob Halley committed
806
	return ((isc_threadresult_t)0);
Bob Halley's avatar
base  
Bob Halley committed
807 808 809
}

static void
Bob Halley's avatar
Bob Halley committed
810
manager_free(isc_taskmgr_t *manager) {
Bob Halley's avatar
Bob Halley committed
811 812
	(void)isc_condition_destroy(&manager->work_available);
	(void)isc_mutex_destroy(&manager->lock);
813 814
	isc_mem_put(manager->mctx, manager->threads,
		    manager->workers * sizeof (isc_thread_t));
Bob Halley's avatar
base  
Bob Halley committed
815
	manager->magic = 0;
Bob Halley's avatar
Bob Halley committed
816
	isc_mem_put(manager->mctx, manager, sizeof *manager);
Bob Halley's avatar
base  
Bob Halley committed
817 818
}

Bob Halley's avatar
Bob Halley committed
819
isc_result_t
Bob Halley's avatar
Bob Halley committed
820
isc_taskmgr_create(isc_mem_t *mctx, unsigned int workers, 
Bob Halley's avatar
Bob Halley committed
821
		   unsigned int default_quantum, isc_taskmgr_t **managerp)
Bob Halley's avatar
Bob Halley committed
822 823
{
	unsigned int i, started = 0;
Bob Halley's avatar
Bob Halley committed
824
	isc_taskmgr_t *manager;
825
	isc_thread_t *threads;
Bob Halley's avatar
Bob Halley committed
826

827 828 829 830
	/*
	 * Create a new task manager.
	 */

Bob Halley's avatar
Bob Halley committed
831
	REQUIRE(workers > 0);
Bob Halley's avatar
Bob Halley committed
832
	REQUIRE(managerp != NULL && *managerp == NULL);
Bob Halley's avatar
base  
Bob Halley committed
833

Bob Halley's avatar
Bob Halley committed
834
	manager = isc_mem_get(mctx, sizeof *manager);
Bob Halley's avatar
base  
Bob Halley committed
835
	if (manager == NULL)
Bob Halley's avatar
Bob Halley committed
836
		return (ISC_R_NOMEMORY);
Bob Halley's avatar
base  
Bob Halley committed
837 838
	manager->magic = TASK_MANAGER_MAGIC;
	manager->mctx = mctx;
839 840 841 842 843 844 845
	threads = isc_mem_get(mctx, workers * sizeof (isc_thread_t));
	if (threads == NULL) {
		isc_mem_put(mctx, manager, sizeof *manager);
		return (ISC_R_NOMEMORY);
	}
	manager->threads = threads;
	manager->workers = 0;
Bob Halley's avatar
Bob Halley committed
846
	if (isc_mutex_init(&manager->lock) != ISC_R_SUCCESS) {
847
		isc_mem_put(mctx, threads, workers * sizeof (isc_thread_t));
Bob Halley's avatar
Bob Halley committed
848
		isc_mem_put(mctx, manager, sizeof *manager);
Bob Halley's avatar
Bob Halley committed
849 850 851
		UNEXPECTED_ERROR(__FILE__, __LINE__,
				 "isc_mutex_init() failed");
		return (ISC_R_UNEXPECTED);
Bob Halley's avatar
update  
Bob Halley committed
852
	}
Bob Halley's avatar
base  
Bob Halley committed
853 854 855 856 857
	if (default_quantum == 0)
		default_quantum = DEFAULT_DEFAULT_QUANTUM;
	manager->default_quantum = default_quantum;
	INIT_LIST(manager->tasks);
	INIT_LIST(manager->ready_tasks);
Bob Halley's avatar
Bob Halley committed
858 859
	if (isc_condition_init(&manager->work_available) != ISC_R_SUCCESS) {
		(void)isc_mutex_destroy(&manager->lock);
860
		isc_mem_put(mctx, threads, workers * sizeof (isc_thread_t));
Bob Halley's avatar
Bob Halley committed
861
		isc_mem_put(mctx, manager, sizeof *manager);
Bob Halley's avatar
Bob Halley committed
862 863 864
		UNEXPECTED_ERROR(__FILE__, __LINE__,
				 "isc_condition_init() failed");
		return (ISC_R_UNEXPECTED);
Bob Halley's avatar
update  
Bob Halley committed
865
	}
Bob Halley's avatar
Bob Halley committed
866
	manager->exiting = ISC_FALSE;
Bob Halley's avatar
base  
Bob Halley committed
867 868 869 870 871 872 873
	manager->workers = 0;

	LOCK(&manager->lock);
	/*
	 * Start workers.
	 */
	for (i = 0; i < workers; i++) {
874 875
		if (isc_thread_create(run, manager,
				      &manager->threads[manager->workers]) == 
Bob Halley's avatar
Bob Halley committed
876
		    ISC_R_SUCCESS) {
Bob Halley's avatar
base  
Bob Halley committed
877 878 879 880 881 882 883 884
			manager->workers++;
			started++;
		}
	}
	UNLOCK(&manager->lock);

	if (started == 0) {
		manager_free(manager);
Bob Halley's avatar
Bob Halley committed
885
		return (ISC_R_NOTHREADS);
Bob Halley's avatar
base  
Bob Halley committed
886 887 888 889
	}		

	*managerp = manager;

Bob Halley's avatar
Bob Halley committed
890
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
891 892
}

Bob Halley's avatar
update  
Bob Halley committed
893
void
Bob Halley's avatar
Bob Halley committed
894 895 896
isc_taskmgr_destroy(isc_taskmgr_t **managerp) {
	isc_taskmgr_t *manager;
	isc_task_t *task;
897
	isc_event_t *event, *prev;
898
	unsigned int i;
Bob Halley's avatar
base  
Bob Halley committed
899

900 901 902 903
	/*
	 * Destroy '*managerp'.
	 */

Bob Halley's avatar
base  
Bob Halley committed
904 905 906 907
	REQUIRE(managerp != NULL);
	manager = *managerp;
	REQUIRE(VALID_MANAGER(manager));

908
	XTHREADTRACE("isc_taskmgr_destroy");
Bob Halley's avatar
base  
Bob Halley committed
909 910 911 912
	/*
	 * Only one non-worker thread may ever call this routine.
	 * If a worker thread wants to initiate shutdown of the
	 * task manager, it should ask some non-worker thread to call
Bob Halley's avatar
Bob Halley committed
913
	 * isc_taskmgr_destroy(), e.g. by signalling a condition variable
Bob Halley's avatar
base  
Bob Halley committed
914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931
	 * that the startup thread is sleeping on.
	 */

	/*
	 * Unlike elsewhere, we're going to hold this lock a long time.
	 * We need to do so, because otherwise the list of tasks could
	 * change while we were traversing it.
	 *
	 * This is also the only function where we will hold both the
	 * task manager lock and a task lock at the same time.
	 */

	LOCK(&manager->lock);

	/*
	 * Make sure we only get called once.
	 */
	INSIST(!manager->exiting);
Bob Halley's avatar
Bob Halley committed
932
	manager->exiting = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
933 934

	/*
935
	 * Post shutdown event(s) to every task (if they haven't already been
Bob Halley's avatar
update  
Bob Halley committed
936
	 * posted).
Bob Halley's avatar
base  
Bob Halley committed
937 938 939 940 941
	 */
	for (task = HEAD(manager->tasks);
	     task != NULL;
	     task = NEXT(task, link)) {
		LOCK(&task->lock);
942 943
		if ((task->flags & TASK_F_SHUTTINGDOWN) == 0) {
			task->flags |= TASK_F_SHUTTINGDOWN;
Bob Halley's avatar
update  
Bob Halley committed
944
			if (task->state == task_state_idle) {
945 946 947 948 949 950 951 952 953 954
				INSIST(EMPTY(task->events));
				if (EMPTY(task->on_shutdown) &&
				    TASK_DONE(task)) {
					task->flags &= ~TASK_F_SENDOK;
					task->state = task_state_done;
				} else {
					task->state = task_state_ready;
					ENQUEUE(manager->ready_tasks, task,
						ready_link);
				}
Bob Halley's avatar
update  
Bob Halley committed
955 956
			}
			INSIST(task->state == task_state_ready ||
957 958 959 960 961 962 963 964 965 966 967 968 969 970 971
			       task->state == task_state_running ||
			       task->state == task_state_done);
			if (task->state != task_state_done) {
				/*
				 * Note that we post shutdown events LIFO.
				 */
				for (event = TAIL(task->on_shutdown);
				     event != NULL;
				     event = prev) {
					prev = PREV(event, link);
					DEQUEUE(task->on_shutdown, event,
						link);
					ENQUEUE(task->events, event, link);
				}
			}
Bob Halley's avatar
base  
Bob Halley committed
972 973 974 975 976 977 978 979 980 981
		}
		UNLOCK(&task->lock);
	}

	/*
	 * Wake up any sleeping workers.  This ensures we get work done if
	 * there's work left to do, and if there are already no tasks left
	 * it will cause the workers to see manager->exiting.
	 */
	BROADCAST(&manager->work_available);
982
	UNLOCK(&manager->lock);
Bob Halley's avatar
base  
Bob Halley committed
983 984 985 986

	/*
	 * Wait for all the worker threads to exit.
	 */
987 988
	for (i = 0; i < manager->workers; i++)
		(void)isc_thread_join(manager->threads[i], NULL);
Bob Halley's avatar
base  
Bob Halley committed
989 990 991 992 993

	manager_free(manager);

	*managerp = NULL;
}