task.c 45.5 KB
Newer Older
Bob Halley's avatar
Bob Halley committed
1
/*
Automatic Updater's avatar
Automatic Updater committed
2
 * Copyright (C) 2004-2011  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

Evan Hunt's avatar
Evan Hunt committed
18
/* $Id: task.c,v 1.122 2011/09/03 16:27:51 each 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

Francis Dupont's avatar
Francis Dupont committed
43 44 45 46
#ifdef OPENSSL_LEAKS
#include <openssl/err.h>
#endif

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
/*%
 * 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 */

67 68
#include "task_p.h"

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

Bob Halley's avatar
Bob Halley committed
82
/***
Bob Halley's avatar
Bob Halley committed
83
 *** Types.
Bob Halley's avatar
Bob Halley committed
84 85
 ***/

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

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

97 98
#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
99

100 101 102 103
typedef struct isc__task isc__task_t;
typedef struct isc__taskmgr isc__taskmgr_t;

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

Bob Halley's avatar
Bob Halley committed
124
#define TASK_F_SHUTTINGDOWN		0x01
125
#define TASK_F_PRIVILEGED		0x02
126

127 128
#define TASK_SHUTTINGDOWN(t)		(((t)->flags & TASK_F_SHUTTINGDOWN) \
					 != 0)
129

130 131
#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
132

133 134 135
typedef ISC_LIST(isc__task_t)	isc__tasklist_t;

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

164
#define DEFAULT_TASKMGR_QUANTUM		10
Bob Halley's avatar
Bob Halley committed
165 166
#define DEFAULT_DEFAULT_QUANTUM		5
#define FINISHED(m)			((m)->exiting && EMPTY((m)->tasks))
Bob Halley's avatar
Bob Halley committed
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
#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);
232 233 234 235 236 237 238 239 240
ISC_TASKFUNC_SCOPE void
isc__task_setprivilege(isc_task_t *task0, isc_boolean_t priv);
ISC_TASKFUNC_SCOPE isc_boolean_t
isc__task_privilege(isc_task_t *task0);
ISC_TASKFUNC_SCOPE void
isc__taskmgr_setmode(isc_taskmgr_t *manager0, isc_taskmgrmode_t mode);
ISC_TASKFUNC_SCOPE isc_taskmgrmode_t
isc__taskmgr_mode(isc_taskmgr_t *manager0);

Evan Hunt's avatar
Evan Hunt committed
241
static inline isc_boolean_t
242 243
empty_readyq(isc__taskmgr_t *manager);

Evan Hunt's avatar
Evan Hunt committed
244
static inline isc__task_t *
245 246
pop_readyq(isc__taskmgr_t *manager);

247
static inline void
248
push_readyq(isc__taskmgr_t *manager, isc__task_t *task);
249 250 251 252 253 254 255

static struct isc__taskmethods {
	isc_taskmethods_t methods;

	/*%
	 * The following are defined just for avoiding unused static functions.
	 */
256
#ifndef BIND9
257
	void *purgeevent, *unsendrange, *getname, *gettag, *getcurrenttime;
258
#endif
259 260 261 262 263 264 265 266 267 268 269 270
} 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,
271 272
		isc__task_purgerange,
		isc__task_beginexclusive,
273 274 275
		isc__task_endexclusive,
		isc__task_setprivilege,
		isc__task_privilege
276 277 278 279 280
	}
#ifndef BIND9
	,
	(void *)isc__task_purgeevent, (void *)isc__task_unsendrange,
	(void *)isc__task_getname, (void *)isc__task_gettag,
281
	(void *)isc__task_getcurrenttime
282
#endif
283 284 285 286
};

static isc_taskmgrmethods_t taskmgrmethods = {
	isc__taskmgr_destroy,
287 288
	isc__taskmgr_setmode,
	isc__taskmgr_mode,
289 290
	isc__task_create
};
291

Bob Halley's avatar
base  
Bob Halley committed
292 293 294 295 296
/***
 *** Tasks.
 ***/

static void
297 298
task_finished(isc__task_t *task) {
	isc__taskmgr_t *manager = task->manager;
Bob Halley's avatar
base  
Bob Halley committed
299 300

	REQUIRE(EMPTY(task->events));
301
	REQUIRE(EMPTY(task->on_shutdown));
302 303 304 305
	REQUIRE(task->references == 0);
	REQUIRE(task->state == task_state_done);

	XTRACE("task_finished");
Bob Halley's avatar
base  
Bob Halley committed
306 307 308

	LOCK(&manager->lock);
	UNLINK(manager->tasks, task, link);
309
#ifdef USE_WORKER_THREADS
Bob Halley's avatar
base  
Bob Halley committed
310 311 312 313 314 315 316 317 318
	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);
	}
319
#endif /* USE_WORKER_THREADS */
Bob Halley's avatar
base  
Bob Halley committed
320
	UNLOCK(&manager->lock);
321

Brian Wellington's avatar
Brian Wellington committed
322
	DESTROYLOCK(&task->lock);
323 324
	task->common.impmagic = 0;
	task->common.magic = 0;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
325
	isc_mem_put(manager->mctx, task, sizeof(*task));
Bob Halley's avatar
base  
Bob Halley committed
326 327
}

328 329 330
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
331
{
Automatic Updater's avatar
Automatic Updater committed
332
	isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0;
333
	isc__task_t *task;
334
	isc_boolean_t exiting;
335
	isc_result_t result;
Bob Halley's avatar
base  
Bob Halley committed
336 337 338 339

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

Andreas Gustafsson's avatar
Andreas Gustafsson committed
340
	task = isc_mem_get(manager->mctx, sizeof(*task));
Bob Halley's avatar
base  
Bob Halley committed
341
	if (task == NULL)
Bob Halley's avatar
Bob Halley committed
342
		return (ISC_R_NOMEMORY);
343
	XTRACE("isc_task_create");
Bob Halley's avatar
base  
Bob Halley committed
344
	task->manager = manager;
345 346
	result = isc_mutex_init(&task->lock);
	if (result != ISC_R_SUCCESS) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
347
		isc_mem_put(manager->mctx, task, sizeof(*task));
348
		return (result);
Bob Halley's avatar
update  
Bob Halley committed
349
	}
Bob Halley's avatar
base  
Bob Halley committed
350 351 352
	task->state = task_state_idle;
	task->references = 1;
	INIT_LIST(task->events);
353
	INIT_LIST(task->on_shutdown);
Bob Halley's avatar
base  
Bob Halley committed
354
	task->quantum = quantum;
Bob Halley's avatar
Bob Halley committed
355
	task->flags = 0;
356
	task->now = 0;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
357
	memset(task->name, 0, sizeof(task->name));
358
	task->tag = NULL;
Bob Halley's avatar
base  
Bob Halley committed
359 360
	INIT_LINK(task, link);
	INIT_LINK(task, ready_link);
361
	INIT_LINK(task, ready_priority_link);
Bob Halley's avatar
base  
Bob Halley committed
362

363
	exiting = ISC_FALSE;
Bob Halley's avatar
base  
Bob Halley committed
364
	LOCK(&manager->lock);
365 366 367 368 369 370
	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
371 372
	UNLOCK(&manager->lock);

373
	if (exiting) {
Brian Wellington's avatar
Brian Wellington committed
374
		DESTROYLOCK(&task->lock);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
375
		isc_mem_put(manager->mctx, task, sizeof(*task));
376 377 378
		return (ISC_R_SHUTTINGDOWN);
	}

379 380 381 382
	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
383

Bob Halley's avatar
Bob Halley committed
384
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
385 386
}

387 388 389
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
390

391 392 393
	/*
	 * Attach *targetp to source.
	 */
Bob Halley's avatar
base  
Bob Halley committed
394

395 396
	REQUIRE(VALID_TASK(source));
	REQUIRE(targetp != NULL && *targetp == NULL);
Bob Halley's avatar
base  
Bob Halley committed
397

398
	XTTRACE(source, "isc_task_attach");
399

400 401 402 403
	LOCK(&source->lock);
	source->references++;
	UNLOCK(&source->lock);

404
	*targetp = (isc_task_t *)source;
Bob Halley's avatar
base  
Bob Halley committed
405 406
}

407
static inline isc_boolean_t
408
task_shutdown(isc__task_t *task) {
409 410
	isc_boolean_t was_idle = ISC_FALSE;
	isc_event_t *event, *prev;
411

412 413 414 415 416 417 418
	/*
	 * Caller must be holding the task's lock.
	 */

	XTRACE("task_shutdown");

	if (! TASK_SHUTTINGDOWN(task)) {
419 420
		XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
				      ISC_MSG_SHUTTINGDOWN, "shutting down"));
421 422 423 424 425 426 427 428
		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);
429

430 431 432 433 434 435
		/*
		 * Note that we post shutdown events LIFO.
		 */
		for (event = TAIL(task->on_shutdown);
		     event != NULL;
		     event = prev) {
436 437 438
			prev = PREV(event, ev_link);
			DEQUEUE(task->on_shutdown, event, ev_link);
			ENQUEUE(task->events, event, ev_link);
439 440 441 442 443 444
		}
	}

	return (was_idle);
}

445 446 447 448 449
/*
 * Moves a task onto the appropriate run queue.
 *
 * Caller must NOT hold manager lock.
 */
450
static inline void
451 452
task_ready(isc__task_t *task) {
	isc__taskmgr_t *manager = task->manager;
453 454 455
#ifdef USE_WORKER_THREADS
	isc_boolean_t has_privilege = isc__task_privilege((isc_task_t *) task);
#endif /* USE_WORKER_THREADS */
456 457 458 459 460 461 462

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

	XTRACE("task_ready");

	LOCK(&manager->lock);
463
	push_readyq(manager, task);
464
#ifdef USE_WORKER_THREADS
465 466
	if (manager->mode == isc_taskmgrmode_normal || has_privilege)
		SIGNAL(&manager->work_available);
467
#endif /* USE_WORKER_THREADS */
468 469 470
	UNLOCK(&manager->lock);
}

Bob Halley's avatar
Bob Halley committed
471
static inline isc_boolean_t
472
task_detach(isc__task_t *task) {
473 474 475 476 477 478 479 480 481 482

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

	REQUIRE(task->references > 0);

	XTRACE("detach");

	task->references--;
Bob Halley's avatar
Bob Halley committed
483 484 485 486 487 488 489
	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
490 491
		 * 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
492 493 494
		 */
		task->state = task_state_ready;
		return (ISC_TRUE);
495 496
	}

Bob Halley's avatar
Bob Halley committed
497
	return (ISC_FALSE);
498 499
}

500 501 502
ISC_TASKFUNC_SCOPE void
isc__task_detach(isc_task_t **taskp) {
	isc__task_t *task;
Bob Halley's avatar
Bob Halley committed
503
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
504

505 506 507 508
	/*
	 * Detach *taskp from its task.
	 */

Bob Halley's avatar
base  
Bob Halley committed
509
	REQUIRE(taskp != NULL);
510
	task = (isc__task_t *)*taskp;
Bob Halley's avatar
base  
Bob Halley committed
511 512
	REQUIRE(VALID_TASK(task));

513 514
	XTRACE("isc_task_detach");

Bob Halley's avatar
base  
Bob Halley committed
515
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
516
	was_idle = task_detach(task);
Bob Halley's avatar
base  
Bob Halley committed
517 518
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
519
	if (was_idle)
520
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
521 522 523 524

	*taskp = NULL;
}

Bob Halley's avatar
Bob Halley committed
525
static inline isc_boolean_t
526
task_send(isc__task_t *task, isc_event_t **eventp) {
Bob Halley's avatar
Bob Halley committed
527
	isc_boolean_t was_idle = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
528
	isc_event_t *event;
529

530
	/*
531
	 * Caller must be holding the task lock.
532 533
	 */

Bob Halley's avatar
update  
Bob Halley committed
534 535
	REQUIRE(eventp != NULL);
	event = *eventp;
Bob Halley's avatar
base  
Bob Halley committed
536
	REQUIRE(event != NULL);
537
	REQUIRE(event->ev_type > 0);
Bob Halley's avatar
Bob Halley committed
538
	REQUIRE(task->state != task_state_done);
Bob Halley's avatar
base  
Bob Halley committed
539

540 541
	XTRACE("task_send");

Bob Halley's avatar
Bob Halley committed
542 543 544 545
	if (task->state == task_state_idle) {
		was_idle = ISC_TRUE;
		INSIST(EMPTY(task->events));
		task->state = task_state_ready;
546
	}
Bob Halley's avatar
Bob Halley committed
547 548
	INSIST(task->state == task_state_ready ||
	       task->state == task_state_running);
549
	ENQUEUE(task->events, event, ev_link);
Bob Halley's avatar
Bob Halley committed
550
	*eventp = NULL;
551

Bob Halley's avatar
Bob Halley committed
552
	return (was_idle);
553
}
554

555 556 557
ISC_TASKFUNC_SCOPE void
isc__task_send(isc_task_t *task0, isc_event_t **eventp) {
	isc__task_t *task = (isc__task_t *)task0;
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
	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
574
	was_idle = task_send(task, eventp);
Bob Halley's avatar
base  
Bob Halley committed
575 576 577 578 579 580
	UNLOCK(&task->lock);

	if (was_idle) {
		/*
		 * We need to add this task to the ready queue.
		 *
581 582 583
		 * 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
584 585
		 *
		 * We've changed the state to ready, so no one else will
586 587 588
		 * 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,
589
		 * removed, or a shutdown is started in the interval
590 591
		 * 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
592
		 */
593
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
594
	}
595 596
}

597 598
ISC_TASKFUNC_SCOPE void
isc__task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
Bob Halley's avatar
Bob Halley committed
599
	isc_boolean_t idle1, idle2;
600
	isc__task_t *task;
601 602 603 604 605 606 607

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

	REQUIRE(taskp != NULL);
608
	task = (isc__task_t *)*taskp;
609 610 611
	REQUIRE(VALID_TASK(task));

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

613
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
614 615
	idle1 = task_send(task, eventp);
	idle2 = task_detach(task);
616 617
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
618 619 620 621 622 623
	/*
	 * 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));
624

Bob Halley's avatar
Bob Halley committed
625 626
	if (idle1 || idle2)
		task_ready(task);
627 628

	*taskp = NULL;
Bob Halley's avatar
base  
Bob Halley committed
629 630
}

631
#define PURGE_OK(event)	(((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0)
632 633

static unsigned int
634
dequeue_events(isc__task_t *task, void *sender, isc_eventtype_t first,
635 636
	       isc_eventtype_t last, void *tag,
	       isc_eventlist_t *events, isc_boolean_t purging)
637
{
Bob Halley's avatar
Bob Halley committed
638
	isc_event_t *event, *next_event;
639
	unsigned int count = 0;
640

Bob Halley's avatar
Bob Halley committed
641
	REQUIRE(VALID_TASK(task));
642
	REQUIRE(last >= first);
Bob Halley's avatar
Bob Halley committed
643

644
	XTRACE("dequeue_events");
Bob Halley's avatar
Bob Halley committed
645

Bob Halley's avatar
Bob Halley committed
646
	/*
647 648 649
	 * 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.
650
	 *
651
	 * sender == NULL means "any sender", and tag == NULL means "any tag".
Bob Halley's avatar
Bob Halley committed
652 653 654
	 */

	LOCK(&task->lock);
655 656

	for (event = HEAD(task->events); event != NULL; event = next_event) {
657 658 659 660
		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) &&
661
		    (!purging || PURGE_OK(event))) {
662 663
			DEQUEUE(task->events, event, ev_link);
			ENQUEUE(*events, event, ev_link);
664
			count++;
Bob Halley's avatar
Bob Halley committed
665 666
		}
	}
667

Bob Halley's avatar
Bob Halley committed
668 669
	UNLOCK(&task->lock);

670 671 672
	return (count);
}

673 674 675
ISC_TASKFUNC_SCOPE unsigned int
isc__task_purgerange(isc_task_t *task0, void *sender, isc_eventtype_t first,
		     isc_eventtype_t last, void *tag)
676
{
677
	isc__task_t *task = (isc__task_t *)task0;
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
	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) {
694
		next_event = NEXT(event, ev_link);
Bob Halley's avatar
Bob Halley committed
695
		isc_event_free(&event);
Bob Halley's avatar
Bob Halley committed
696
	}
697

698 699 700 701 702
	/*
	 * Note that purging never changes the state of the task.
	 */

	return (count);
Bob Halley's avatar
Bob Halley committed
703 704
}

705 706 707
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
708
{
709 710 711 712
	/*
	 * Purge events from a task's event queue.
	 */

713 714
	XTRACE("isc_task_purge");

715
	return (isc__task_purgerange(task, sender, type, type, tag));
716 717
}

718 719 720
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
721 722 723 724
	isc_event_t *curr_event, *next_event;

	/*
	 * Purge 'event' from a task's event queue.
725 726
	 *
	 * XXXRTH:  WARNING:  This method may be removed before beta.
Bob Halley's avatar
Bob Halley committed
727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744
	 */

	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) {
745
		next_event = NEXT(curr_event, ev_link);
746
		if (curr_event == event && PURGE_OK(event)) {
747
			DEQUEUE(task->events, curr_event, ev_link);
Bob Halley's avatar
Bob Halley committed
748 749 750 751 752 753 754 755 756 757 758 759 760
			break;
		}
	}
	UNLOCK(&task->lock);

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

761 762 763 764
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)
765 766 767 768 769 770 771
{
	/*
	 * Remove events from a task's event queue.
	 */

	XTRACE("isc_task_unsendrange");

772 773
	return (dequeue_events((isc__task_t *)task, sender, first,
			       last, tag, events, ISC_FALSE));
774 775
}

776 777 778
ISC_TASKFUNC_SCOPE unsigned int
isc__task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type,
		 void *tag, isc_eventlist_t *events)
779 780 781 782 783 784 785
{
	/*
	 * Remove events from a task's event queue.
	 */

	XTRACE("isc_task_unsend");

786 787
	return (dequeue_events((isc__task_t *)task, sender, type,
			       type, tag, events, ISC_FALSE));
788 789
}

790 791 792
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
793
{
794
	isc__task_t *task = (isc__task_t *)task0;
795 796 797 798
	isc_boolean_t disallowed = ISC_FALSE;
	isc_result_t result = ISC_R_SUCCESS;
	isc_event_t *event;

799 800 801 802 803
	/*
	 * Send a shutdown event with action 'action' and argument 'arg' when
	 * 'task' is shutdown.
	 */

804
	REQUIRE(VALID_TASK(task));
805
	REQUIRE(action != NULL);
806

807
	event = isc_event_allocate(task->manager->mctx,
808 809 810 811
				   NULL,
				   ISC_TASKEVENT_SHUTDOWN,
				   action,
				   arg,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
812
				   sizeof(*event));
813 814 815 816
	if (event == NULL)
		return (ISC_R_NOMEMORY);

	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
817
	if (TASK_SHUTTINGDOWN(task)) {
818
		disallowed = ISC_TRUE;
819
		result = ISC_R_SHUTTINGDOWN;
820
	} else
821
		ENQUEUE(task->on_shutdown, event, ev_link);
822 823 824
	UNLOCK(&task->lock);

	if (disallowed)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
825
		isc_mem_put(task->manager->mctx, event, sizeof(*event));
826 827 828 829

	return (result);
}

830 831 832
ISC_TASKFUNC_SCOPE void
isc__task_shutdown(isc_task_t *task0) {
	isc__task_t *task = (isc__task_t *)task0;
833
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
834

835 836 837 838
	/*
	 * Shutdown 'task'.
	 */

Bob Halley's avatar
base  
Bob Halley committed
839 840 841
	REQUIRE(VALID_TASK(task));

	LOCK(&task->lock);
842
	was_idle = task_shutdown(task);
Bob Halley's avatar
base  
Bob Halley committed
843 844
	UNLOCK(&task->lock);

845 846
	if (was_idle)
		task_ready(task);
Bob Halley's avatar
Bob Halley committed
847
}
Bob Halley's avatar
base  
Bob Halley committed
848

849 850
ISC_TASKFUNC_SCOPE void
isc__task_destroy(isc_task_t **taskp) {
Bob Halley's avatar
Bob Halley committed
851

852 853 854 855
	/*
	 * Destroy '*taskp'.
	 */

Bob Halley's avatar
Bob Halley committed
856 857
	REQUIRE(taskp != NULL);

Bob Halley's avatar
Bob Halley committed
858 859
	isc_task_shutdown(*taskp);
	isc_task_detach(taskp);
Bob Halley's avatar
base  
Bob Halley committed
860 861
}

862 863 864
ISC_TASKFUNC_SCOPE void
isc__task_setname(isc_task_t *task0, const char *name, void *tag) {
	isc__task_t *task = (isc__task_t *)task0;
865 866 867 868 869 870 871 872 873 874 875 876 877

	/*
	 * 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
878

879 880 881 882 883 884
ISC_TASKFUNC_SCOPE const char *
isc__task_getname(isc_task_t *task0) {
	isc__task_t *task = (isc__task_t *)task0;

	REQUIRE(VALID_TASK(task));

885 886 887
	return (task->name);
}

888 889 890 891 892 893
ISC_TASKFUNC_SCOPE void *
isc__task_gettag(isc_task_t *task0) {
	isc__task_t *task = (isc__task_t *)task0;

	REQUIRE(VALID_TASK(task));

894 895 896
	return (task->tag);
}

897 898 899 900
ISC_TASKFUNC_SCOPE void
isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t) {
	isc__task_t *task = (isc__task_t *)task0;

901 902 903 904 905 906 907
	REQUIRE(VALID_TASK(task));
	REQUIRE(t != NULL);

	LOCK(&task->lock);
	*t = task->now;
	UNLOCK(&task->lock);
}
908

Bob Halley's avatar
base  
Bob Halley committed
909 910 911
/***
 *** Task Manager.
 ***/
912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972

/*
 * Return ISC_TRUE if the current ready list for the manager, which is
 * either ready_tasks or the ready_priority_tasks, depending on whether
 * the manager is currently in normal or privileged execution mode.
 *
 * Caller must hold the task manager lock.
 */
static inline isc_boolean_t
empty_readyq(isc__taskmgr_t *manager) {
	isc__tasklist_t queue;

	if (manager->mode == isc_taskmgrmode_normal)
		queue = manager->ready_tasks;
	else
		queue = manager->ready_priority_tasks;

	return (ISC_TF(EMPTY(queue)));
}

/*
 * Dequeue and return a pointer to the first task on the current ready
 * list for the manager.
 * If the task is privileged, dequeue it from the other ready list
 * as well.
 *
 * Caller must hold the task manager lock.
 */
static inline isc__task_t *
pop_readyq(isc__taskmgr_t *manager) {
	isc__task_t *task;

	if (manager->mode == isc_taskmgrmode_normal)
		task = HEAD(manager->ready_tasks);
	else
		task = HEAD(mana