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

Bob Halley's avatar
base  
Bob Halley committed
20
#include <isc/assertions.h>
21
#include <isc/boolean.h>
Bob Halley's avatar
Bob Halley committed
22
#include <isc/thread.h>
23 24
#include <isc/mutex.h>
#include <isc/condition.h>
25
#include <isc/error.h>
Bob Halley's avatar
Bob Halley committed
26
#include <isc/task.h>
Bob Halley's avatar
base  
Bob Halley committed
27

28
#include "util.h"
Bob Halley's avatar
base  
Bob Halley committed
29

Bob Halley's avatar
Bob Halley committed
30
#ifdef ISC_TASK_TRACE
Bob Halley's avatar
update  
Bob Halley committed
31
#define XTRACE(m)		printf("%s task %p thread %lu\n", (m), \
Bob Halley's avatar
Bob Halley committed
32
				       task, isc_thread_self())
Bob Halley's avatar
Bob Halley committed
33 34 35
#else
#define XTRACE(m)
#endif
Bob Halley's avatar
base  
Bob Halley committed
36

Bob Halley's avatar
Bob Halley committed
37 38

/***
Bob Halley's avatar
Bob Halley committed
39
 *** Types.
Bob Halley's avatar
Bob Halley committed
40 41
 ***/

Bob Halley's avatar
Bob Halley committed
42 43 44 45 46 47 48 49 50
typedef enum {
	task_state_idle, task_state_ready, task_state_running,
	task_state_shutdown
} 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
51
struct isc_task {
Bob Halley's avatar
Bob Halley committed
52 53
	/* Not locked. */
	unsigned int			magic;
Bob Halley's avatar
Bob Halley committed
54
	isc_taskmgr_t *			manager;
Bob Halley's avatar
Bob Halley committed
55
	isc_mutex_t			lock;
Bob Halley's avatar
Bob Halley committed
56 57 58
	/* Locked by task lock. */
	task_state_t			state;
	unsigned int			references;
Bob Halley's avatar
Bob Halley committed
59
	isc_eventlist_t			events;
60
	isc_eventlist_t			on_shutdown;
Bob Halley's avatar
Bob Halley committed
61
	unsigned int			quantum;
Bob Halley's avatar
Bob Halley committed
62
	isc_boolean_t			enqueue_allowed;
63
	isc_boolean_t			shutting_down;
Bob Halley's avatar
Bob Halley committed
64
	/* Locked by task manager lock. */
Bob Halley's avatar
Bob Halley committed
65 66
	LINK(isc_task_t)		link;
	LINK(isc_task_t)		ready_link;
Bob Halley's avatar
Bob Halley committed
67 68 69 70 71 72
};

#define TASK_MANAGER_MAGIC		0x54534B4DU	/* TSKM. */
#define VALID_MANAGER(m)		((m) != NULL && \
					 (m)->magic == TASK_MANAGER_MAGIC)

Bob Halley's avatar
Bob Halley committed
73
struct isc_taskmgr {
Bob Halley's avatar
Bob Halley committed
74 75
	/* Not locked. */
	unsigned int			magic;
Bob Halley's avatar
Bob Halley committed
76
	isc_memctx_t *			mctx;
Bob Halley's avatar
Bob Halley committed
77
	isc_mutex_t			lock;
78 79
	unsigned int			workers;
	isc_thread_t *			threads;
Bob Halley's avatar
Bob Halley committed
80 81
	/* Locked by task manager lock. */
	unsigned int			default_quantum;
Bob Halley's avatar
Bob Halley committed
82 83
	LIST(isc_task_t)		tasks;
	LIST(isc_task_t)		ready_tasks;
Bob Halley's avatar
Bob Halley committed
84
	isc_condition_t			work_available;
Bob Halley's avatar
Bob Halley committed
85
	isc_boolean_t			exiting;
Bob Halley's avatar
Bob Halley committed
86
};
Bob Halley's avatar
Bob Halley committed
87

Bob Halley's avatar
Bob Halley committed
88 89
#define DEFAULT_DEFAULT_QUANTUM		5
#define FINISHED(m)			((m)->exiting && EMPTY((m)->tasks))
Bob Halley's avatar
Bob Halley committed
90

Bob Halley's avatar
update  
Bob Halley committed
91 92 93 94 95

/***
 *** Events.
 ***/

Bob Halley's avatar
Bob Halley committed
96 97
static inline isc_event_t *
event_allocate(isc_memctx_t *mctx, void *sender, isc_eventtype_t type,
Bob Halley's avatar
Bob Halley committed
98
	       isc_taskaction_t action, void *arg, size_t size)
Bob Halley's avatar
update  
Bob Halley committed
99
{
Bob Halley's avatar
Bob Halley committed
100
	isc_event_t *event;
Bob Halley's avatar
update  
Bob Halley committed
101

Bob Halley's avatar
Bob Halley committed
102
	event = isc_mem_get(mctx, size);
Bob Halley's avatar
update  
Bob Halley committed
103 104 105 106
	if (event == NULL)
		return (NULL);
	event->mctx = mctx;
	event->size = size;
Bob Halley's avatar
Bob Halley committed
107
	event->sender = sender;
Bob Halley's avatar
update  
Bob Halley committed
108 109 110
	event->type = type;
	event->action = action;
	event->arg = arg;
111 112
	event->destroy = NULL;
	INIT_LINK(event, link);
Bob Halley's avatar
update  
Bob Halley committed
113 114 115 116

	return (event);
}

Bob Halley's avatar
Bob Halley committed
117 118
isc_event_t *
isc_event_allocate(isc_memctx_t *mctx, void *sender, isc_eventtype_t type,
Bob Halley's avatar
Bob Halley committed
119
		   isc_taskaction_t action, void *arg, size_t size)
Bob Halley's avatar
update  
Bob Halley committed
120
{
Bob Halley's avatar
Bob Halley committed
121
	if (size < sizeof (struct isc_event))
Bob Halley's avatar
update  
Bob Halley committed
122 123 124 125 126 127
		return (NULL);
	if (type < 0)
		return (NULL);
	if (action == NULL)
		return (NULL);

Bob Halley's avatar
Bob Halley committed
128
	return (event_allocate(mctx, sender, type, action, arg, size));
Bob Halley's avatar
update  
Bob Halley committed
129 130 131
}

void
Bob Halley's avatar
Bob Halley committed
132 133
isc_event_free(isc_event_t **eventp) {
	isc_event_t *event;
Bob Halley's avatar
update  
Bob Halley committed
134 135 136 137 138
	
	REQUIRE(eventp != NULL);
	event = *eventp;
	REQUIRE(event != NULL);

Bob Halley's avatar
Bob Halley committed
139 140
	if (event->destroy != NULL)
		(event->destroy)(event);
Bob Halley's avatar
Bob Halley committed
141
	isc_mem_put(event->mctx, event, event->size);
Bob Halley's avatar
update  
Bob Halley committed
142 143 144 145

	*eventp = NULL;
}

Bob Halley's avatar
base  
Bob Halley committed
146 147 148 149 150
/***
 *** Tasks.
 ***/

static void
Bob Halley's avatar
Bob Halley committed
151 152
task_free(isc_task_t *task) {
	isc_taskmgr_t *manager = task->manager;
Bob Halley's avatar
base  
Bob Halley committed
153

Bob Halley's avatar
Bob Halley committed
154
	XTRACE("free task");
Bob Halley's avatar
base  
Bob Halley committed
155
	REQUIRE(EMPTY(task->events));
156
	REQUIRE(EMPTY(task->on_shutdown));
Bob Halley's avatar
base  
Bob Halley committed
157 158 159 160 161 162 163 164 165 166 167 168 169

	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
170
	(void)isc_mutex_destroy(&task->lock);
Bob Halley's avatar
base  
Bob Halley committed
171
	task->magic = 0;
Bob Halley's avatar
Bob Halley committed
172
	isc_mem_put(manager->mctx, task, sizeof *task);
Bob Halley's avatar
base  
Bob Halley committed
173 174
}

Bob Halley's avatar
Bob Halley committed
175
isc_result_t
176 177
isc_task_create(isc_taskmgr_t *manager, unsigned int quantum,
		isc_task_t **taskp)
Bob Halley's avatar
Bob Halley committed
178
{
Bob Halley's avatar
Bob Halley committed
179
	isc_task_t *task;
Bob Halley's avatar
base  
Bob Halley committed
180 181 182 183

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

Bob Halley's avatar
Bob Halley committed
184
	task = isc_mem_get(manager->mctx, sizeof *task);
Bob Halley's avatar
base  
Bob Halley committed
185
	if (task == NULL)
Bob Halley's avatar
Bob Halley committed
186
		return (ISC_R_NOMEMORY);
Bob Halley's avatar
base  
Bob Halley committed
187 188

	task->manager = manager;
Bob Halley's avatar
Bob Halley committed
189
	if (isc_mutex_init(&task->lock) != ISC_R_SUCCESS) {
Bob Halley's avatar
Bob Halley committed
190
		isc_mem_put(manager->mctx, task, sizeof *task);
Bob Halley's avatar
Bob Halley committed
191 192 193
		UNEXPECTED_ERROR(__FILE__, __LINE__,
				 "isc_mutex_init() failed");
		return (ISC_R_UNEXPECTED);
Bob Halley's avatar
update  
Bob Halley committed
194
	}
Bob Halley's avatar
base  
Bob Halley committed
195 196 197
	task->state = task_state_idle;
	task->references = 1;
	INIT_LIST(task->events);
198
	INIT_LIST(task->on_shutdown);
Bob Halley's avatar
base  
Bob Halley committed
199
	task->quantum = quantum;
Bob Halley's avatar
Bob Halley committed
200
	task->enqueue_allowed = ISC_TRUE;
201
	task->shutting_down = ISC_FALSE;
Bob Halley's avatar
base  
Bob Halley committed
202 203 204 205 206 207 208 209 210
	INIT_LINK(task, link);
	INIT_LINK(task, ready_link);

	LOCK(&manager->lock);
	if (task->quantum == 0)
		task->quantum = manager->default_quantum;
	APPEND(manager->tasks, task, link);
	UNLOCK(&manager->lock);

211
	task->magic = TASK_MAGIC;
Bob Halley's avatar
base  
Bob Halley committed
212 213
	*taskp = task;

Bob Halley's avatar
Bob Halley committed
214
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
215 216
}

Bob Halley's avatar
update  
Bob Halley committed
217
void
Bob Halley's avatar
Bob Halley committed
218
isc_task_attach(isc_task_t *task, isc_task_t **taskp) {
Bob Halley's avatar
base  
Bob Halley committed
219 220 221 222 223 224 225 226 227 228 229

	REQUIRE(VALID_TASK(task));
	REQUIRE(taskp != NULL && *taskp == NULL);

	LOCK(&task->lock);
	task->references++;
	UNLOCK(&task->lock);

	*taskp = task;
}

Bob Halley's avatar
Bob Halley committed
230
void
Bob Halley's avatar
Bob Halley committed
231
isc_task_detach(isc_task_t **taskp) {
Bob Halley's avatar
Bob Halley committed
232
	isc_boolean_t free_task = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
233
	isc_task_t *task;
Bob Halley's avatar
base  
Bob Halley committed
234

Bob Halley's avatar
Bob Halley committed
235
	XTRACE("isc_task_detach");
Bob Halley's avatar
base  
Bob Halley committed
236 237 238 239 240 241 242 243

	REQUIRE(taskp != NULL);
	task = *taskp;
	REQUIRE(VALID_TASK(task));

	LOCK(&task->lock);
	REQUIRE(task->references > 0);
	task->references--;
244
	if (task->state == task_state_shutdown && task->references == 0)
Bob Halley's avatar
Bob Halley committed
245
		free_task = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
246 247 248 249 250 251 252 253
	UNLOCK(&task->lock);

	if (free_task)
		task_free(task);

	*taskp = NULL;
}

254
isc_result_t
Bob Halley's avatar
Bob Halley committed
255
isc_task_send(isc_task_t *task, isc_event_t **eventp) {
Bob Halley's avatar
Bob Halley committed
256
	isc_boolean_t was_idle = ISC_FALSE;
257 258
	isc_boolean_t disallowed = ISC_FALSE;
	isc_result_t result = ISC_R_SUCCESS;
Bob Halley's avatar
Bob Halley committed
259
	isc_event_t *event;
Bob Halley's avatar
base  
Bob Halley committed
260 261

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

Bob Halley's avatar
Bob Halley committed
268
	XTRACE("sending");
Bob Halley's avatar
base  
Bob Halley committed
269 270 271 272 273 274
	/*
	 * 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);
275 276 277 278
	/*
	 * Note: we require that task->shutting_down implies
	 * !task->enqueue_allowed.
	 */
Bob Halley's avatar
update  
Bob Halley committed
279
	if (task->enqueue_allowed) {
Bob Halley's avatar
base  
Bob Halley committed
280
		if (task->state == task_state_idle) {
Bob Halley's avatar
Bob Halley committed
281
			was_idle = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
282 283 284 285 286 287
			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);
288 289 290 291 292 293 294 295 296
	} else {
		disallowed = ISC_TRUE;
		if (task->state == task_state_shutdown)
			result = ISC_R_TASKSHUTDOWN;
		else if (task->shutting_down)
			result = ISC_R_TASKSHUTTINGDOWN;
		else
			result = ISC_R_TASKNOSEND;
	}
Bob Halley's avatar
base  
Bob Halley committed
297 298
	UNLOCK(&task->lock);

299 300
	if (disallowed)
		return (result);
Bob Halley's avatar
base  
Bob Halley committed
301 302

	if (was_idle) {
Bob Halley's avatar
Bob Halley committed
303
		isc_taskmgr_t *manager;
Bob Halley's avatar
base  
Bob Halley committed
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327

		/*
		 * 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
		 * be trying to add this task to the ready queue.  It
		 * thus doesn't matter if more events have been added to
		 * the queue after we gave up the task lock.
		 *
		 * Shutting down a task requires posting a shutdown event
		 * to the task's queue and then executing it, so there's
		 * no way the task can disappear.  A task is always on the
		 * task manager's 'tasks' list, so the task manager can
		 * always post a shutdown event to all tasks if it is
		 * requested to shutdown.
		 */
		manager = task->manager;
		INSIST(VALID_MANAGER(manager));
		LOCK(&manager->lock);
		ENQUEUE(manager->ready_tasks, task, ready_link);
328
		SIGNAL(&manager->work_available);
Bob Halley's avatar
base  
Bob Halley committed
329 330 331
		UNLOCK(&manager->lock);
	}

Bob Halley's avatar
update  
Bob Halley committed
332 333
	*eventp = NULL;

Bob Halley's avatar
Bob Halley committed
334
	XTRACE("sent");
335 336

	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
337 338
}

339
unsigned int
Bob Halley's avatar
Bob Halley committed
340 341
isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type) {
	isc_event_t *event, *next_event;
Bob Halley's avatar
Bob Halley committed
342
	isc_eventlist_t purgeable;
343
	unsigned int purge_count;
Bob Halley's avatar
Bob Halley committed
344 345 346 347 348 349 350 351

	REQUIRE(VALID_TASK(task));
	REQUIRE(type >= 0);

	/*
	 * Purge events matching 'sender' and 'type'.  sender == NULL means
	 * "any sender".  type == NULL means any type.  Task manager events
	 * cannot be purged.
352 353
	 *
	 * Purging never changes the state of the task.
Bob Halley's avatar
Bob Halley committed
354 355 356
	 */

	INIT_LIST(purgeable);
357
	purge_count = 0;
Bob Halley's avatar
Bob Halley committed
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375

	LOCK(&task->lock);
	for (event = HEAD(task->events);
	     event != NULL;
	     event = next_event) {
		next_event = NEXT(event, link);
		if ((sender == NULL || event->sender == sender) &&
		    ((type == 0 && event->type > 0) || event->type == type)) {
			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
376
		isc_event_free(&event);
377
		purge_count++;
Bob Halley's avatar
Bob Halley committed
378
	}
379 380

	return (purge_count);
Bob Halley's avatar
Bob Halley committed
381 382
}

383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 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
isc_result_t
isc_task_allowsend(isc_task_t *task, isc_boolean_t enqueue_allowed) {
	isc_result_t result = ISC_R_SUCCESS;

	REQUIRE(VALID_TASK(task));

	LOCK(&task->lock);
	if (task->state == task_state_shutdown)
		result = ISC_R_TASKSHUTDOWN;
	else if (task->shutting_down)
		result = ISC_R_TASKSHUTTINGDOWN;
	else
		task->enqueue_allowed = enqueue_allowed;
	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;

	REQUIRE(VALID_TASK(task));

	event = event_allocate(task->manager->mctx,
			       NULL,
			       ISC_TASKEVENT_SHUTDOWN,
			       action,
			       arg,
			       sizeof *event);
	if (event == NULL)
		return (ISC_R_NOMEMORY);

	LOCK(&task->lock);
	if (task->state == task_state_shutdown) {
		disallowed = ISC_TRUE;
		result = ISC_R_TASKSHUTDOWN;
	} else if (task->shutting_down) {
		disallowed = ISC_TRUE;
		result = ISC_R_TASKSHUTTINGDOWN;
	} else
		ENQUEUE(task->on_shutdown, event, link);
	UNLOCK(&task->lock);

	if (disallowed)
		isc_mem_put(task->manager->mctx, event, sizeof *event);

	return (result);
}

Bob Halley's avatar
Bob Halley committed
435
void
Bob Halley's avatar
Bob Halley committed
436
isc_task_shutdown(isc_task_t *task) {
Bob Halley's avatar
Bob Halley committed
437
	isc_boolean_t was_idle = ISC_FALSE;
438 439
	isc_boolean_t queued_something = ISC_FALSE;
	isc_event_t *event, *prev;
Bob Halley's avatar
base  
Bob Halley committed
440 441 442 443

	REQUIRE(VALID_TASK(task));

	/*
444
	 * This routine is very similar to isc_task_send() above.
Bob Halley's avatar
base  
Bob Halley committed
445 446 447
	 */

	LOCK(&task->lock);
448
	if (!task->shutting_down) {
Bob Halley's avatar
base  
Bob Halley committed
449
		if (task->state == task_state_idle) {
Bob Halley's avatar
Bob Halley committed
450
			was_idle = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
451 452 453 454 455
			INSIST(EMPTY(task->events));
			task->state = task_state_ready;
		}
		INSIST(task->state == task_state_ready ||
		       task->state == task_state_running);
456 457 458 459 460 461 462 463 464 465 466
		/*
		 * 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;
		}
Bob Halley's avatar
Bob Halley committed
467
		task->enqueue_allowed = ISC_FALSE;
468 469
		task->shutting_down = ISC_TRUE;
	}
Bob Halley's avatar
base  
Bob Halley committed
470 471
	UNLOCK(&task->lock);

472
	if (was_idle && queued_something) {
Bob Halley's avatar
Bob Halley committed
473
		isc_taskmgr_t *manager;
Bob Halley's avatar
base  
Bob Halley committed
474 475 476 477 478

		manager = task->manager;
		INSIST(VALID_MANAGER(manager));
		LOCK(&manager->lock);
		ENQUEUE(manager->ready_tasks, task, ready_link);
479
		SIGNAL(&manager->work_available);
Bob Halley's avatar
base  
Bob Halley committed
480 481
		UNLOCK(&manager->lock);
	}
Bob Halley's avatar
Bob Halley committed
482
}
Bob Halley's avatar
base  
Bob Halley committed
483

Bob Halley's avatar
Bob Halley committed
484
void
Bob Halley's avatar
Bob Halley committed
485
isc_task_destroy(isc_task_t **taskp) {
Bob Halley's avatar
Bob Halley committed
486 487 488

	REQUIRE(taskp != NULL);

Bob Halley's avatar
Bob Halley committed
489 490
	isc_task_shutdown(*taskp);
	isc_task_detach(taskp);
Bob Halley's avatar
base  
Bob Halley committed
491 492 493
}


Bob Halley's avatar
Bob Halley committed
494

Bob Halley's avatar
base  
Bob Halley committed
495 496 497 498
/***
 *** Task Manager.
 ***/

Bob Halley's avatar
update  
Bob Halley committed
499
static isc_threadresult_t
Bob Halley's avatar
Bob Halley committed
500 501 502
#ifdef _WIN32
WINAPI
#endif
Bob Halley's avatar
update  
Bob Halley committed
503
run(void *uap) {
Bob Halley's avatar
Bob Halley committed
504 505
	isc_taskmgr_t *manager = uap;
	isc_task_t *task;
Bob Halley's avatar
base  
Bob Halley committed
506

Bob Halley's avatar
Bob Halley committed
507
	XTRACE("start");
Bob Halley's avatar
base  
Bob Halley committed
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564

	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
565
		 * isc_task_send() above, it is safe for us to dequeue
Bob Halley's avatar
base  
Bob Halley committed
566 567 568 569 570
		 * 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)) {
Bob Halley's avatar
Bob Halley committed
571
			XTRACE("wait");
Bob Halley's avatar
base  
Bob Halley committed
572
			WAIT(&manager->work_available, &manager->lock);
Bob Halley's avatar
Bob Halley committed
573
			XTRACE("awake");
Bob Halley's avatar
base  
Bob Halley committed
574
		}
Bob Halley's avatar
Bob Halley committed
575
		XTRACE("working");
Bob Halley's avatar
base  
Bob Halley committed
576 577 578
		
		task = HEAD(manager->ready_tasks);
		if (task != NULL) {
Bob Halley's avatar
Bob Halley committed
579
			unsigned int dispatch_count = 0;
Bob Halley's avatar
Bob Halley committed
580 581 582
			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
583
			isc_event_t *event;
Bob Halley's avatar
base  
Bob Halley committed
584 585 586 587 588 589 590 591 592 593 594 595

			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
596 597 598 599 600 601 602 603
			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
604
				done = ISC_TRUE;
Bob Halley's avatar
Bob Halley committed
605 606 607
				XTRACE("ready but empty");
			} else
				task->state = task_state_running;
Bob Halley's avatar
base  
Bob Halley committed
608
			while (!done) {
Bob Halley's avatar
update  
Bob Halley committed
609 610 611
				INSIST(!EMPTY(task->events));
				event = HEAD(task->events);
				DEQUEUE(task->events, event, link);
Bob Halley's avatar
base  
Bob Halley committed
612 613 614 615 616
				UNLOCK(&task->lock);

				/*
				 * Execute the event action.
				 */
Bob Halley's avatar
Bob Halley committed
617
				XTRACE("execute action");
Bob Halley's avatar
update  
Bob Halley committed
618
				if (event->action != NULL)
619
					(event->action)(task, event);
Bob Halley's avatar
Bob Halley committed
620
				dispatch_count++;
Bob Halley's avatar
update  
Bob Halley committed
621
				
Bob Halley's avatar
base  
Bob Halley committed
622
				LOCK(&task->lock);
623
				if (EMPTY(task->events)) {
Bob Halley's avatar
Bob Halley committed
624
					/*
625 626 627 628
					 * 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
629
					 */
Bob Halley's avatar
Bob Halley committed
630
					XTRACE("empty");
631 632 633 634 635 636 637 638
					if (task->shutting_down) {
						XTRACE("shutdown");
						if (task->references == 0)
							free_task = ISC_TRUE;
						task->state =
							task_state_shutdown;
					} else
						task->state = task_state_idle;
Bob Halley's avatar
Bob Halley committed
639
					done = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
640 641 642 643 644 645 646 647 648 649 650
				} 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
651
					XTRACE("quantum");
Bob Halley's avatar
base  
Bob Halley committed
652
					task->state = task_state_ready;
Bob Halley's avatar
Bob Halley committed
653 654
					requeue = ISC_TRUE;
					done = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
				}
			}
			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);

Bob Halley's avatar
Bob Halley committed
690
	XTRACE("exit");
Bob Halley's avatar
base  
Bob Halley committed
691

Bob Halley's avatar
update  
Bob Halley committed
692
	return ((isc_threadresult_t)0);
Bob Halley's avatar
base  
Bob Halley committed
693 694 695
}

static void
Bob Halley's avatar
Bob Halley committed
696
manager_free(isc_taskmgr_t *manager) {
Bob Halley's avatar
Bob Halley committed
697 698
	(void)isc_condition_destroy(&manager->work_available);
	(void)isc_mutex_destroy(&manager->lock);
699 700
	isc_mem_put(manager->mctx, manager->threads,
		    manager->workers * sizeof (isc_thread_t));
Bob Halley's avatar
base  
Bob Halley committed
701
	manager->magic = 0;
Bob Halley's avatar
Bob Halley committed
702
	isc_mem_put(manager->mctx, manager, sizeof *manager);
Bob Halley's avatar
base  
Bob Halley committed
703 704
}

Bob Halley's avatar
Bob Halley committed
705
isc_result_t
Bob Halley's avatar
Bob Halley committed
706 707
isc_taskmgr_create(isc_memctx_t *mctx, unsigned int workers, 
		   unsigned int default_quantum, isc_taskmgr_t **managerp)
Bob Halley's avatar
Bob Halley committed
708 709
{
	unsigned int i, started = 0;
Bob Halley's avatar
Bob Halley committed
710
	isc_taskmgr_t *manager;
711
	isc_thread_t *threads;
Bob Halley's avatar
Bob Halley committed
712 713

	REQUIRE(workers > 0);
Bob Halley's avatar
base  
Bob Halley committed
714

Bob Halley's avatar
Bob Halley committed
715
	manager = isc_mem_get(mctx, sizeof *manager);
Bob Halley's avatar
base  
Bob Halley committed
716
	if (manager == NULL)
Bob Halley's avatar
Bob Halley committed
717
		return (ISC_R_NOMEMORY);
Bob Halley's avatar
base  
Bob Halley committed
718 719
	manager->magic = TASK_MANAGER_MAGIC;
	manager->mctx = mctx;
720 721 722 723 724 725 726
	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
727
	if (isc_mutex_init(&manager->lock) != ISC_R_SUCCESS) {
728
		isc_mem_put(mctx, threads, workers * sizeof (isc_thread_t));
Bob Halley's avatar
Bob Halley committed
729
		isc_mem_put(mctx, manager, sizeof *manager);
Bob Halley's avatar
Bob Halley committed
730 731 732
		UNEXPECTED_ERROR(__FILE__, __LINE__,
				 "isc_mutex_init() failed");
		return (ISC_R_UNEXPECTED);
Bob Halley's avatar
update  
Bob Halley committed
733
	}
Bob Halley's avatar
base  
Bob Halley committed
734 735 736 737 738
	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
739 740
	if (isc_condition_init(&manager->work_available) != ISC_R_SUCCESS) {
		(void)isc_mutex_destroy(&manager->lock);
741
		isc_mem_put(mctx, threads, workers * sizeof (isc_thread_t));
Bob Halley's avatar
Bob Halley committed
742
		isc_mem_put(mctx, manager, sizeof *manager);
Bob Halley's avatar
Bob Halley committed
743 744 745
		UNEXPECTED_ERROR(__FILE__, __LINE__,
				 "isc_condition_init() failed");
		return (ISC_R_UNEXPECTED);
Bob Halley's avatar
update  
Bob Halley committed
746
	}
Bob Halley's avatar
Bob Halley committed
747
	manager->exiting = ISC_FALSE;
Bob Halley's avatar
base  
Bob Halley committed
748 749 750 751 752 753 754
	manager->workers = 0;

	LOCK(&manager->lock);
	/*
	 * Start workers.
	 */
	for (i = 0; i < workers; i++) {
755 756
		if (isc_thread_create(run, manager,
				      &manager->threads[manager->workers]) == 
Bob Halley's avatar
Bob Halley committed
757
		    ISC_R_SUCCESS) {
Bob Halley's avatar
base  
Bob Halley committed
758 759 760 761 762 763 764 765
			manager->workers++;
			started++;
		}
	}
	UNLOCK(&manager->lock);

	if (started == 0) {
		manager_free(manager);
Bob Halley's avatar
Bob Halley committed
766
		return (ISC_R_NOTHREADS);
Bob Halley's avatar
base  
Bob Halley committed
767 768 769 770
	}		

	*managerp = manager;

Bob Halley's avatar
Bob Halley committed
771
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
772 773
}

Bob Halley's avatar
update  
Bob Halley committed
774
void
Bob Halley's avatar
Bob Halley committed
775 776 777
isc_taskmgr_destroy(isc_taskmgr_t **managerp) {
	isc_taskmgr_t *manager;
	isc_task_t *task;
778
	isc_event_t *event, *prev;
779
	unsigned int i;
Bob Halley's avatar
base  
Bob Halley committed
780 781 782 783 784

	REQUIRE(managerp != NULL);
	manager = *managerp;
	REQUIRE(VALID_MANAGER(manager));

Bob Halley's avatar
Bob Halley committed
785
	XTRACE("isc_taskmgr_destroy");
Bob Halley's avatar
base  
Bob Halley committed
786 787 788 789
	/*
	 * 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
790
	 * isc_taskmgr_destroy(), e.g. by signalling a condition variable
Bob Halley's avatar
base  
Bob Halley committed
791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808
	 * 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
809
	manager->exiting = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
810 811

	/*
Bob Halley's avatar
update  
Bob Halley committed
812 813
	 * Post the shutdown event to every task (if it hasn't already been
	 * posted).
Bob Halley's avatar
base  
Bob Halley committed
814 815 816 817 818
	 */
	for (task = HEAD(manager->tasks);
	     task != NULL;
	     task = NEXT(task, link)) {
		LOCK(&task->lock);
819 820 821 822 823 824 825 826 827 828 829
		if (!task->shutting_down) {
			/*
			 * 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
update  
Bob Halley committed
830 831 832 833 834 835 836
			if (task->state == task_state_idle) {
				task->state = task_state_ready;
				ENQUEUE(manager->ready_tasks, task,
					ready_link);
			}
			INSIST(task->state == task_state_ready ||
			       task->state == task_state_running);
Bob Halley's avatar
Bob Halley committed
837
			task->enqueue_allowed = ISC_FALSE;
838
			task->shutting_down = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
839 840 841 842 843 844 845 846 847 848
		}
		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);
849
	UNLOCK(&manager->lock);
Bob Halley's avatar
base  
Bob Halley committed
850 851 852 853

	/*
	 * Wait for all the worker threads to exit.
	 */
854 855
	for (i = 0; i < manager->workers; i++)
		(void)isc_thread_join(manager->threads[i], NULL);
Bob Halley's avatar
base  
Bob Halley committed
856 857 858 859 860

	manager_free(manager);

	*managerp = NULL;
}