task.c 56.1 KB
Newer Older
Bob Halley's avatar
Bob Halley committed
1
/*
2
 * Copyright (C) 1998-2016  Internet Systems Consortium, Inc. ("ISC")
3
 *
4 5 6
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
Bob Halley's avatar
Bob Halley committed
7
 */
Bob Halley's avatar
base  
Bob Halley committed
8

9 10
/*! \file
 * \author Principal Author: Bob Halley
Bob Halley's avatar
Bob Halley committed
11 12
 */

13 14 15 16 17
/*
 * XXXRTH  Need to document the states a task can be in, and the rules
 * for changing states.
 */

Bob Halley's avatar
Bob Halley committed
18 19
#include <config.h>

20
#include <isc/app.h>
21
#include <isc/condition.h>
22
#include <isc/event.h>
23
#include <isc/json.h>
24
#include <isc/magic.h>
25
#include <isc/mem.h>
26
#include <isc/msgs.h>
27
#include <isc/once.h>
Brian Wellington's avatar
Brian Wellington committed
28
#include <isc/platform.h>
29
#include <isc/print.h>
30
#include <isc/string.h>
Bob Halley's avatar
Bob Halley committed
31
#include <isc/task.h>
32
#include <isc/thread.h>
Evan Hunt's avatar
Evan Hunt committed
33
#include <isc/time.h>
Michael Graff's avatar
Michael Graff committed
34
#include <isc/util.h>
35
#include <isc/xml.h>
Bob Halley's avatar
base  
Bob Halley committed
36

Francis Dupont's avatar
Francis Dupont committed
37 38 39 40
#ifdef OPENSSL_LEAKS
#include <openssl/err.h>
#endif

41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
/*%
 * 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 */

59 60
#include "task_p.h"

Bob Halley's avatar
Bob Halley committed
61
#ifdef ISC_TASK_TRACE
62
#define XTRACE(m)		fprintf(stderr, "task %p thread %lu: %s\n", \
63
				       task, isc_thread_self(), (m))
64
#define XTTRACE(t, m)		fprintf(stderr, "task %p thread %lu: %s\n", \
65
				       (t), isc_thread_self(), (m))
66
#define XTHREADTRACE(m)		fprintf(stderr, "thread %lu: %s\n", \
67
				       isc_thread_self(), (m))
Bob Halley's avatar
Bob Halley committed
68 69
#else
#define XTRACE(m)
70
#define XTTRACE(t, m)
71
#define XTHREADTRACE(m)
Bob Halley's avatar
Bob Halley committed
72
#endif
Bob Halley's avatar
base  
Bob Halley committed
73

Bob Halley's avatar
Bob Halley committed
74
/***
Bob Halley's avatar
Bob Halley committed
75
 *** Types.
Bob Halley's avatar
Bob Halley committed
76 77
 ***/

Bob Halley's avatar
Bob Halley committed
78 79
typedef enum {
	task_state_idle, task_state_ready, task_state_running,
80
	task_state_done
Bob Halley's avatar
Bob Halley committed
81 82
} task_state_t;

83
#if defined(HAVE_LIBXML2) || defined(HAVE_JSON)
84 85 86
static const char *statenames[] = {
	"idle", "ready", "running", "done",
};
Mark Andrews's avatar
Mark Andrews committed
87
#endif
88

89 90
#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
91

92 93 94 95
typedef struct isc__task isc__task_t;
typedef struct isc__taskmgr isc__taskmgr_t;

struct isc__task {
Bob Halley's avatar
Bob Halley committed
96
	/* Not locked. */
97 98
	isc_task_t			common;
	isc__taskmgr_t *		manager;
Bob Halley's avatar
Bob Halley committed
99
	isc_mutex_t			lock;
Bob Halley's avatar
Bob Halley committed
100 101 102
	/* Locked by task lock. */
	task_state_t			state;
	unsigned int			references;
Bob Halley's avatar
Bob Halley committed
103
	isc_eventlist_t			events;
104
	isc_eventlist_t			on_shutdown;
105
	unsigned int			nevents;
Bob Halley's avatar
Bob Halley committed
106
	unsigned int			quantum;
107
	unsigned int			flags;
108
	isc_stdtime_t			now;
Evan Hunt's avatar
Evan Hunt committed
109
	isc_time_t			tnow;
110 111
	char				name[16];
	void *				tag;
Bob Halley's avatar
Bob Halley committed
112
	/* Locked by task manager lock. */
113 114
	LINK(isc__task_t)		link;
	LINK(isc__task_t)		ready_link;
115
	LINK(isc__task_t)		ready_priority_link;
Bob Halley's avatar
Bob Halley committed
116 117
};

Bob Halley's avatar
Bob Halley committed
118
#define TASK_F_SHUTTINGDOWN		0x01
119
#define TASK_F_PRIVILEGED		0x02
120

121 122
#define TASK_SHUTTINGDOWN(t)		(((t)->flags & TASK_F_SHUTTINGDOWN) \
					 != 0)
123

124 125
#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
126

127 128 129
typedef ISC_LIST(isc__task_t)	isc__tasklist_t;

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

	/*
	 * 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;
161
	isc__task_t			*excl;
162
#ifdef USE_SHARED_MANAGER
163
	unsigned int			refs;
164
#endif /* ISC_PLATFORM_USETHREADS */
Bob Halley's avatar
Bob Halley committed
165
};
Bob Halley's avatar
Bob Halley committed
166

167
#define DEFAULT_TASKMGR_QUANTUM		10
Bob Halley's avatar
Bob Halley committed
168 169
#define DEFAULT_DEFAULT_QUANTUM		5
#define FINISHED(m)			((m)->exiting && EMPTY((m)->tasks))
Bob Halley's avatar
Bob Halley committed
170

171 172 173 174 175
#ifdef USE_SHARED_MANAGER
static isc__taskmgr_t *taskmgr = NULL;
#endif /* USE_SHARED_MANAGER */

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

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

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

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

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

static struct isc__taskmethods {
	isc_taskmethods_t methods;

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

static isc_taskmgrmethods_t taskmgrmethods = {
	isc__taskmgr_destroy,
291 292
	isc__taskmgr_setmode,
	isc__taskmgr_mode,
293
	isc__task_create,
294 295
	isc_taskmgr_setexcltask,
	isc_taskmgr_excltask
296
};
297

Bob Halley's avatar
base  
Bob Halley committed
298 299 300 301 302
/***
 *** Tasks.
 ***/

static void
303 304
task_finished(isc__task_t *task) {
	isc__taskmgr_t *manager = task->manager;
Bob Halley's avatar
base  
Bob Halley committed
305 306

	REQUIRE(EMPTY(task->events));
307
	REQUIRE(task->nevents == 0);
308
	REQUIRE(EMPTY(task->on_shutdown));
309 310 311 312
	REQUIRE(task->references == 0);
	REQUIRE(task->state == task_state_done);

	XTRACE("task_finished");
Bob Halley's avatar
base  
Bob Halley committed
313 314 315

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

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

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

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

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

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

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

388 389 390 391
	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
392

Bob Halley's avatar
Bob Halley committed
393
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
394 395
}

396
void
397 398
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
399

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

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

407
	XTTRACE(source, "isc_task_attach");
408

409 410 411 412
	LOCK(&source->lock);
	source->references++;
	UNLOCK(&source->lock);

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

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

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

	XTRACE("task_shutdown");

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

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

	return (was_idle);
}

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

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

	XTRACE("task_ready");

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

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

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

	REQUIRE(task->references > 0);

	XTRACE("detach");

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

Bob Halley's avatar
Bob Halley committed
507
	return (ISC_FALSE);
508 509
}

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

515 516 517 518
	/*
	 * Detach *taskp from its task.
	 */

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

523 524
	XTRACE("isc_task_detach");

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

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

	*taskp = NULL;
}

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

540
	/*
541
	 * Caller must be holding the task lock.
542 543
	 */

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

551 552
	XTRACE("task_send");

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	LOCK(&task->lock);
667 668

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

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

683 684 685
	return (count);
}

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

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

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

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

726 727
	XTRACE("isc_task_purge");

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

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

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

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

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

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

	XTRACE("isc_task_unsendrange");

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

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

	XTRACE("isc_task_unsend");

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

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

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

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

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