task.c 50.5 KB
Newer Older
Bob Halley's avatar
Bob Halley committed
1
/*
Tinderbox User's avatar
Tinderbox User committed
2
 * Copyright (C) 2004-2013  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/json.h>
34
#include <isc/magic.h>
35
#include <isc/mem.h>
36
#include <isc/msgs.h>
Brian Wellington's avatar
Brian Wellington committed
37
#include <isc/platform.h>
38
#include <isc/string.h>
Bob Halley's avatar
Bob Halley committed
39
#include <isc/task.h>
40
#include <isc/thread.h>
Michael Graff's avatar
Michael Graff committed
41
#include <isc/util.h>
42
#include <isc/xml.h>
Bob Halley's avatar
base  
Bob Halley committed
43

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

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

68 69
#include "task_p.h"

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

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

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

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

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

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

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

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

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

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

135 136 137
typedef ISC_LIST(isc__task_t)	isc__tasklist_t;

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

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

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

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

static struct isc__taskmethods {
	isc_taskmethods_t methods;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

410
	XTTRACE(source, "isc_task_attach");
411

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

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

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

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

	XTRACE("task_shutdown");

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

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

	return (was_idle);
}

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

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

	XTRACE("task_ready");

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

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

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

	REQUIRE(task->references > 0);

	XTRACE("detach");

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

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

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

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

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

526 527
	XTRACE("isc_task_detach");

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

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

	*taskp = NULL;
}

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

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

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

553 554
	XTRACE("task_send");

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	LOCK(&task->lock);
669 670

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

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

685 686 687
	return (count);
}

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

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

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

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

728 729
	XTRACE("isc_task_purge");

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

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

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

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

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

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

	XTRACE("isc_task_unsendrange");

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

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

	XTRACE("isc_task_unsend");

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

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

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

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

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

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

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

	return (result);
}

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

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

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

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

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

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

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

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

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

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

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

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

	REQUIRE(VALID_TASK(task));

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

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

	REQUIRE(VALID_TASK(task));

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

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

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

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