task.c 56 KB
Newer Older
Bob Halley's avatar
Bob Halley committed
1
/*
2
 * Copyright (C) 2004-2015  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/app.h>
32
#include <isc/condition.h>
33
#include <isc/event.h>
34
#include <isc/json.h>
35
#include <isc/magic.h>
36
#include <isc/mem.h>
37
#include <isc/msgs.h>
38
#include <isc/once.h>
Brian Wellington's avatar
Brian Wellington committed
39
#include <isc/platform.h>
40
#include <isc/print.h>
41
#include <isc/string.h>
Bob Halley's avatar
Bob Halley committed
42
#include <isc/task.h>
43
#include <isc/thread.h>
Michael Graff's avatar
Michael Graff committed
44
#include <isc/util.h>
45
#include <isc/xml.h>
Bob Halley's avatar
base  
Bob Halley committed
46

Francis Dupont's avatar
Francis Dupont committed
47 48 49 50
#ifdef OPENSSL_LEAKS
#include <openssl/err.h>
#endif

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
/*%
 * 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 ISC_PLATFORM_USETHREADS
#define USE_WORKER_THREADS
#else
#define USE_SHARED_MANAGER
#endif	/* ISC_PLATFORM_USETHREADS */

69 70
#include "task_p.h"

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

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

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

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

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

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

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

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

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

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

136 137 138
typedef ISC_LIST(isc__task_t)	isc__tasklist_t;

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

	/*
	 * Multiple threads can read/write 'excl' at the same time, so we need
	 * to protect the access.  We can't use 'lock' since isc_task_detach()
	 * will try to acquire it.
	 */
	isc_mutex_t			excl_lock;
170
	isc__task_t			*excl;
171
#ifdef USE_SHARED_MANAGER
172
	unsigned int			refs;
173
#endif /* ISC_PLATFORM_USETHREADS */
Bob Halley's avatar
Bob Halley committed
174
};
Bob Halley's avatar
Bob Halley committed
175

176
#define DEFAULT_TASKMGR_QUANTUM		10
Bob Halley's avatar
Bob Halley committed
177 178
#define DEFAULT_DEFAULT_QUANTUM		5
#define FINISHED(m)			((m)->exiting && EMPTY((m)->tasks))
Bob Halley's avatar
Bob Halley committed
179

180 181 182 183 184
#ifdef USE_SHARED_MANAGER
static isc__taskmgr_t *taskmgr = NULL;
#endif /* USE_SHARED_MANAGER */

/*%
185 186 187
 * The following are intended for internal use (indicated by "isc__"
 * prefix) but are not declared as static, allowing direct access from
 * unit tests etc.
188 189
 */

190
isc_result_t
191 192
isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum,
		 isc_task_t **taskp);
193
void
194
isc__task_attach(isc_task_t *source0, isc_task_t **targetp);
195
void
196
isc__task_detach(isc_task_t **taskp);
197
void
198
isc__task_send(isc_task_t *task0, isc_event_t **eventp);
199
void
200
isc__task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp);
201
unsigned int
202 203
isc__task_purgerange(isc_task_t *task0, void *sender, isc_eventtype_t first,
		     isc_eventtype_t last, void *tag);
204
unsigned int
205 206
isc__task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
		void *tag);
207 208 209
isc_boolean_t
isc_task_purgeevent(isc_task_t *task0, isc_event_t *event);
unsigned int
210 211 212
isc__task_unsendrange(isc_task_t *task, void *sender, isc_eventtype_t first,
		      isc_eventtype_t last, void *tag,
		      isc_eventlist_t *events);
213
unsigned int
214 215
isc__task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type,
		 void *tag, isc_eventlist_t *events);
216
isc_result_t
217
isc__task_onshutdown(isc_task_t *task0, isc_taskaction_t action,
Mark Andrews's avatar
Mark Andrews committed
218
		     void *arg);
219
void
220
isc__task_shutdown(isc_task_t *task0);
221
void
222
isc__task_destroy(isc_task_t **taskp);
223
void
224
isc__task_setname(isc_task_t *task0, const char *name, void *tag);
225
const char *
226
isc__task_getname(isc_task_t *task0);
227
void *
228
isc__task_gettag(isc_task_t *task0);
229
void
230
isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t);
231
isc_result_t
232 233
isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers,
		    unsigned int default_quantum, isc_taskmgr_t **managerp);
234
void
235
isc__taskmgr_destroy(isc_taskmgr_t **managerp);
236 237 238 239 240
void
isc_taskmgr_setexcltask(isc_taskmgr_t *mgr0, isc_task_t *task0);
isc_result_t
isc_taskmgr_excltask(isc_taskmgr_t *mgr0, isc_task_t **taskp);
isc_result_t
241
isc__task_beginexclusive(isc_task_t *task);
242
void
243
isc__task_endexclusive(isc_task_t *task0);
244
void
245
isc__task_setprivilege(isc_task_t *task0, isc_boolean_t priv);
246
isc_boolean_t
247
isc__task_privilege(isc_task_t *task0);
248
void
249
isc__taskmgr_setmode(isc_taskmgr_t *manager0, isc_taskmgrmode_t mode);
250
isc_taskmgrmode_t
251 252
isc__taskmgr_mode(isc_taskmgr_t *manager0);

Evan Hunt's avatar
Evan Hunt committed
253
static inline isc_boolean_t
254 255
empty_readyq(isc__taskmgr_t *manager);

Evan Hunt's avatar
Evan Hunt committed
256
static inline isc__task_t *
257 258
pop_readyq(isc__taskmgr_t *manager);

259
static inline void
260
push_readyq(isc__taskmgr_t *manager, isc__task_t *task);
261 262 263 264 265 266 267

static struct isc__taskmethods {
	isc_taskmethods_t methods;

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

static isc_taskmgrmethods_t taskmgrmethods = {
	isc__taskmgr_destroy,
296 297
	isc__taskmgr_setmode,
	isc__taskmgr_mode,
298
	isc__task_create,
299 300
	isc_taskmgr_setexcltask,
	isc_taskmgr_excltask
301
};
302

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

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

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

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

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

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

340
isc_result_t
341 342
isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum,
		 isc_task_t **taskp)
Bob Halley's avatar
Bob Halley committed
343
{
Automatic Updater's avatar
Automatic Updater committed
344
	isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0;
345
	isc__task_t *task;
346
	isc_boolean_t exiting;
347
	isc_result_t result;
Bob Halley's avatar
base  
Bob Halley committed
348 349 350 351

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

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

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

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

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

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

400
void
401 402
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
403

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

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

411
	XTTRACE(source, "isc_task_attach");
412

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

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

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

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

	XTRACE("task_shutdown");

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

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

	return (was_idle);
}

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

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

	XTRACE("task_ready");

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

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

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

	REQUIRE(task->references > 0);

	XTRACE("detach");

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

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

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

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

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

527 528
	XTRACE("isc_task_detach");

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

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

	*taskp = NULL;
}

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

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

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

554 555
	XTRACE("task_send");

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	LOCK(&task->lock);
670 671

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

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

686 687 688
	return (count);
}

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

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

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

721
unsigned int
722 723
isc__task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
		void *tag)
Bob Halley's avatar
Bob Halley committed
724
{
725 726 727 728
	/*
	 * Purge events from a task's event queue.
	 */

729 730
	XTRACE("isc_task_purge");

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

734 735
isc_boolean_t
isc_task_purgeevent(isc_task_t *task0, isc_event_t *event) {
736
	isc__task_t *task = (isc__task_t *)task0;
Bob Halley's avatar
Bob Halley committed
737 738 739 740
	isc_event_t *curr_event, *next_event;

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

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

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

778
unsigned int
779 780 781
isc__task_unsendrange(isc_task_t *task, void *sender, isc_eventtype_t first,
		      isc_eventtype_t last, void *tag,
		      isc_eventlist_t *events)
782 783 784 785 786 787 788
{
	/*
	 * Remove events from a task's event queue.
	 */

	XTRACE("isc_task_unsendrange");

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

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

	XTRACE("isc_task_unsend");

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

807
isc_result_t
808
isc__task_onshutdown(isc_task_t *task0, isc_taskaction_t action,
Mark Andrews's avatar
Mark Andrews committed
809
		     void *arg)
David Lawrence's avatar
David Lawrence committed
810
{
811
	isc__task_t *task = (isc__task_t *)task0;
812 813 814 815
	isc_boolean_t disallowed = ISC_FALSE;
	isc_result_t result = ISC_R_SUCCESS;
	isc_event_t *event;

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

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

824
	event = isc_event_allocate(task->manager->mctx,
825 826 827 828
				   NULL,
				   ISC_TASKEVENT_SHUTDOWN,
				   action,
				   arg,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
829
				   sizeof(