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);
Bob Halley's avatar
base  
Bob Halley committed
549

550 551
	XTRACE("task_send");

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	LOCK(&task->lock);
666 667

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

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

682 683 684
	return (count);
}

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

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

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

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

725 726
	XTRACE("isc_task_purge");

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

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

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

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

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

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

	XTRACE("isc_task_unsendrange");

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

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

	XTRACE("isc_task_unsend");

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

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

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

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

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

	LOCK(&task->lock);