task.c 25.9 KB
Newer Older
Bob Halley's avatar
Bob Halley committed
1
/*
David Lawrence's avatar
David Lawrence committed
2
 * Copyright (C) 1998-2000  Internet Software Consortium.
3
 *
Bob Halley's avatar
Bob Halley committed
4 5 6
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
7
 *
8 9 10 11 12 13 14 15
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Bob Halley's avatar
Bob Halley committed
16
 */
Bob Halley's avatar
base  
Bob Halley committed
17

Brian Wellington's avatar
Brian Wellington committed
18
/* $Id: task.c,v 1.73 2000/08/29 22:55:57 bwelling 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/mem.h>
Brian Wellington's avatar
Brian Wellington committed
34
#include <isc/platform.h>
35
#include <isc/string.h>
Bob Halley's avatar
Bob Halley committed
36
#include <isc/task.h>
37
#include <isc/thread.h>
Michael Graff's avatar
Michael Graff committed
38
#include <isc/util.h>
Bob Halley's avatar
base  
Bob Halley committed
39

40 41 42 43
#ifndef ISC_PLATFORM_USETHREADS
#include "task_p.h"
#endif

44 45
#define ISC_TASK_NAMES 1

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

Bob Halley's avatar
Bob Halley committed
59
/***
Bob Halley's avatar
Bob Halley committed
60
 *** Types.
Bob Halley's avatar
Bob Halley committed
61 62
 ***/

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

Bob Halley's avatar
Bob Halley committed
93
#define TASK_F_SHUTTINGDOWN		0x01
94

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

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

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

Bob Halley's avatar
Bob Halley committed
124 125
#define DEFAULT_DEFAULT_QUANTUM		5
#define FINISHED(m)			((m)->exiting && EMPTY((m)->tasks))
Bob Halley's avatar
Bob Halley committed
126

127 128 129 130
#ifndef ISC_PLATFORM_USETHREADS
static isc_taskmgr_t *taskmgr = NULL;
#endif

Bob Halley's avatar
base  
Bob Halley committed
131 132 133 134 135
/***
 *** Tasks.
 ***/

static void
136
task_finished(isc_task_t *task) {
Bob Halley's avatar
Bob Halley committed
137
	isc_taskmgr_t *manager = task->manager;
Bob Halley's avatar
base  
Bob Halley committed
138 139

	REQUIRE(EMPTY(task->events));
140
	REQUIRE(EMPTY(task->on_shutdown));
141 142 143 144
	REQUIRE(task->references == 0);
	REQUIRE(task->state == task_state_done);

	XTRACE("task_finished");
Bob Halley's avatar
base  
Bob Halley committed
145 146 147

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

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

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

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

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

200
	exiting = ISC_FALSE;
Bob Halley's avatar
base  
Bob Halley committed
201
	LOCK(&manager->lock);
202 203 204 205 206 207
	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
208 209
	UNLOCK(&manager->lock);

210
	if (exiting) {
Brian Wellington's avatar
Brian Wellington committed
211
		DESTROYLOCK(&task->lock);
212
		isc_mem_put(manager->mctx, task, sizeof *task);
213 214 215
		return (ISC_R_SHUTTINGDOWN);
	}

216
	task->magic = TASK_MAGIC;
Bob Halley's avatar
base  
Bob Halley committed
217 218
	*taskp = task;

Bob Halley's avatar
Bob Halley committed
219
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
220 221
}

Bob Halley's avatar
update  
Bob Halley committed
222
void
223
isc_task_attach(isc_task_t *source, isc_task_t **targetp) {
Bob Halley's avatar
base  
Bob Halley committed
224

225 226 227
	/*
	 * Attach *targetp to source.
	 */
Bob Halley's avatar
base  
Bob Halley committed
228

229 230
	REQUIRE(VALID_TASK(source));
	REQUIRE(targetp != NULL && *targetp == NULL);
Bob Halley's avatar
base  
Bob Halley committed
231

232 233
	XTTRACE(source, "attach");

234 235 236 237 238
	LOCK(&source->lock);
	source->references++;
	UNLOCK(&source->lock);

	*targetp = source;
Bob Halley's avatar
base  
Bob Halley committed
239 240
}

241 242 243 244
static inline isc_boolean_t
task_shutdown(isc_task_t *task) {
	isc_boolean_t was_idle = ISC_FALSE;
	isc_event_t *event, *prev;
245

246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
	/*
	 * Caller must be holding the task's lock.
	 */

	XTRACE("task_shutdown");

	if (! TASK_SHUTTINGDOWN(task)) {
		XTRACE("shutting down");
		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) {
268 269 270
			prev = PREV(event, ev_link);
			DEQUEUE(task->on_shutdown, event, ev_link);
			ENQUEUE(task->events, event, ev_link);
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
		}
	}

	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);
289
#ifdef ISC_PLATFORM_USETHREADS
290
	SIGNAL(&manager->work_available);
291
#endif
292 293 294 295

	UNLOCK(&manager->lock);
}

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

Bob Halley's avatar
Bob Halley committed
322
	return (ISC_FALSE);
323 324
}

Bob Halley's avatar
Bob Halley committed
325
void
Bob Halley's avatar
Bob Halley committed
326 327
isc_task_detach(isc_task_t **taskp) {
	isc_task_t *task;
Bob Halley's avatar
Bob Halley committed
328
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
329

330 331 332 333
	/*
	 * Detach *taskp from its task.
	 */

Bob Halley's avatar
base  
Bob Halley committed
334 335 336 337
	REQUIRE(taskp != NULL);
	task = *taskp;
	REQUIRE(VALID_TASK(task));

338 339
	XTRACE("isc_task_detach");

Bob Halley's avatar
base  
Bob Halley committed
340
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
341
	was_idle = task_detach(task);
Bob Halley's avatar
base  
Bob Halley committed
342 343
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
344
	if (was_idle)
345
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
346 347 348 349

	*taskp = NULL;
}

Bob Halley's avatar
Bob Halley committed
350 351 352
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
353
	isc_event_t *event;
354

355
	/*
356
	 * Caller must be holding the task lock.
357 358
	 */

Bob Halley's avatar
update  
Bob Halley committed
359 360
	REQUIRE(eventp != NULL);
	event = *eventp;
Bob Halley's avatar
base  
Bob Halley committed
361
	REQUIRE(event != NULL);
362
	REQUIRE(event->ev_type > 0);
Bob Halley's avatar
Bob Halley committed
363
	REQUIRE(task->state != task_state_done);
Bob Halley's avatar
base  
Bob Halley committed
364

365 366
	XTRACE("task_send");

Bob Halley's avatar
Bob Halley committed
367 368 369 370
	if (task->state == task_state_idle) {
		was_idle = ISC_TRUE;
		INSIST(EMPTY(task->events));
		task->state = task_state_ready;
371
	}
Bob Halley's avatar
Bob Halley committed
372 373
	INSIST(task->state == task_state_ready ||
	       task->state == task_state_running);
374
	ENQUEUE(task->events, event, ev_link);
Bob Halley's avatar
Bob Halley committed
375
	*eventp = NULL;
376

Bob Halley's avatar
Bob Halley committed
377
	return (was_idle);
378
}
379

Bob Halley's avatar
Bob Halley committed
380
void
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
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
398
	was_idle = task_send(task, eventp);
Bob Halley's avatar
base  
Bob Halley committed
399 400 401 402 403 404
	UNLOCK(&task->lock);

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

Bob Halley's avatar
Bob Halley committed
421
void
422
isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
Bob Halley's avatar
Bob Halley committed
423
	isc_boolean_t idle1, idle2;
424 425 426 427 428 429 430 431 432 433 434 435
	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
436

437
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
438 439
	idle1 = task_send(task, eventp);
	idle2 = task_detach(task);
440 441
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
442 443 444 445 446 447
	/*
	 * 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));
448

Bob Halley's avatar
Bob Halley committed
449 450
	if (idle1 || idle2)
		task_ready(task);
451 452

	*taskp = NULL;
Bob Halley's avatar
base  
Bob Halley committed
453 454
}

455
#define PURGE_OK(event)	(((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0)
456 457 458 459 460

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)
461
{
Bob Halley's avatar
Bob Halley committed
462
	isc_event_t *event, *next_event;
463
	unsigned int count = 0;
464

Bob Halley's avatar
Bob Halley committed
465
	REQUIRE(VALID_TASK(task));
466
	REQUIRE(last >= first);
Bob Halley's avatar
Bob Halley committed
467

468
	XTRACE("dequeue_events");
Bob Halley's avatar
Bob Halley committed
469

Bob Halley's avatar
Bob Halley committed
470
	/*
471 472 473
	 * 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.
474
	 *
475
	 * sender == NULL means "any sender", and tag == NULL means "any tag".
Bob Halley's avatar
Bob Halley committed
476 477 478
	 */

	LOCK(&task->lock);
479 480

	for (event = HEAD(task->events); event != NULL; event = next_event) {
481 482 483 484
		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) &&
485
		    (!purging || PURGE_OK(event))) {
486 487
			DEQUEUE(task->events, event, ev_link);
			ENQUEUE(*events, event, ev_link);
488
			count++;
Bob Halley's avatar
Bob Halley committed
489 490
		}
	}
491

Bob Halley's avatar
Bob Halley committed
492 493
	UNLOCK(&task->lock);

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

521 522 523 524 525
	/*
	 * Note that purging never changes the state of the task.
	 */

	return (count);
Bob Halley's avatar
Bob Halley committed
526 527
}

528
unsigned int
Bob Halley's avatar
Bob Halley committed
529
isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
530
	       void *tag)
Bob Halley's avatar
Bob Halley committed
531
{
532 533 534 535
	/*
	 * Purge events from a task's event queue.
	 */

536 537
	XTRACE("isc_task_purge");

Bob Halley's avatar
Bob Halley committed
538
	return (isc_task_purgerange(task, sender, type, type, tag));
539 540
}

Bob Halley's avatar
Bob Halley committed
541 542 543 544 545 546
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.
547 548
	 *
	 * XXXRTH:  WARNING:  This method may be removed before beta.
Bob Halley's avatar
Bob Halley committed
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
	 */

	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) {
567
		next_event = NEXT(curr_event, ev_link);
568
		if (curr_event == event && PURGE_OK(event)) {
569
			DEQUEUE(task->events, curr_event, ev_link);
Bob Halley's avatar
Bob Halley committed
570 571 572 573 574 575 576 577 578 579 580 581 582
			break;
		}
	}
	UNLOCK(&task->lock);

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
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));
}

612
isc_result_t
David Lawrence's avatar
David Lawrence committed
613 614
isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, const void *arg)
{
615 616 617 618
	isc_boolean_t disallowed = ISC_FALSE;
	isc_result_t result = ISC_R_SUCCESS;
	isc_event_t *event;

619 620 621 622 623
	/*
	 * Send a shutdown event with action 'action' and argument 'arg' when
	 * 'task' is shutdown.
	 */

624
	REQUIRE(VALID_TASK(task));
625
	REQUIRE(action != NULL);
626

627
	event = isc_event_allocate(task->manager->mctx,
628 629 630 631 632
				   NULL,
				   ISC_TASKEVENT_SHUTDOWN,
				   action,
				   arg,
				   sizeof *event);
633 634 635 636
	if (event == NULL)
		return (ISC_R_NOMEMORY);

	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
637
	if (TASK_SHUTTINGDOWN(task)) {
638
		disallowed = ISC_TRUE;
639
		result = ISC_R_SHUTTINGDOWN;
640
	} else
641
		ENQUEUE(task->on_shutdown, event, ev_link);
642 643 644
	UNLOCK(&task->lock);

	if (disallowed)
645
		isc_mem_put(task->manager->mctx, event, sizeof *event);
646 647 648 649

	return (result);
}

Bob Halley's avatar
Bob Halley committed
650
void
Bob Halley's avatar
Bob Halley committed
651
isc_task_shutdown(isc_task_t *task) {
652
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
653

654 655 656 657
	/*
	 * Shutdown 'task'.
	 */

Bob Halley's avatar
base  
Bob Halley committed
658 659 660
	REQUIRE(VALID_TASK(task));

	LOCK(&task->lock);
661
	was_idle = task_shutdown(task);
Bob Halley's avatar
base  
Bob Halley committed
662 663
	UNLOCK(&task->lock);

664 665
	if (was_idle)
		task_ready(task);
Bob Halley's avatar
Bob Halley committed
666
}
Bob Halley's avatar
base  
Bob Halley committed
667

Bob Halley's avatar
Bob Halley committed
668
void
Bob Halley's avatar
Bob Halley committed
669
isc_task_destroy(isc_task_t **taskp) {
Bob Halley's avatar
Bob Halley committed
670

671 672 673 674
	/*
	 * Destroy '*taskp'.
	 */

Bob Halley's avatar
Bob Halley committed
675 676
	REQUIRE(taskp != NULL);

Bob Halley's avatar
Bob Halley committed
677 678
	isc_task_shutdown(*taskp);
	isc_task_detach(taskp);
Bob Halley's avatar
base  
Bob Halley committed
679 680
}

681
void
David Lawrence's avatar
David Lawrence committed
682
isc_task_setname(isc_task_t *task, const char *name, void *tag) {
683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699

	/*
	 * Name 'task'.
	 */

	REQUIRE(VALID_TASK(task));

#ifdef ISC_TASK_NAMES
	LOCK(&task->lock);
	memset(task->name, 0, sizeof(task->name));
	strncpy(task->name, name, sizeof(task->name) - 1);
	task->tag = tag;
	UNLOCK(&task->lock);
#else
	(void)name;
	(void)tag;
#endif
Bob Halley's avatar
base  
Bob Halley committed
700

701
}
Bob Halley's avatar
Bob Halley committed
702

703 704 705 706 707 708 709 710 711 712 713
const char *
isc_task_getname(isc_task_t *task) {
	return (task->name);
}

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


Bob Halley's avatar
base  
Bob Halley committed
714 715 716
/***
 *** Task Manager.
 ***/
717 718
static void
dispatch(isc_taskmgr_t *manager) {
Bob Halley's avatar
Bob Halley committed
719
	isc_task_t *task;
Bob Halley's avatar
base  
Bob Halley committed
720 721 722 723 724 725

	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.
726
	 *
Bob Halley's avatar
base  
Bob Halley committed
727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
	 * 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.
754
	 *
Bob Halley's avatar
base  
Bob Halley committed
755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773
	 * 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);
774 775 776
#ifndef ISC_PLATFORM_USETHREADS
	while (!EMPTY(manager->ready_tasks) && !FINISHED(manager)) {
#else
Bob Halley's avatar
base  
Bob Halley committed
777 778 779
	while (!FINISHED(manager)) {
		/*
		 * For reasons similar to those given in the comment in
780
		 * isc_task_send() above, it is safe for us to dequeue
Bob Halley's avatar
base  
Bob Halley committed
781 782 783 784 785
		 * 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)) {
786
			XTHREADTRACE("wait");
Bob Halley's avatar
base  
Bob Halley committed
787
			WAIT(&manager->work_available, &manager->lock);
788
			XTHREADTRACE("awake");
Bob Halley's avatar
base  
Bob Halley committed
789
		}
790
#endif
791
		XTHREADTRACE("working");
792

Bob Halley's avatar
base  
Bob Halley committed
793 794
		task = HEAD(manager->ready_tasks);
		if (task != NULL) {
Bob Halley's avatar
Bob Halley committed
795
			unsigned int dispatch_count = 0;
Bob Halley's avatar
Bob Halley committed
796 797
			isc_boolean_t done = ISC_FALSE;
			isc_boolean_t requeue = ISC_FALSE;
798
			isc_boolean_t finished = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
799
			isc_event_t *event;
Bob Halley's avatar
base  
Bob Halley committed
800 801 802 803 804

			INSIST(VALID_TASK(task));

			/*
			 * Note we only unlock the manager lock if we actually
805
			 * have a task to do.  We must reacquire the manager
Bob Halley's avatar
base  
Bob Halley committed
806 807 808 809 810 811
			 * 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
812
			INSIST(task->state == task_state_ready);
813 814 815 816 817
			task->state = task_state_running;
			XTRACE("running");
			do {
				if (!EMPTY(task->events)) {
					event = HEAD(task->events);
818
					DEQUEUE(task->events, event, ev_link);
Bob Halley's avatar
base  
Bob Halley committed
819

820 821 822 823
					/*
					 * Execute the event action.
					 */
					XTRACE("execute action");
824
					if (event->ev_action != NULL) {
825
						UNLOCK(&task->lock);
826
						(event->ev_action)(task,event);
827 828 829
						LOCK(&task->lock);
					}
					dispatch_count++;
830
				}
831

Bob Halley's avatar
Bob Halley committed
832 833 834 835
				if (task->references == 0 &&
				    EMPTY(task->events) &&
				    !TASK_SHUTTINGDOWN(task)) {
					isc_boolean_t was_idle;
836

Bob Halley's avatar
Bob Halley committed
837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
					/*
					 * 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);
861 862
				}

863
				if (EMPTY(task->events)) {
Bob Halley's avatar
Bob Halley committed
864
					/*
865
					 * Nothing else to do for this task
Bob Halley's avatar
Bob Halley committed
866
					 * right now.
Bob Halley's avatar
Bob Halley committed
867
					 */
Bob Halley's avatar
Bob Halley committed
868
					XTRACE("empty");
Bob Halley's avatar
Bob Halley committed
869 870 871 872 873
					if (task->references == 0 &&
					    TASK_SHUTTINGDOWN(task)) {
						/*
						 * The task is done.
						 */
874
						XTRACE("done");
Bob Halley's avatar
Bob Halley committed
875
						finished = ISC_TRUE;
876
						task->state = task_state_done;
877 878
					} else
						task->state = task_state_idle;
Bob Halley's avatar
Bob Halley committed
879
					done = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
880 881 882 883 884 885 886 887 888 889 890
				} 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
891
					XTRACE("quantum");
Bob Halley's avatar
base  
Bob Halley committed
892
					task->state = task_state_ready;
Bob Halley's avatar
Bob Halley committed
893 894
					requeue = ISC_TRUE;
					done = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
895
				}
896
			} while (!done);
Bob Halley's avatar
base  
Bob Halley committed
897 898
			UNLOCK(&task->lock);

899 900
			if (finished)
				task_finished(task);
Bob Halley's avatar
base  
Bob Halley committed
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928

			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);
929 930 931 932 933 934 935 936 937 938 939 940 941
}

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

	XTHREADTRACE("start");

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

943
	XTHREADTRACE("exit");
Bob Halley's avatar
base  
Bob Halley committed
944

Bob Halley's avatar
update  
Bob Halley committed
945
	return ((isc_threadresult_t)0);
Bob Halley's avatar
base  
Bob Halley committed
946
}
947
#endif
Bob Halley's avatar
base  
Bob Halley committed
948 949

static void
Bob Halley's avatar
Bob Halley committed
950
manager_free(isc_taskmgr_t *manager) {
951 952
	isc_mem_t *mctx;

953
#ifdef ISC_PLATFORM_USETHREADS
Bob Halley's avatar
Bob Halley committed
954
	(void)isc_condition_destroy(&manager->work_available);
955 956
	isc_mem_put(manager->mctx, manager->threads,
		    manager->workers * sizeof (isc_thread_t));
957 958
#endif
	DESTROYLOCK(&manager->lock);
Bob Halley's avatar
base  
Bob Halley committed
959
	manager->magic = 0;
960 961 962
	mctx = manager->mctx;
	isc_mem_put(mctx, manager, sizeof *manager);
	isc_mem_detach(&mctx);
Bob Halley's avatar
base  
Bob Halley committed
963 964
}

Bob Halley's avatar
Bob Halley committed
965
isc_result_t
966
isc_taskmgr_create(isc_mem_t *mctx, unsigned int workers,
Bob Halley's avatar
Bob Halley committed
967
		   unsigned int default_quantum, isc_taskmgr_t **managerp)
Bob Halley's avatar
Bob Halley committed
968 969
{
	unsigned int i, started = 0;
Bob Halley's avatar
Bob Halley committed
970
	isc_taskmgr_t *manager;
Bob Halley's avatar
Bob Halley committed
971

972 973 974 975
	/*
	 * Create a new task manager.
	 */

Bob Halley's avatar
Bob Halley committed
976
	REQUIRE(workers > 0);
Bob Halley's avatar
Bob Halley committed
977
	REQUIRE(managerp != NULL && *managerp == NULL);
Bob Halley's avatar
base  
Bob Halley committed
978

979 980 981 982 983 984 985 986 987 988 989
#ifndef ISC_PLATFORM_USETHREADS
	UNUSED(i);
	UNUSED(started);

	if (taskmgr != NULL) {
		taskmgr->refs++;
		*managerp = taskmgr;
		return (ISC_R_SUCCESS);
	}
#endif

Bob Halley's avatar
Bob Halley committed
990
	manager = isc_mem_get(mctx, sizeof *manager);
Bob Halley's avatar
base  
Bob Halley committed
991
	if (manager == NULL)
Bob Halley's avatar
Bob Halley committed
992
		return (ISC_R_NOMEMORY);
Bob Halley's avatar
base  
Bob Halley committed
993
	manager->magic = TASK_MANAGER_MAGIC;