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

18
/* $Id: task.c,v 1.108 2009/09/01 00:22:28 jinmei Exp $ */
David Lawrence's avatar
David Lawrence committed
19

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

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

Bob Halley's avatar
Bob Halley committed
29 30
#include <config.h>

31
#include <isc/condition.h>
32
#include <isc/event.h>
33
#include <isc/magic.h>
34
#include <isc/mem.h>
35
#include <isc/msgs.h>
Brian Wellington's avatar
Brian Wellington committed
36
#include <isc/platform.h>
37
#include <isc/string.h>
Bob Halley's avatar
Bob Halley committed
38
#include <isc/task.h>
39
#include <isc/thread.h>
Michael Graff's avatar
Michael Graff committed
40
#include <isc/util.h>
41
#include <isc/xml.h>
Bob Halley's avatar
base  
Bob Halley committed
42

43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
/*%
 * For BIND9 internal applications:
 * when built with threads we use multiple worker threads shared by the whole
 * application.
 * when built without threads we share a single global task manager and use
 * an integrated event loop for socket, timer, and other generic task events.
 * For generic library:
 * we don't use either of them: an application can have multiple task managers
 * whether or not it's threaded, and if the application is threaded each thread
 * is expected to have a separate manager; no "worker threads" are shared by
 * the application threads.
 */
#ifdef BIND9
#ifdef ISC_PLATFORM_USETHREADS
#define USE_WORKER_THREADS
#else
#define USE_SHARED_MANAGER
#endif	/* ISC_PLATFORM_USETHREADS */
#endif	/* BIND9 */

#ifndef USE_WORKER_THREADS
64
#include "task_p.h"
65
#endif /* USE_WORKER_THREADS */
66

Bob Halley's avatar
Bob Halley committed
67
#ifdef ISC_TASK_TRACE
68
#define XTRACE(m)		fprintf(stderr, "task %p thread %lu: %s\n", \
69
				       task, isc_thread_self(), (m))
70
#define XTTRACE(t, m)		fprintf(stderr, "task %p thread %lu: %s\n", \
71
				       (t), isc_thread_self(), (m))
72
#define XTHREADTRACE(m)		fprintf(stderr, "thread %lu: %s\n", \
73
				       isc_thread_self(), (m))
Bob Halley's avatar
Bob Halley committed
74 75
#else
#define XTRACE(m)
76
#define XTTRACE(t, m)
77
#define XTHREADTRACE(m)
Bob Halley's avatar
Bob Halley committed
78
#endif
Bob Halley's avatar
base  
Bob Halley committed
79

Bob Halley's avatar
Bob Halley committed
80
/***
Bob Halley's avatar
Bob Halley committed
81
 *** Types.
Bob Halley's avatar
Bob Halley committed
82 83
 ***/

Bob Halley's avatar
Bob Halley committed
84 85
typedef enum {
	task_state_idle, task_state_ready, task_state_running,
86
	task_state_done
Bob Halley's avatar
Bob Halley committed
87 88
} task_state_t;

89
#if defined(HAVE_LIBXML2) && defined(BIND9)
90 91 92
static const char *statenames[] = {
	"idle", "ready", "running", "done",
};
Mark Andrews's avatar
Mark Andrews committed
93
#endif
94

95 96
#define TASK_MAGIC			ISC_MAGIC('T', 'A', 'S', 'K')
#define VALID_TASK(t)			ISC_MAGIC_VALID(t, TASK_MAGIC)
Bob Halley's avatar
Bob Halley committed
97

98 99 100 101
typedef struct isc__task isc__task_t;
typedef struct isc__taskmgr isc__taskmgr_t;

struct isc__task {
Bob Halley's avatar
Bob Halley committed
102
	/* Not locked. */
103 104
	isc_task_t			common;
	isc__taskmgr_t *		manager;
Bob Halley's avatar
Bob Halley committed
105
	isc_mutex_t			lock;
Bob Halley's avatar
Bob Halley committed
106 107 108
	/* Locked by task lock. */
	task_state_t			state;
	unsigned int			references;
Bob Halley's avatar
Bob Halley committed
109
	isc_eventlist_t			events;
110
	isc_eventlist_t			on_shutdown;
Bob Halley's avatar
Bob Halley committed
111
	unsigned int			quantum;
112
	unsigned int			flags;
113
	isc_stdtime_t			now;
114 115
	char				name[16];
	void *				tag;
Bob Halley's avatar
Bob Halley committed
116
	/* Locked by task manager lock. */
117 118
	LINK(isc__task_t)		link;
	LINK(isc__task_t)		ready_link;
Bob Halley's avatar
Bob Halley committed
119 120
};

Bob Halley's avatar
Bob Halley committed
121
#define TASK_F_SHUTTINGDOWN		0x01
122

123 124
#define TASK_SHUTTINGDOWN(t)		(((t)->flags & TASK_F_SHUTTINGDOWN) \
					 != 0)
125

126 127
#define TASK_MANAGER_MAGIC		ISC_MAGIC('T', 'S', 'K', 'M')
#define VALID_MANAGER(m)		ISC_MAGIC_VALID(m, TASK_MANAGER_MAGIC)
Bob Halley's avatar
Bob Halley committed
128

129 130 131
typedef ISC_LIST(isc__task_t)	isc__tasklist_t;

struct isc__taskmgr {
Bob Halley's avatar
Bob Halley committed
132
	/* Not locked. */
133
	isc_taskmgr_t			common;
Bob Halley's avatar
Bob Halley committed
134
	isc_mem_t *			mctx;
Bob Halley's avatar
Bob Halley committed
135
	isc_mutex_t			lock;
136
#ifdef ISC_PLATFORM_USETHREADS
137
	unsigned int			workers;
138
	isc_thread_t *			threads;
139
#endif /* ISC_PLATFORM_USETHREADS */
Bob Halley's avatar
Bob Halley committed
140 141
	/* Locked by task manager lock. */
	unsigned int			default_quantum;
142 143
	LIST(isc__task_t)		tasks;
	isc__tasklist_t			ready_tasks;
144
#ifdef ISC_PLATFORM_USETHREADS
Bob Halley's avatar
Bob Halley committed
145
	isc_condition_t			work_available;
146
	isc_condition_t			exclusive_granted;
147
#endif /* ISC_PLATFORM_USETHREADS */
148 149
	unsigned int			tasks_running;
	isc_boolean_t			exclusive_requested;
Bob Halley's avatar
Bob Halley committed
150
	isc_boolean_t			exiting;
151
#ifdef USE_SHARED_MANAGER
152
	unsigned int			refs;
153
#endif /* ISC_PLATFORM_USETHREADS */
Bob Halley's avatar
Bob Halley committed
154
};
Bob Halley's avatar
Bob Halley committed
155

156
#define DEFAULT_TASKMGR_QUANTUM		10
Bob Halley's avatar
Bob Halley committed
157 158
#define DEFAULT_DEFAULT_QUANTUM		5
#define FINISHED(m)			((m)->exiting && EMPTY((m)->tasks))
Bob Halley's avatar
Bob Halley committed
159

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
#ifdef USE_SHARED_MANAGER
static isc__taskmgr_t *taskmgr = NULL;
#endif /* USE_SHARED_MANAGER */

/*%
 * The following can be either static or public, depending on build environment.
 */

#ifdef BIND9
#define ISC_TASKFUNC_SCOPE
#else
#define ISC_TASKFUNC_SCOPE static
#endif

ISC_TASKFUNC_SCOPE isc_result_t
isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum,
		 isc_task_t **taskp);
ISC_TASKFUNC_SCOPE void
isc__task_attach(isc_task_t *source0, isc_task_t **targetp);
ISC_TASKFUNC_SCOPE void
isc__task_detach(isc_task_t **taskp);
ISC_TASKFUNC_SCOPE void
isc__task_send(isc_task_t *task0, isc_event_t **eventp);
ISC_TASKFUNC_SCOPE void
isc__task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp);
ISC_TASKFUNC_SCOPE unsigned int
isc__task_purgerange(isc_task_t *task0, void *sender, isc_eventtype_t first,
		     isc_eventtype_t last, void *tag);
ISC_TASKFUNC_SCOPE unsigned int
isc__task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
		void *tag);
ISC_TASKFUNC_SCOPE isc_boolean_t
isc__task_purgeevent(isc_task_t *task0, isc_event_t *event);
ISC_TASKFUNC_SCOPE 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);
ISC_TASKFUNC_SCOPE unsigned int
isc__task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type,
		 void *tag, isc_eventlist_t *events);
ISC_TASKFUNC_SCOPE isc_result_t
isc__task_onshutdown(isc_task_t *task0, isc_taskaction_t action,
		     const void *arg);
ISC_TASKFUNC_SCOPE void
isc__task_shutdown(isc_task_t *task0);
ISC_TASKFUNC_SCOPE void
isc__task_destroy(isc_task_t **taskp);
ISC_TASKFUNC_SCOPE void
isc__task_setname(isc_task_t *task0, const char *name, void *tag);
ISC_TASKFUNC_SCOPE const char *
isc__task_getname(isc_task_t *task0);
ISC_TASKFUNC_SCOPE void *
isc__task_gettag(isc_task_t *task0);
ISC_TASKFUNC_SCOPE void
isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t);
ISC_TASKFUNC_SCOPE isc_result_t
isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers,
		    unsigned int default_quantum, isc_taskmgr_t **managerp);
ISC_TASKFUNC_SCOPE void
isc__taskmgr_destroy(isc_taskmgr_t **managerp);
ISC_TASKFUNC_SCOPE isc_result_t
isc__task_beginexclusive(isc_task_t *task);
ISC_TASKFUNC_SCOPE void
isc__task_endexclusive(isc_task_t *task0);

static struct isc__taskmethods {
	isc_taskmethods_t methods;

	/*%
	 * The following are defined just for avoiding unused static functions.
	 */
	void *purgeevent, *unsendrange,
		*getname, *gettag, *getcurrenttime, *beginexclusive,
		*endexclusive;
} taskmethods = {
	{
		isc__task_attach,
		isc__task_detach,
		isc__task_destroy,
		isc__task_send,
		isc__task_sendanddetach,
		isc__task_unsend,
		isc__task_onshutdown,
		isc__task_shutdown,
		isc__task_setname,
		isc__task_purge,
		isc__task_purgerange
	},
	isc__task_purgeevent, isc__task_unsendrange,
	isc__task_getname, isc__task_gettag,
	isc__task_getcurrenttime, isc__task_beginexclusive,
	isc__task_endexclusive
};

static isc_taskmgrmethods_t taskmgrmethods = {
	isc__taskmgr_destroy,
	isc__task_create
};
258

Bob Halley's avatar
base  
Bob Halley committed
259 260 261 262 263
/***
 *** Tasks.
 ***/

static void
264 265
task_finished(isc__task_t *task) {
	isc__taskmgr_t *manager = task->manager;
Bob Halley's avatar
base  
Bob Halley committed
266 267

	REQUIRE(EMPTY(task->events));
268
	REQUIRE(EMPTY(task->on_shutdown));
269 270 271 272
	REQUIRE(task->references == 0);
	REQUIRE(task->state == task_state_done);

	XTRACE("task_finished");
Bob Halley's avatar
base  
Bob Halley committed
273 274 275

	LOCK(&manager->lock);
	UNLINK(manager->tasks, task, link);
276
#ifdef USE_WORKER_THREADS
Bob Halley's avatar
base  
Bob Halley committed
277 278 279 280 281 282 283 284 285
	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);
	}
286
#endif /* USE_WORKER_THREADS */
Bob Halley's avatar
base  
Bob Halley committed
287
	UNLOCK(&manager->lock);
288

Brian Wellington's avatar
Brian Wellington committed
289
	DESTROYLOCK(&task->lock);
290 291
	task->common.impmagic = 0;
	task->common.magic = 0;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
292
	isc_mem_put(manager->mctx, task, sizeof(*task));
Bob Halley's avatar
base  
Bob Halley committed
293 294
}

295 296 297
ISC_TASKFUNC_SCOPE isc_result_t
isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum,
		 isc_task_t **taskp)
Bob Halley's avatar
Bob Halley committed
298
{
299 300
	isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; 
	isc__task_t *task;
301
	isc_boolean_t exiting;
302
	isc_result_t result;
Bob Halley's avatar
base  
Bob Halley committed
303 304 305 306

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

Andreas Gustafsson's avatar
Andreas Gustafsson committed
307
	task = isc_mem_get(manager->mctx, sizeof(*task));
Bob Halley's avatar
base  
Bob Halley committed
308
	if (task == NULL)
Bob Halley's avatar
Bob Halley committed
309
		return (ISC_R_NOMEMORY);
310
	XTRACE("isc_task_create");
Bob Halley's avatar
base  
Bob Halley committed
311
	task->manager = manager;
312 313
	result = isc_mutex_init(&task->lock);
	if (result != ISC_R_SUCCESS) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
314
		isc_mem_put(manager->mctx, task, sizeof(*task));
315
		return (result);
Bob Halley's avatar
update  
Bob Halley committed
316
	}
Bob Halley's avatar
base  
Bob Halley committed
317 318 319
	task->state = task_state_idle;
	task->references = 1;
	INIT_LIST(task->events);
320
	INIT_LIST(task->on_shutdown);
Bob Halley's avatar
base  
Bob Halley committed
321
	task->quantum = quantum;
Bob Halley's avatar
Bob Halley committed
322
	task->flags = 0;
323
	task->now = 0;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
324
	memset(task->name, 0, sizeof(task->name));
325
	task->tag = NULL;
Bob Halley's avatar
base  
Bob Halley committed
326 327 328
	INIT_LINK(task, link);
	INIT_LINK(task, ready_link);

329
	exiting = ISC_FALSE;
Bob Halley's avatar
base  
Bob Halley committed
330
	LOCK(&manager->lock);
331 332 333 334 335 336
	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
337 338
	UNLOCK(&manager->lock);

339
	if (exiting) {
Brian Wellington's avatar
Brian Wellington committed
340
		DESTROYLOCK(&task->lock);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
341
		isc_mem_put(manager->mctx, task, sizeof(*task));
342 343 344
		return (ISC_R_SHUTTINGDOWN);
	}

345 346 347 348
	task->common.methods = (isc_taskmethods_t *)&taskmethods;
	task->common.magic = ISCAPI_TASK_MAGIC;
	task->common.impmagic = TASK_MAGIC;
	*taskp = (isc_task_t *)task;
Bob Halley's avatar
base  
Bob Halley committed
349

Bob Halley's avatar
Bob Halley committed
350
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
351 352
}

353 354 355
ISC_TASKFUNC_SCOPE void
isc__task_attach(isc_task_t *source0, isc_task_t **targetp) {
	isc__task_t *source = (isc__task_t *)source0;
Bob Halley's avatar
base  
Bob Halley committed
356

357 358 359
	/*
	 * Attach *targetp to source.
	 */
Bob Halley's avatar
base  
Bob Halley committed
360

361 362
	REQUIRE(VALID_TASK(source));
	REQUIRE(targetp != NULL && *targetp == NULL);
Bob Halley's avatar
base  
Bob Halley committed
363

364
	XTTRACE(source, "isc_task_attach");
365

366 367 368 369
	LOCK(&source->lock);
	source->references++;
	UNLOCK(&source->lock);

370
	*targetp = (isc_task_t *)source;
Bob Halley's avatar
base  
Bob Halley committed
371 372
}

373
static inline isc_boolean_t
374
task_shutdown(isc__task_t *task) {
375 376
	isc_boolean_t was_idle = ISC_FALSE;
	isc_event_t *event, *prev;
377

378 379 380 381 382 383 384
	/*
	 * Caller must be holding the task's lock.
	 */

	XTRACE("task_shutdown");

	if (! TASK_SHUTTINGDOWN(task)) {
385 386
		XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
				      ISC_MSG_SHUTTINGDOWN, "shutting down"));
387 388 389 390 391 392 393 394 395 396 397 398 399 400
		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) {
401 402 403
			prev = PREV(event, ev_link);
			DEQUEUE(task->on_shutdown, event, ev_link);
			ENQUEUE(task->events, event, ev_link);
404 405 406 407 408 409 410
		}
	}

	return (was_idle);
}

static inline void
411 412
task_ready(isc__task_t *task) {
	isc__taskmgr_t *manager = task->manager;
413 414 415 416 417 418 419 420 421

	REQUIRE(VALID_MANAGER(manager));
	REQUIRE(task->state == task_state_ready);

	XTRACE("task_ready");

	LOCK(&manager->lock);

	ENQUEUE(manager->ready_tasks, task, ready_link);
422
#ifdef USE_WORKER_THREADS
423
	SIGNAL(&manager->work_available);
424
#endif /* USE_WORKER_THREADS */
425 426 427 428

	UNLOCK(&manager->lock);
}

Bob Halley's avatar
Bob Halley committed
429
static inline isc_boolean_t
430
task_detach(isc__task_t *task) {
431 432 433 434 435 436 437 438 439 440

	/*
	 * Caller must be holding the task lock.
	 */

	REQUIRE(task->references > 0);

	XTRACE("detach");

	task->references--;
Bob Halley's avatar
Bob Halley committed
441 442 443 444 445 446 447
	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
448 449
		 * 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
450 451 452
		 */
		task->state = task_state_ready;
		return (ISC_TRUE);
453 454
	}

Bob Halley's avatar
Bob Halley committed
455
	return (ISC_FALSE);
456 457
}

458 459 460
ISC_TASKFUNC_SCOPE void
isc__task_detach(isc_task_t **taskp) {
	isc__task_t *task;
Bob Halley's avatar
Bob Halley committed
461
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
462

463 464 465 466
	/*
	 * Detach *taskp from its task.
	 */

Bob Halley's avatar
base  
Bob Halley committed
467
	REQUIRE(taskp != NULL);
468
	task = (isc__task_t *)*taskp;
Bob Halley's avatar
base  
Bob Halley committed
469 470
	REQUIRE(VALID_TASK(task));

471 472
	XTRACE("isc_task_detach");

Bob Halley's avatar
base  
Bob Halley committed
473
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
474
	was_idle = task_detach(task);
Bob Halley's avatar
base  
Bob Halley committed
475 476
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
477
	if (was_idle)
478
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
479 480 481 482

	*taskp = NULL;
}

Bob Halley's avatar
Bob Halley committed
483
static inline isc_boolean_t
484
task_send(isc__task_t *task, isc_event_t **eventp) {
Bob Halley's avatar
Bob Halley committed
485
	isc_boolean_t was_idle = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
486
	isc_event_t *event;
487

488
	/*
489
	 * Caller must be holding the task lock.
490 491
	 */

Bob Halley's avatar
update  
Bob Halley committed
492 493
	REQUIRE(eventp != NULL);
	event = *eventp;
Bob Halley's avatar
base  
Bob Halley committed
494
	REQUIRE(event != NULL);
495
	REQUIRE(event->ev_type > 0);
Bob Halley's avatar
Bob Halley committed
496
	REQUIRE(task->state != task_state_done);
Bob Halley's avatar
base  
Bob Halley committed
497

498 499
	XTRACE("task_send");

Bob Halley's avatar
Bob Halley committed
500 501 502 503
	if (task->state == task_state_idle) {
		was_idle = ISC_TRUE;
		INSIST(EMPTY(task->events));
		task->state = task_state_ready;
504
	}
Bob Halley's avatar
Bob Halley committed
505 506
	INSIST(task->state == task_state_ready ||
	       task->state == task_state_running);
507
	ENQUEUE(task->events, event, ev_link);
Bob Halley's avatar
Bob Halley committed
508
	*eventp = NULL;
509

Bob Halley's avatar
Bob Halley committed
510
	return (was_idle);
511
}
512

513 514 515
ISC_TASKFUNC_SCOPE void
isc__task_send(isc_task_t *task0, isc_event_t **eventp) {
	isc__task_t *task = (isc__task_t *)task0;
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
	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
532
	was_idle = task_send(task, eventp);
Bob Halley's avatar
base  
Bob Halley committed
533 534 535 536 537 538
	UNLOCK(&task->lock);

	if (was_idle) {
		/*
		 * We need to add this task to the ready queue.
		 *
539 540 541
		 * 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
542 543
		 *
		 * We've changed the state to ready, so no one else will
544 545 546
		 * 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,
547
		 * removed, or a shutdown is started in the interval
548 549
		 * 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
550
		 */
551
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
552
	}
553 554
}

555 556
ISC_TASKFUNC_SCOPE void
isc__task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
Bob Halley's avatar
Bob Halley committed
557
	isc_boolean_t idle1, idle2;
558
	isc__task_t *task;
559 560 561 562 563 564 565

	/*
	 * Send '*event' to '*taskp' and then detach '*taskp' from its
	 * task.
	 */

	REQUIRE(taskp != NULL);
566
	task = (isc__task_t *)*taskp;
567 568 569
	REQUIRE(VALID_TASK(task));

	XTRACE("isc_task_sendanddetach");
Bob Halley's avatar
update  
Bob Halley committed
570

571
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
572 573
	idle1 = task_send(task, eventp);
	idle2 = task_detach(task);
574 575
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
576 577 578 579 580 581
	/*
	 * 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));
582

Bob Halley's avatar
Bob Halley committed
583 584
	if (idle1 || idle2)
		task_ready(task);
585 586

	*taskp = NULL;
Bob Halley's avatar
base  
Bob Halley committed
587 588
}

589
#define PURGE_OK(event)	(((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0)
590 591

static unsigned int
592
dequeue_events(isc__task_t *task, void *sender, isc_eventtype_t first,
593 594
	       isc_eventtype_t last, void *tag,
	       isc_eventlist_t *events, isc_boolean_t purging)
595
{
Bob Halley's avatar
Bob Halley committed
596
	isc_event_t *event, *next_event;
597
	unsigned int count = 0;
598

Bob Halley's avatar
Bob Halley committed
599
	REQUIRE(VALID_TASK(task));
600
	REQUIRE(last >= first);
Bob Halley's avatar
Bob Halley committed
601

602
	XTRACE("dequeue_events");
Bob Halley's avatar
Bob Halley committed
603

Bob Halley's avatar
Bob Halley committed
604
	/*
605 606 607
	 * 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.
608
	 *
609
	 * sender == NULL means "any sender", and tag == NULL means "any tag".
Bob Halley's avatar
Bob Halley committed
610 611 612
	 */

	LOCK(&task->lock);
613 614

	for (event = HEAD(task->events); event != NULL; event = next_event) {
615 616 617 618
		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) &&
619
		    (!purging || PURGE_OK(event))) {
620 621
			DEQUEUE(task->events, event, ev_link);
			ENQUEUE(*events, event, ev_link);
622
			count++;
Bob Halley's avatar
Bob Halley committed
623 624
		}
	}
625

Bob Halley's avatar
Bob Halley committed
626 627
	UNLOCK(&task->lock);

628 629 630
	return (count);
}

631 632 633
ISC_TASKFUNC_SCOPE unsigned int
isc__task_purgerange(isc_task_t *task0, void *sender, isc_eventtype_t first,
		     isc_eventtype_t last, void *tag)
634
{
635
	isc__task_t *task = (isc__task_t *)task0;
636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
	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) {
652
		next_event = NEXT(event, ev_link);
Bob Halley's avatar
Bob Halley committed
653
		isc_event_free(&event);
Bob Halley's avatar
Bob Halley committed
654
	}
655

656 657 658 659 660
	/*
	 * Note that purging never changes the state of the task.
	 */

	return (count);
Bob Halley's avatar
Bob Halley committed
661 662
}

663 664 665
ISC_TASKFUNC_SCOPE unsigned int
isc__task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
		void *tag)
Bob Halley's avatar
Bob Halley committed
666
{
667 668 669 670
	/*
	 * Purge events from a task's event queue.
	 */

671 672
	XTRACE("isc_task_purge");

673
	return (isc__task_purgerange(task, sender, type, type, tag));
674 675
}

676 677 678
ISC_TASKFUNC_SCOPE isc_boolean_t
isc__task_purgeevent(isc_task_t *task0, isc_event_t *event) {
	isc__task_t *task = (isc__task_t *)task0;
Bob Halley's avatar
Bob Halley committed
679 680 681 682
	isc_event_t *curr_event, *next_event;

	/*
	 * Purge 'event' from a task's event queue.
683 684
	 *
	 * XXXRTH:  WARNING:  This method may be removed before beta.
Bob Halley's avatar
Bob Halley committed
685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
	 */

	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) {
703
		next_event = NEXT(curr_event, ev_link);
704
		if (curr_event == event && PURGE_OK(event)) {
705
			DEQUEUE(task->events, curr_event, ev_link);
Bob Halley's avatar
Bob Halley committed
706 707 708 709 710 711 712 713 714 715 716 717 718
			break;
		}
	}
	UNLOCK(&task->lock);

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

719 720 721 722
ISC_TASKFUNC_SCOPE 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)
723 724 725 726 727 728 729
{
	/*
	 * Remove events from a task's event queue.
	 */

	XTRACE("isc_task_unsendrange");

730 731
	return (dequeue_events((isc__task_t *)task, sender, first,
			       last, tag, events, ISC_FALSE));
732 733
}

734 735 736
ISC_TASKFUNC_SCOPE unsigned int
isc__task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type,
		 void *tag, isc_eventlist_t *events)
737 738 739 740 741 742 743
{
	/*
	 * Remove events from a task's event queue.
	 */

	XTRACE("isc_task_unsend");

744 745
	return (dequeue_events((isc__task_t *)task, sender, type,
			       type, tag, events, ISC_FALSE));
746 747
}

748 749 750
ISC_TASKFUNC_SCOPE isc_result_t
isc__task_onshutdown(isc_task_t *task0, isc_taskaction_t action,
		     const void *arg)
David Lawrence's avatar
David Lawrence committed
751
{
752
	isc__task_t *task = (isc__task_t *)task0;
753 754 755 756
	isc_boolean_t disallowed = ISC_FALSE;
	isc_result_t result = ISC_R_SUCCESS;
	isc_event_t *event;

757 758 759 760 761
	/*
	 * Send a shutdown event with action 'action' and argument 'arg' when
	 * 'task' is shutdown.
	 */

762
	REQUIRE(VALID_TASK(task));
763
	REQUIRE(action != NULL);
764

765
	event = isc_event_allocate(task->manager->mctx,
766 767 768 769
				   NULL,
				   ISC_TASKEVENT_SHUTDOWN,
				   action,
				   arg,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
770
				   sizeof(*event));
771 772 773 774
	if (event == NULL)
		return (ISC_R_NOMEMORY);

	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
775
	if (TASK_SHUTTINGDOWN(task)) {
776
		disallowed = ISC_TRUE;
777
		result = ISC_R_SHUTTINGDOWN;
778
	} else
779
		ENQUEUE(task->on_shutdown, event, ev_link);
780 781 782
	UNLOCK(&task->lock);

	if (disallowed)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
783
		isc_mem_put(task->manager->mctx, event, sizeof(*event));
784 785 786 787

	return (result);
}

788 789 790
ISC_TASKFUNC_SCOPE void
isc__task_shutdown(isc_task_t *task0) {
	isc__task_t *task = (isc__task_t *)task0;
791
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
792

793 794 795 796
	/*
	 * Shutdown 'task'.
	 */

Bob Halley's avatar
base  
Bob Halley committed
797 798 799
	REQUIRE(VALID_TASK(task));

	LOCK(&task->lock);
800
	was_idle = task_shutdown(task);
Bob Halley's avatar
base  
Bob Halley committed
801 802
	UNLOCK(&task->lock);

803 804
	if (was_idle)
		task_ready(task);
Bob Halley's avatar
Bob Halley committed
805
}
Bob Halley's avatar
base  
Bob Halley committed
806

807 808
ISC_TASKFUNC_SCOPE void
isc__task_destroy(isc_task_t **taskp) {
Bob Halley's avatar
Bob Halley committed
809

810 811 812 813
	/*
	 * Destroy '*taskp'.
	 */

Bob Halley's avatar
Bob Halley committed
814 815
	REQUIRE(taskp != NULL);

Bob Halley's avatar
Bob Halley committed
816 817
	isc_task_shutdown(*taskp);
	isc_task_detach(taskp);
Bob Halley's avatar
base  
Bob Halley committed
818 819
}

820 821 822
ISC_TASKFUNC_SCOPE void
isc__task_setname(isc_task_t *task0, const char *name, void *tag) {
	isc__task_t *task = (isc__task_t *)task0;
823 824 825 826 827 828 829 830 831 832 833 834 835

	/*
	 * Name 'task'.
	 */

	REQUIRE(VALID_TASK(task));

	LOCK(&task->lock);
	memset(task->name, 0, sizeof(task->name));
	strncpy(task->name, name, sizeof(task->name) - 1);
	task->tag = tag;
	UNLOCK(&task->lock);
}
Bob Halley's avatar
Bob Halley committed
836

837 838 839 840 841 842
ISC_TASKFUNC_SCOPE const char *
isc__task_getname(isc_task_t *task0) {
	isc__task_t *task = (isc__task_t *)task0;

	REQUIRE(VALID_TASK(task));

843 844 845
	return (task->name);
}

846 847 848 849 850 851
ISC_TASKFUNC_SCOPE void *
isc__task_gettag(isc_task_t *task0) {
	isc__task_t *task = (isc__task_t *)task0;

	REQUIRE(VALID_TASK(task));

852 853 854
	return (task->tag);
}

855 856 857 858
ISC_TASKFUNC_SCOPE void
isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t) {
	isc__task_t *task = (isc__task_t *)task0;

859 860 861 862 863 864 865 866 867
	REQUIRE(VALID_TASK(task));
	REQUIRE(t != NULL);

	LOCK(&task->lock);

	*t = task->now;

	UNLOCK(&task->lock);
}
868

Bob Halley's avatar
base  
Bob Halley committed
869 870 871
/***
 *** Task Manager.
 ***/
872
static void
873 874 875
dispatch(isc__taskmgr_t *manager) {
	isc__task_t *task;
#ifndef USE_WORKER_THREADS
876
	unsigned int total_dispatch_count = 0;
877 878
	isc__tasklist_t ready_tasks;
#endif /* USE_WORKER_THREADS */
Bob Halley's avatar
base  
Bob Halley committed
879 880 881 882 883 884

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

932
#ifndef USE_WORKER_THREADS
933 934
	ISC_LIST_INIT(ready_tasks);
#endif
Bob Halley's avatar
base  
Bob Halley committed
935 936
	LOCK(&manager->lock);
	while (!FINISHED(manager)) {
937
#ifdef USE_WORKER_THREADS
Bob Halley's avatar
base  
Bob Halley committed
938 939
		/*
		 * For reasons similar to those given in the comment in
940
		 * isc_task_send() above, it is safe for us to dequeue
Bob Halley's avatar
base  
Bob Halley committed
941 942 943 944
		 * the task while only holding the manager lock, and then
		 * change the task to running state while only holding the
		 * task lock.
		 */
945
		while ((EMPTY(manager->ready_tasks) ||
Automatic Updater's avatar
Automatic Updater committed
946 947 948
			manager->exclusive_requested) &&
			!FINISHED(manager))
		{
949 950 951
			XTHREADTRACE(isc_msgcat_get(isc_msgcat,
						    ISC_MSGSET_GENERAL,
						    ISC_MSG_WAIT, "wait"));
Bob Halley's avatar
base  
Bob Halley committed
952
			WAIT(&manager->work_available, &manager->lock);
953 954 955
			XTHREADTRACE(isc_msgcat_get(isc_msgcat,
						    ISC_MSGSET_TASK,
						    ISC_MSG_AWAKE, "awake"));
Bob Halley's avatar
base  
Bob Halley committed
956
		}
957
#else /* USE_WORKER_THREADS */
958 959 960
		if (total_dispatch_count >= DEFAULT_TASKMGR_QUANTUM ||
		    EMPTY(manager->ready_tasks))
			break;
961
#endif /* USE_WORKER_THREADS */
962 963
		XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK,
					    ISC_MSG_WORKING, "working"));
964

Bob Halley's avatar
base  
Bob Halley committed
965 966
		task = HEAD(manager->ready_tasks);
		if (task != NULL) {
Bob Halley's avatar
Bob Halley committed
967
			unsigned int dispatch_count = 0;
Bob Halley's avatar
Bob Halley committed
968 969
			isc_boolean_t done = ISC_FALSE;
			isc_boolean_t requeue = ISC_FALSE;
970
			isc_boolean_t finished = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
971
			isc_event_t *event;
Bob Halley's avatar
base  
Bob Halley committed
972 973 974 975 976

			INSIST(VALID_TASK(task));

			/*
			 * Note we only unlock the manager lock if we actually
977
			 * have a task to do.  We must reacquire the manager
Bob Halley's avatar
base  
Bob Halley committed
978