task.c 55.4 KB
Newer Older
Bob Halley's avatar
Bob Halley committed
1
/*
Tinderbox User's avatar
Tinderbox User committed
2
 * Copyright (C) 2004-2014  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/string.h>
Bob Halley's avatar
Bob Halley committed
41
#include <isc/task.h>
42
#include <isc/thread.h>
Michael Graff's avatar
Michael Graff committed
43
#include <isc/util.h>
44
#include <isc/xml.h>
Bob Halley's avatar
base  
Bob Halley committed
45

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

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 ISC_PLATFORM_USETHREADS
#define USE_WORKER_THREADS
#else
#define USE_SHARED_MANAGER
#endif	/* ISC_PLATFORM_USETHREADS */

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(HAVE_JSON)
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
#ifdef USE_SHARED_MANAGER
static isc__taskmgr_t *taskmgr = NULL;
#endif /* USE_SHARED_MANAGER */

/*%
177 178 179
 * The following are intended for internal use (indicated by "isc__"
 * prefix) but are not declared as static, allowing direct access from
 * unit tests etc.
180 181
 */

182
isc_result_t
183 184
isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum,
		 isc_task_t **taskp);
185
void
186
isc__task_attach(isc_task_t *source0, isc_task_t **targetp);
187
void
188
isc__task_detach(isc_task_t **taskp);
189
void
190
isc__task_send(isc_task_t *task0, isc_event_t **eventp);
191
void
192
isc__task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp);
193
unsigned int
194 195
isc__task_purgerange(isc_task_t *task0, void *sender, isc_eventtype_t first,
		     isc_eventtype_t last, void *tag);
196
unsigned int
197 198
isc__task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
		void *tag);
199 200 201
isc_boolean_t
isc_task_purgeevent(isc_task_t *task0, isc_event_t *event);
unsigned int
202 203 204
isc__task_unsendrange(isc_task_t *task, void *sender, isc_eventtype_t first,
		      isc_eventtype_t last, void *tag,
		      isc_eventlist_t *events);
205
unsigned int
206 207
isc__task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type,
		 void *tag, isc_eventlist_t *events);
208
isc_result_t
209 210
isc__task_onshutdown(isc_task_t *task0, isc_taskaction_t action,
		     const void *arg);
211
void
212
isc__task_shutdown(isc_task_t *task0);
213
void
214
isc__task_destroy(isc_task_t **taskp);
215
void
216
isc__task_setname(isc_task_t *task0, const char *name, void *tag);
217
const char *
218
isc__task_getname(isc_task_t *task0);
219
void *
220
isc__task_gettag(isc_task_t *task0);
221
void
222
isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t);
223
isc_result_t
224 225
isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers,
		    unsigned int default_quantum, isc_taskmgr_t **managerp);
226
void
227
isc__taskmgr_destroy(isc_taskmgr_t **managerp);
228 229 230 231 232
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
233
isc__task_beginexclusive(isc_task_t *task);
234
void
235
isc__task_endexclusive(isc_task_t *task0);
236
void
237
isc__task_setprivilege(isc_task_t *task0, isc_boolean_t priv);
238
isc_boolean_t
239
isc__task_privilege(isc_task_t *task0);
240
void
241
isc__taskmgr_setmode(isc_taskmgr_t *manager0, isc_taskmgrmode_t mode);
242
isc_taskmgrmode_t
243 244
isc__taskmgr_mode(isc_taskmgr_t *manager0);

Evan Hunt's avatar
Evan Hunt committed
245
static inline isc_boolean_t
246 247
empty_readyq(isc__taskmgr_t *manager);

Evan Hunt's avatar
Evan Hunt committed
248
static inline isc__task_t *
249 250
pop_readyq(isc__taskmgr_t *manager);

251
static inline void
252
push_readyq(isc__taskmgr_t *manager, isc__task_t *task);
253 254 255 256 257 258 259

static struct isc__taskmethods {
	isc_taskmethods_t methods;

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

static isc_taskmgrmethods_t taskmgrmethods = {
	isc__taskmgr_destroy,
288 289
	isc__taskmgr_setmode,
	isc__taskmgr_mode,
290
	isc__task_create,
291 292
	isc_taskmgr_setexcltask,
	isc_taskmgr_excltask
293
};
294

Bob Halley's avatar
base  
Bob Halley committed
295 296 297 298 299
/***
 *** Tasks.
 ***/

static void
300 301
task_finished(isc__task_t *task) {
	isc__taskmgr_t *manager = task->manager;
Bob Halley's avatar
base  
Bob Halley committed
302 303

	REQUIRE(EMPTY(task->events));
304
	REQUIRE(task->nevents == 0);
305
	REQUIRE(EMPTY(task->on_shutdown));
306 307 308 309
	REQUIRE(task->references == 0);
	REQUIRE(task->state == task_state_done);

	XTRACE("task_finished");
Bob Halley's avatar
base  
Bob Halley committed
310 311 312

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

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

332
isc_result_t
333 334
isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum,
		 isc_task_t **taskp)
Bob Halley's avatar
Bob Halley committed
335
{
Automatic Updater's avatar
Automatic Updater committed
336
	isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0;
337
	isc__task_t *task;
338
	isc_boolean_t exiting;
339
	isc_result_t result;
Bob Halley's avatar
base  
Bob Halley committed
340 341 342 343

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

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

368
	exiting = ISC_FALSE;
Bob Halley's avatar
base  
Bob Halley committed
369
	LOCK(&manager->lock);
370 371 372 373 374 375
	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
376 377
	UNLOCK(&manager->lock);

378
	if (exiting) {
Brian Wellington's avatar
Brian Wellington committed
379
		DESTROYLOCK(&task->lock);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
380
		isc_mem_put(manager->mctx, task, sizeof(*task));
381 382 383
		return (ISC_R_SHUTTINGDOWN);
	}

384 385 386 387
	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
388

Bob Halley's avatar
Bob Halley committed
389
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
390 391
}

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

396 397 398
	/*
	 * Attach *targetp to source.
	 */
Bob Halley's avatar
base  
Bob Halley committed
399

400 401
	REQUIRE(VALID_TASK(source));
	REQUIRE(targetp != NULL && *targetp == NULL);
Bob Halley's avatar
base  
Bob Halley committed
402

403
	XTTRACE(source, "isc_task_attach");
404

405 406 407 408
	LOCK(&source->lock);
	source->references++;
	UNLOCK(&source->lock);

409
	*targetp = (isc_task_t *)source;
Bob Halley's avatar
base  
Bob Halley committed
410 411
}

412
static inline isc_boolean_t
413
task_shutdown(isc__task_t *task) {
414 415
	isc_boolean_t was_idle = ISC_FALSE;
	isc_event_t *event, *prev;
416

417 418 419 420 421 422 423
	/*
	 * Caller must be holding the task's lock.
	 */

	XTRACE("task_shutdown");

	if (! TASK_SHUTTINGDOWN(task)) {
424 425
		XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
				      ISC_MSG_SHUTTINGDOWN, "shutting down"));
426 427 428 429 430 431 432 433
		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);
434

435 436 437 438 439 440
		/*
		 * Note that we post shutdown events LIFO.
		 */
		for (event = TAIL(task->on_shutdown);
		     event != NULL;
		     event = prev) {
441 442 443
			prev = PREV(event, ev_link);
			DEQUEUE(task->on_shutdown, event, ev_link);
			ENQUEUE(task->events, event, ev_link);
444
			task->nevents++;
445 446 447 448 449 450
		}
	}

	return (was_idle);
}

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

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

	XTRACE("task_ready");

	LOCK(&manager->lock);
469
	push_readyq(manager, task);
470
#ifdef USE_WORKER_THREADS
471 472
	if (manager->mode == isc_taskmgrmode_normal || has_privilege)
		SIGNAL(&manager->work_available);
473
#endif /* USE_WORKER_THREADS */
474 475 476
	UNLOCK(&manager->lock);
}

Bob Halley's avatar
Bob Halley committed
477
static inline isc_boolean_t
478
task_detach(isc__task_t *task) {
479 480 481 482 483 484 485 486 487 488

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

	REQUIRE(task->references > 0);

	XTRACE("detach");

	task->references--;
Bob Halley's avatar
Bob Halley committed
489 490 491 492 493 494 495
	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
496 497
		 * 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
498 499 500
		 */
		task->state = task_state_ready;
		return (ISC_TRUE);
501 502
	}

Bob Halley's avatar
Bob Halley committed
503
	return (ISC_FALSE);
504 505
}

506
void
507 508
isc__task_detach(isc_task_t **taskp) {
	isc__task_t *task;
Bob Halley's avatar
Bob Halley committed
509
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
510

511 512 513 514
	/*
	 * Detach *taskp from its task.
	 */

Bob Halley's avatar
base  
Bob Halley committed
515
	REQUIRE(taskp != NULL);
516
	task = (isc__task_t *)*taskp;
Bob Halley's avatar
base  
Bob Halley committed
517 518
	REQUIRE(VALID_TASK(task));

519 520
	XTRACE("isc_task_detach");

Bob Halley's avatar
base  
Bob Halley committed
521
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
522
	was_idle = task_detach(task);
Bob Halley's avatar
base  
Bob Halley committed
523 524
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
525
	if (was_idle)
526
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
527 528 529 530

	*taskp = NULL;
}

Bob Halley's avatar
Bob Halley committed
531
static inline isc_boolean_t
532
task_send(isc__task_t *task, isc_event_t **eventp) {
Bob Halley's avatar
Bob Halley committed
533
	isc_boolean_t was_idle = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
534
	isc_event_t *event;
535

536
	/*
537
	 * Caller must be holding the task lock.
538 539
	 */

Bob Halley's avatar
update  
Bob Halley committed
540 541
	REQUIRE(eventp != NULL);
	event = *eventp;
Bob Halley's avatar
base  
Bob Halley committed
542
	REQUIRE(event != NULL);
543
	REQUIRE(event->ev_type > 0);
Bob Halley's avatar
Bob Halley committed
544
	REQUIRE(task->state != task_state_done);
Bob Halley's avatar
base  
Bob Halley committed
545

546 547
	XTRACE("task_send");

Bob Halley's avatar
Bob Halley committed
548 549 550 551
	if (task->state == task_state_idle) {
		was_idle = ISC_TRUE;
		INSIST(EMPTY(task->events));
		task->state = task_state_ready;
552
	}
Bob Halley's avatar
Bob Halley committed
553 554
	INSIST(task->state == task_state_ready ||
	       task->state == task_state_running);
555
	ENQUEUE(task->events, event, ev_link);
556
	task->nevents++;
Bob Halley's avatar
Bob Halley committed
557
	*eventp = NULL;
558

Bob Halley's avatar
Bob Halley committed
559
	return (was_idle);
560
}
561

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

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

604
void
605
isc__task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
Bob Halley's avatar
Bob Halley committed
606
	isc_boolean_t idle1, idle2;
607
	isc__task_t *task;
608 609 610 611 612 613 614

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

	REQUIRE(taskp != NULL);
615
	task = (isc__task_t *)*taskp;
616 617 618
	REQUIRE(VALID_TASK(task));

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

620
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
621 622
	idle1 = task_send(task, eventp);
	idle2 = task_detach(task);
623 624
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
625 626 627 628 629 630
	/*
	 * 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));
631

Bob Halley's avatar
Bob Halley committed
632 633
	if (idle1 || idle2)
		task_ready(task);
634 635

	*taskp = NULL;
Bob Halley's avatar
base  
Bob Halley committed
636 637
}

638
#define PURGE_OK(event)	(((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0)
639 640

static unsigned int
641
dequeue_events(isc__task_t *task, void *sender, isc_eventtype_t first,
642 643
	       isc_eventtype_t last, void *tag,
	       isc_eventlist_t *events, isc_boolean_t purging)
644
{
Bob Halley's avatar
Bob Halley committed
645
	isc_event_t *event, *next_event;
646
	unsigned int count = 0;
647

Bob Halley's avatar
Bob Halley committed
648
	REQUIRE(VALID_TASK(task));
649
	REQUIRE(last >= first);
Bob Halley's avatar
Bob Halley committed
650

651
	XTRACE("dequeue_events");
Bob Halley's avatar
Bob Halley committed
652

Bob Halley's avatar
Bob Halley committed
653
	/*
654 655 656
	 * 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.
657
	 *
658
	 * sender == NULL means "any sender", and tag == NULL means "any tag".
Bob Halley's avatar
Bob Halley committed
659 660 661
	 */

	LOCK(&task->lock);
662 663

	for (event = HEAD(task->events); event != NULL; event = next_event) {
664 665 666 667
		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) &&
668
		    (!purging || PURGE_OK(event))) {
669
			DEQUEUE(task->events, event, ev_link);
670
			task->nevents--;
671
			ENQUEUE(*events, event, ev_link);
672
			count++;
Bob Halley's avatar
Bob Halley committed
673 674
		}
	}
675

Bob Halley's avatar
Bob Halley committed
676 677
	UNLOCK(&task->lock);

678 679 680
	return (count);
}

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

706 707 708 709 710
	/*
	 * Note that purging never changes the state of the task.
	 */

	return (count);
Bob Halley's avatar
Bob Halley committed
711 712
}

713
unsigned int
714 715
isc__task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
		void *tag)
Bob Halley's avatar
Bob Halley committed
716
{
717 718 719 720
	/*
	 * Purge events from a task's event queue.
	 */

721 722
	XTRACE("isc_task_purge");

723
	return (isc__task_purgerange(task, sender, type, type, tag));
724 725
}

726 727
isc_boolean_t
isc_task_purgeevent(isc_task_t *task0, isc_event_t *event) {
728
	isc__task_t *task = (isc__task_t *)task0;
Bob Halley's avatar
Bob Halley committed
729 730 731 732
	isc_event_t *curr_event, *next_event;

	/*
	 * Purge 'event' from a task's event queue.
733 734
	 *
	 * XXXRTH:  WARNING:  This method may be removed before beta.
Bob Halley's avatar
Bob Halley committed
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752
	 */

	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) {
753
		next_event = NEXT(curr_event, ev_link);
754
		if (curr_event == event && PURGE_OK(event)) {
755
			DEQUEUE(task->events, curr_event, ev_link);
756
			task->nevents--;
Bob Halley's avatar
Bob Halley committed
757 758 759 760 761 762 763 764 765 766 767 768 769
			break;
		}
	}
	UNLOCK(&task->lock);

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

770
unsigned int
771 772 773
isc__task_unsendrange(isc_task_t *task, void *sender, isc_eventtype_t first,
		      isc_eventtype_t last, void *tag,
		      isc_eventlist_t *events)
774 775 776 777 778 779 780
{
	/*
	 * Remove events from a task's event queue.
	 */

	XTRACE("isc_task_unsendrange");

781 782
	return (dequeue_events((isc__task_t *)task, sender, first,
			       last, tag, events, ISC_FALSE));
783 784
}

785
unsigned int
786 787
isc__task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type,
		 void *tag, isc_eventlist_t *events)
788 789 790 791 792 793 794
{
	/*
	 * Remove events from a task's event queue.
	 */

	XTRACE("isc_task_unsend");

795 796
	return (dequeue_events((isc__task_t *)task, sender, type,
			       type, tag, events, ISC_FALSE));
797 798
}

799
isc_result_t
800 801
isc__task_onshutdown(isc_task_t *task0, isc_taskaction_t action,
		     const void *arg)
David Lawrence's avatar
David Lawrence committed
802
{
803
	isc__task_t *task = (isc__task_t *)task0;
804 805 806 807
	isc_boolean_t disallowed = ISC_FALSE;
	isc_result_t result = ISC_R_SUCCESS;
	isc_event_t *event;

808 809 810 811 812
	/*
	 * Send a shutdown event with action 'action' and argument 'arg' when
	 * 'task' is shutdown.
	 */

813
	REQUIRE(VALID_TASK(task));
814
	REQUIRE(action != NULL);
815

816
	event = isc_event_allocate(task->manager->mctx,
817 818 819 820
				   NULL,
				   ISC_TASKEVENT_SHUTDOWN,
				   action,
				   arg,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
821
				   sizeof(*event));
822 823 824 825
	if (event == NULL)
		return (ISC_R_NOMEMORY);

	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
826
	if (TASK_SHUTTINGDOWN(task)) {
827
		disallowed = ISC_TRUE;