task.c 47.8 KB
Newer Older
Bob Halley's avatar
Bob Halley committed
1
/*
Tinderbox User's avatar
Tinderbox User committed
2
 * Copyright (C) 2004-2012  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

Mark Andrews's avatar
Mark Andrews committed
18
/* $Id$ */
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;
113
	unsigned int			nevents;
Bob Halley's avatar
Bob Halley committed
114
	unsigned int			quantum;
115
	unsigned int			flags;
116
	isc_stdtime_t			now;
117 118
	char				name[16];
	void *				tag;
Bob Halley's avatar
Bob Halley committed
119
	/* Locked by task manager lock. */
120 121
	LINK(isc__task_t)		link;
	LINK(isc__task_t)		ready_link;
122
	LINK(isc__task_t)		ready_priority_link;
Bob Halley's avatar
Bob Halley committed
123 124
};

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

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

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

134 135 136
typedef ISC_LIST(isc__task_t)	isc__tasklist_t;

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

167
#define DEFAULT_TASKMGR_QUANTUM		10
Bob Halley's avatar
Bob Halley committed
168 169
#define DEFAULT_DEFAULT_QUANTUM		5
#define FINISHED(m)			((m)->exiting && EMPTY((m)->tasks))
Bob Halley's avatar
Bob Halley committed
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
#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);
231 232 233 234
ISC_TASKFUNC_SCOPE void
isc__taskmgr_setexcltask(isc_taskmgr_t *mgr0, isc_task_t *task0);
ISC_TASKFUNC_SCOPE isc_result_t
isc__taskmgr_excltask(isc_taskmgr_t *mgr0, isc_task_t **taskp);
235 236 237 238
ISC_TASKFUNC_SCOPE isc_result_t
isc__task_beginexclusive(isc_task_t *task);
ISC_TASKFUNC_SCOPE void
isc__task_endexclusive(isc_task_t *task0);
239 240 241 242 243 244 245 246 247
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
248
static inline isc_boolean_t
249 250
empty_readyq(isc__taskmgr_t *manager);

Evan Hunt's avatar
Evan Hunt committed
251
static inline isc__task_t *
252 253
pop_readyq(isc__taskmgr_t *manager);

254
static inline void
255
push_readyq(isc__taskmgr_t *manager, isc__task_t *task);
256 257 258 259 260 261 262

static struct isc__taskmethods {
	isc_taskmethods_t methods;

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

static isc_taskmgrmethods_t taskmgrmethods = {
	isc__taskmgr_destroy,
294 295
	isc__taskmgr_setmode,
	isc__taskmgr_mode,
296 297 298
	isc__task_create,
	isc__taskmgr_setexcltask,
	isc__taskmgr_excltask
299
};
300

Bob Halley's avatar
base  
Bob Halley committed
301 302 303 304 305
/***
 *** Tasks.
 ***/

static void
306 307
task_finished(isc__task_t *task) {
	isc__taskmgr_t *manager = task->manager;
Bob Halley's avatar
base  
Bob Halley committed
308 309

	REQUIRE(EMPTY(task->events));
310
	REQUIRE(task->nevents == 0);
311
	REQUIRE(EMPTY(task->on_shutdown));
312 313 314 315
	REQUIRE(task->references == 0);
	REQUIRE(task->state == task_state_done);

	XTRACE("task_finished");
Bob Halley's avatar
base  
Bob Halley committed
316 317 318

	LOCK(&manager->lock);
	UNLINK(manager->tasks, task, link);
319
#ifdef USE_WORKER_THREADS
Bob Halley's avatar
base  
Bob Halley committed
320 321 322 323 324 325 326 327 328
	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);
	}
329
#endif /* USE_WORKER_THREADS */
Bob Halley's avatar
base  
Bob Halley committed
330
	UNLOCK(&manager->lock);
331

Brian Wellington's avatar
Brian Wellington committed
332
	DESTROYLOCK(&task->lock);
333 334
	task->common.impmagic = 0;
	task->common.magic = 0;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
335
	isc_mem_put(manager->mctx, task, sizeof(*task));
Bob Halley's avatar
base  
Bob Halley committed
336 337
}

338 339 340
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
341
{
Automatic Updater's avatar
Automatic Updater committed
342
	isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0;
343
	isc__task_t *task;
344
	isc_boolean_t exiting;
345
	isc_result_t result;
Bob Halley's avatar
base  
Bob Halley committed
346 347 348 349

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

Andreas Gustafsson's avatar
Andreas Gustafsson committed
350
	task = isc_mem_get(manager->mctx, sizeof(*task));
Bob Halley's avatar
base  
Bob Halley committed
351
	if (task == NULL)
Bob Halley's avatar
Bob Halley committed
352
		return (ISC_R_NOMEMORY);
353
	XTRACE("isc_task_create");
Bob Halley's avatar
base  
Bob Halley committed
354
	task->manager = manager;
355 356
	result = isc_mutex_init(&task->lock);
	if (result != ISC_R_SUCCESS) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
357
		isc_mem_put(manager->mctx, task, sizeof(*task));
358
		return (result);
Bob Halley's avatar
update  
Bob Halley committed
359
	}
Bob Halley's avatar
base  
Bob Halley committed
360 361 362
	task->state = task_state_idle;
	task->references = 1;
	INIT_LIST(task->events);
363
	INIT_LIST(task->on_shutdown);
364
	task->nevents = 0;
Bob Halley's avatar
base  
Bob Halley committed
365
	task->quantum = quantum;
Bob Halley's avatar
Bob Halley committed
366
	task->flags = 0;
367
	task->now = 0;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
368
	memset(task->name, 0, sizeof(task->name));
369
	task->tag = NULL;
Bob Halley's avatar
base  
Bob Halley committed
370 371
	INIT_LINK(task, link);
	INIT_LINK(task, ready_link);
372
	INIT_LINK(task, ready_priority_link);
Bob Halley's avatar
base  
Bob Halley committed
373

374
	exiting = ISC_FALSE;
Bob Halley's avatar
base  
Bob Halley committed
375
	LOCK(&manager->lock);
376 377 378 379 380 381
	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
382 383
	UNLOCK(&manager->lock);

384
	if (exiting) {
Brian Wellington's avatar
Brian Wellington committed
385
		DESTROYLOCK(&task->lock);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
386
		isc_mem_put(manager->mctx, task, sizeof(*task));
387 388 389
		return (ISC_R_SHUTTINGDOWN);
	}

390 391 392 393
	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
394

Bob Halley's avatar
Bob Halley committed
395
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
396 397
}

398 399 400
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
401

402 403 404
	/*
	 * Attach *targetp to source.
	 */
Bob Halley's avatar
base  
Bob Halley committed
405

406 407
	REQUIRE(VALID_TASK(source));
	REQUIRE(targetp != NULL && *targetp == NULL);
Bob Halley's avatar
base  
Bob Halley committed
408

409
	XTTRACE(source, "isc_task_attach");
410

411 412 413 414
	LOCK(&source->lock);
	source->references++;
	UNLOCK(&source->lock);

415
	*targetp = (isc_task_t *)source;
Bob Halley's avatar
base  
Bob Halley committed
416 417
}

418
static inline isc_boolean_t
419
task_shutdown(isc__task_t *task) {
420 421
	isc_boolean_t was_idle = ISC_FALSE;
	isc_event_t *event, *prev;
422

423 424 425 426 427 428 429
	/*
	 * Caller must be holding the task's lock.
	 */

	XTRACE("task_shutdown");

	if (! TASK_SHUTTINGDOWN(task)) {
430 431
		XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
				      ISC_MSG_SHUTTINGDOWN, "shutting down"));
432 433 434 435 436 437 438 439
		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);
440

441 442 443 444 445 446
		/*
		 * Note that we post shutdown events LIFO.
		 */
		for (event = TAIL(task->on_shutdown);
		     event != NULL;
		     event = prev) {
447 448 449
			prev = PREV(event, ev_link);
			DEQUEUE(task->on_shutdown, event, ev_link);
			ENQUEUE(task->events, event, ev_link);
450
			task->nevents++;
451 452 453 454 455 456
		}
	}

	return (was_idle);
}

457 458 459 460 461
/*
 * Moves a task onto the appropriate run queue.
 *
 * Caller must NOT hold manager lock.
 */
462
static inline void
463 464
task_ready(isc__task_t *task) {
	isc__taskmgr_t *manager = task->manager;
465 466 467
#ifdef USE_WORKER_THREADS
	isc_boolean_t has_privilege = isc__task_privilege((isc_task_t *) task);
#endif /* USE_WORKER_THREADS */
468 469 470 471 472 473 474

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

	XTRACE("task_ready");

	LOCK(&manager->lock);
475
	push_readyq(manager, task);
476
#ifdef USE_WORKER_THREADS
477 478
	if (manager->mode == isc_taskmgrmode_normal || has_privilege)
		SIGNAL(&manager->work_available);
479
#endif /* USE_WORKER_THREADS */
480 481 482
	UNLOCK(&manager->lock);
}

Bob Halley's avatar
Bob Halley committed
483
static inline isc_boolean_t
484
task_detach(isc__task_t *task) {
485 486 487 488 489 490 491 492 493 494

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

	REQUIRE(task->references > 0);

	XTRACE("detach");

	task->references--;
Bob Halley's avatar
Bob Halley committed
495 496 497 498 499 500 501
	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
502 503
		 * 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
504 505 506
		 */
		task->state = task_state_ready;
		return (ISC_TRUE);
507 508
	}

Bob Halley's avatar
Bob Halley committed
509
	return (ISC_FALSE);
510 511
}

512 513 514
ISC_TASKFUNC_SCOPE void
isc__task_detach(isc_task_t **taskp) {
	isc__task_t *task;
Bob Halley's avatar
Bob Halley committed
515
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
516

517 518 519 520
	/*
	 * Detach *taskp from its task.
	 */

Bob Halley's avatar
base  
Bob Halley committed
521
	REQUIRE(taskp != NULL);
522
	task = (isc__task_t *)*taskp;
Bob Halley's avatar
base  
Bob Halley committed
523 524
	REQUIRE(VALID_TASK(task));

525 526
	XTRACE("isc_task_detach");

Bob Halley's avatar
base  
Bob Halley committed
527
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
528
	was_idle = task_detach(task);
Bob Halley's avatar
base  
Bob Halley committed
529 530
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
531
	if (was_idle)
532
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
533 534 535 536

	*taskp = NULL;
}

Bob Halley's avatar
Bob Halley committed
537
static inline isc_boolean_t
538
task_send(isc__task_t *task, isc_event_t **eventp) {
Bob Halley's avatar
Bob Halley committed
539
	isc_boolean_t was_idle = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
540
	isc_event_t *event;
541

542
	/*
543
	 * Caller must be holding the task lock.
544 545
	 */

Bob Halley's avatar
update  
Bob Halley committed
546 547
	REQUIRE(eventp != NULL);
	event = *eventp;
Bob Halley's avatar
base  
Bob Halley committed
548
	REQUIRE(event != NULL);
549
	REQUIRE(event->ev_type > 0);
Bob Halley's avatar
Bob Halley committed
550
	REQUIRE(task->state != task_state_done);
Bob Halley's avatar
base  
Bob Halley committed
551

552 553
	XTRACE("task_send");

Bob Halley's avatar
Bob Halley committed
554 555 556 557
	if (task->state == task_state_idle) {
		was_idle = ISC_TRUE;
		INSIST(EMPTY(task->events));
		task->state = task_state_ready;
558
	}
Bob Halley's avatar
Bob Halley committed
559 560
	INSIST(task->state == task_state_ready ||
	       task->state == task_state_running);
561
	ENQUEUE(task->events, event, ev_link);
562
	task->nevents++;
Bob Halley's avatar
Bob Halley committed
563
	*eventp = NULL;
564

Bob Halley's avatar
Bob Halley committed
565
	return (was_idle);
566
}
567

568 569 570
ISC_TASKFUNC_SCOPE void
isc__task_send(isc_task_t *task0, isc_event_t **eventp) {
	isc__task_t *task = (isc__task_t *)task0;
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
	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
587
	was_idle = task_send(task, eventp);
Bob Halley's avatar
base  
Bob Halley committed
588 589 590 591 592 593
	UNLOCK(&task->lock);

	if (was_idle) {
		/*
		 * We need to add this task to the ready queue.
		 *
594 595 596
		 * 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
597 598
		 *
		 * We've changed the state to ready, so no one else will
599 600 601
		 * 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,
602
		 * removed, or a shutdown is started in the interval
603 604
		 * 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
605
		 */
606
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
607
	}
608 609
}

610 611
ISC_TASKFUNC_SCOPE void
isc__task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
Bob Halley's avatar
Bob Halley committed
612
	isc_boolean_t idle1, idle2;
613
	isc__task_t *task;
614 615 616 617 618 619 620

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

	REQUIRE(taskp != NULL);
621
	task = (isc__task_t *)*taskp;
622 623 624
	REQUIRE(VALID_TASK(task));

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

626
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
627 628
	idle1 = task_send(task, eventp);
	idle2 = task_detach(task);
629 630
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
631 632 633 634 635 636
	/*
	 * 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));
637

Bob Halley's avatar
Bob Halley committed
638 639
	if (idle1 || idle2)
		task_ready(task);
640 641

	*taskp = NULL;
Bob Halley's avatar
base  
Bob Halley committed
642 643
}

644
#define PURGE_OK(event)	(((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0)
645 646

static unsigned int
647
dequeue_events(isc__task_t *task, void *sender, isc_eventtype_t first,
648 649
	       isc_eventtype_t last, void *tag,
	       isc_eventlist_t *events, isc_boolean_t purging)
650
{
Bob Halley's avatar
Bob Halley committed
651
	isc_event_t *event, *next_event;
652
	unsigned int count = 0;
653

Bob Halley's avatar
Bob Halley committed
654
	REQUIRE(VALID_TASK(task));
655
	REQUIRE(last >= first);
Bob Halley's avatar
Bob Halley committed
656

657
	XTRACE("dequeue_events");
Bob Halley's avatar
Bob Halley committed
658

Bob Halley's avatar
Bob Halley committed
659
	/*
660 661 662
	 * 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.
663
	 *
664
	 * sender == NULL means "any sender", and tag == NULL means "any tag".
Bob Halley's avatar
Bob Halley committed
665 666 667
	 */

	LOCK(&task->lock);
668 669

	for (event = HEAD(task->events); event != NULL; event = next_event) {
670 671 672 673
		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) &&
674
		    (!purging || PURGE_OK(event))) {
675
			DEQUEUE(task->events, event, ev_link);
676
			task->nevents--;
677
			ENQUEUE(*events, event, ev_link);
678
			count++;
Bob Halley's avatar
Bob Halley committed
679 680
		}
	}
681

Bob Halley's avatar
Bob Halley committed
682 683
	UNLOCK(&task->lock);

684 685 686
	return (count);
}

687 688 689
ISC_TASKFUNC_SCOPE unsigned int
isc__task_purgerange(isc_task_t *task0, void *sender, isc_eventtype_t first,
		     isc_eventtype_t last, void *tag)
690
{
691
	isc__task_t *task = (isc__task_t *)task0;
692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
	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) {
708
		next_event = NEXT(event, ev_link);
Bob Halley's avatar
Bob Halley committed
709
		isc_event_free(&event);
Bob Halley's avatar
Bob Halley committed
710
	}
711

712 713 714 715 716
	/*
	 * Note that purging never changes the state of the task.
	 */

	return (count);
Bob Halley's avatar
Bob Halley committed
717 718
}

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

727 728
	XTRACE("isc_task_purge");

729
	return (isc__task_purgerange(task, sender, type, type, tag));
730 731
}

732 733 734
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
735 736 737 738
	isc_event_t *curr_event, *next_event;

	/*
	 * Purge 'event' from a task's event queue.
739 740
	 *
	 * XXXRTH:  WARNING:  This method may be removed before beta.
Bob Halley's avatar
Bob Halley committed
741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
	 */

	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) {
759
		next_event = NEXT(curr_event, ev_link);
760
		if (curr_event == event && PURGE_OK(event)) {
761
			DEQUEUE(task->events, curr_event, ev_link);
762
			task->nevents--;
Bob Halley's avatar
Bob Halley committed
763 764 765 766 767 768 769 770 771 772 773 774 775
			break;
		}
	}
	UNLOCK(&task->lock);

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

776 777 778 779
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)
780 781 782 783 784 785 786
{
	/*
	 * Remove events from a task's event queue.
	 */

	XTRACE("isc_task_unsendrange");

787 788
	return (dequeue_events((isc__task_t *)task, sender, first,
			       last, tag, events, ISC_FALSE));
789 790
}

791 792 793
ISC_TASKFUNC_SCOPE unsigned int
isc__task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type,
		 void *tag, isc_eventlist_t *events)
794 795 796 797 798 799 800
{
	/*
	 * Remove events from a task's event queue.
	 */

	XTRACE("isc_task_unsend");

801 802
	return (dequeue_events((isc__task_t *)task, sender, type,
			       type, tag, events, ISC_FALSE));
803 804
}

805 806 807
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
808
{
809
	isc__task_t *task = (isc__task_t *)task0;
810 811 812 813
	isc_boolean_t disallowed = ISC_FALSE;
	isc_result_t result = ISC_R_SUCCESS;
	isc_event_t *event;

814 815 816 817 818
	/*
	 * Send a shutdown event with action 'action' and argument 'arg' when
	 * 'task' is shutdown.
	 */

819
	REQUIRE(VALID_TASK(task));
820
	REQUIRE(action != NULL);
821

822
	event = isc_event_allocate(task->manager->mctx,
823 824 825 826
				   NULL,
				   ISC_TASKEVENT_SHUTDOWN,
				   action,
				   arg,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
827
				   sizeof(*event));
828 829 830 831
	if (event == NULL)
		return (ISC_R_NOMEMORY);

	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
832
	if (TASK_SHUTTINGDOWN(task)) {
833
		disallowed = ISC_TRUE;
834
		result = ISC_R_SHUTTINGDOWN;
835
	} else
836
		ENQUEUE(task->on_shutdown, event, ev_link);
837 838 839
	UNLOCK(&task->lock);

	if (disallowed)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
840
		isc_mem_put(task->manager->mctx, event, sizeof(*event));
841 842 843 844

	return (result);
}

845 846 847
ISC_TASKFUNC_SCOPE void
isc__task_shutdown(isc_task_t *task0) {
	isc__task_t *task = (isc__task_t *)task0;
848
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
849

850 851 852 853
	/*
	 * Shutdown 'task'.
	 */

Bob Halley's avatar
base  
Bob Halley committed
854 855 856
	REQUIRE(VALID_TASK(task));

	LOCK(&task->lock);
857
	was_idle = task_shutdown(task);
Bob Halley's avatar
base  
Bob Halley committed
858 859
	UNLOCK(&task->lock);

860 861
	if (was_idle)
		task_ready(task);
Bob Halley's avatar
Bob Halley committed
862
}
Bob Halley's avatar
base  
Bob Halley committed
863

864 865
ISC_TASKFUNC_SCOPE void
isc__task_destroy(isc_task_t **taskp) {
Bob Halley's avatar
Bob Halley committed
866

867 868 869 870
	/*
	 * Destroy '*taskp'.
	 */

Bob Halley's avatar
Bob Halley committed
871 872
	REQUIRE(taskp != NULL);

Bob Halley's avatar
Bob Halley committed
873 874
	isc_task_shutdown(*taskp);
	isc_task_detach(taskp);
Bob Halley's avatar
base  
Bob Halley committed
875 876
}

877 878 879
ISC_TASKFUNC_SCOPE void
isc__task_setname(isc_task_t *task0, const char *name, void *tag) {
	isc__task_t *task = (isc__task_t *)task0;
880 881 882 883 884 885 886 887 888 889 890 891 892

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

894 895 896 897 898 899
ISC_TASKFUNC_SCOPE const char *
isc__task_getname(isc_task_t *task0) {
	isc__task_t *task = (isc__task_t *)task0;

	REQUIRE(VALID_TASK(task));

900 901 902
	return (task->name);
}

903 904 905 906 907 908
ISC_TASKFUNC_SCOPE void *
isc__task_gettag(isc_task_t *task0) {
	isc__task_t *task = (isc__task_t *)task0;

	REQUIRE(VALID_TASK(task));

909 910 911
	return (task->tag);
}

912 913 914 915
ISC_TASKFUNC_SCOPE void
isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t) {
	isc__task_t *task = (isc__task_t *)task0;

916 917 918 919 920 921 922
	REQUIRE(VALID_TASK(task));
	REQUIRE(t != NULL);

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