task.c 56.6 KB
Newer Older
Bob Halley's avatar
Bob Halley committed
1
/*
2
 * Copyright (C) 2004-2016  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

18 19
/*! \file
 * \author Principal Author: Bob Halley
Bob Halley's avatar
Bob Halley committed
20 21
 */

22 23 24 25 26
/*
 * XXXRTH  Need to document the states a task can be in, and the rules
 * for changing states.
 */

Bob Halley's avatar
Bob Halley committed
27 28
#include <config.h>

29
#include <isc/app.h>
30
#include <isc/condition.h>
31
#include <isc/event.h>
32
#include <isc/json.h>
33
#include <isc/magic.h>
34
#include <isc/mem.h>
35
#include <isc/msgs.h>
36
#include <isc/once.h>
Brian Wellington's avatar
Brian Wellington committed
37
#include <isc/platform.h>
38
#include <isc/print.h>
39
#include <isc/string.h>
Bob Halley's avatar
Bob Halley committed
40
#include <isc/task.h>
41
#include <isc/thread.h>
Evan Hunt's avatar
Evan Hunt committed
42
#include <isc/time.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;
Evan Hunt's avatar
Evan Hunt committed
118
	isc_time_t			tnow;
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);
Evan Hunt's avatar
Evan Hunt committed
231 232
void
isc__task_getcurrenttimex(isc_task_t *task0, isc_time_t *t);
233
isc_result_t
234 235
isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers,
		    unsigned int default_quantum, isc_taskmgr_t **managerp);
236
void
237
isc__taskmgr_destroy(isc_taskmgr_t **managerp);
238 239 240 241 242
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
243
isc__task_beginexclusive(isc_task_t *task);
244
void
245
isc__task_endexclusive(isc_task_t *task0);
246
void
247
isc__task_setprivilege(isc_task_t *task0, isc_boolean_t priv);
248
isc_boolean_t
249
isc__task_privilege(isc_task_t *task0);
250
void
251
isc__taskmgr_setmode(isc_taskmgr_t *manager0, isc_taskmgrmode_t mode);
252
isc_taskmgrmode_t
253 254
isc__taskmgr_mode(isc_taskmgr_t *manager0);

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

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

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

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
270 271
	void *purgeevent, *unsendrange, *getname, *gettag,
	     *getcurrenttime, *getcurrenttimex;
272 273 274 275 276 277 278 279 280 281 282 283
} 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,
284 285
		isc__task_purgerange,
		isc__task_beginexclusive,
286 287 288
		isc__task_endexclusive,
		isc__task_setprivilege,
		isc__task_privilege
289 290 291 292 293
	},
	(void *)isc_task_purgeevent,
	(void *)isc__task_unsendrange,
	(void *)isc__task_getname,
	(void *)isc__task_gettag,
Evan Hunt's avatar
Evan Hunt committed
294 295
	(void *)isc__task_getcurrenttime,
	(void *)isc__task_getcurrenttimex
296 297 298 299
};

static isc_taskmgrmethods_t taskmgrmethods = {
	isc__taskmgr_destroy,
300 301
	isc__taskmgr_setmode,
	isc__taskmgr_mode,
302
	isc__task_create,
303 304
	isc_taskmgr_setexcltask,
	isc_taskmgr_excltask
305
};
306

Bob Halley's avatar
base  
Bob Halley committed
307 308 309 310 311
/***
 *** Tasks.
 ***/

static void
312 313
task_finished(isc__task_t *task) {
	isc__taskmgr_t *manager = task->manager;
Bob Halley's avatar
base  
Bob Halley committed
314 315

	REQUIRE(EMPTY(task->events));
316
	REQUIRE(task->nevents == 0);
317
	REQUIRE(EMPTY(task->on_shutdown));
318 319 320 321
	REQUIRE(task->references == 0);
	REQUIRE(task->state == task_state_done);

	XTRACE("task_finished");
Bob Halley's avatar
base  
Bob Halley committed
322 323 324

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

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

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

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

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

381
	exiting = ISC_FALSE;
Bob Halley's avatar
base  
Bob Halley committed
382
	LOCK(&manager->lock);
383 384 385 386 387 388
	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
389 390
	UNLOCK(&manager->lock);

391
	if (exiting) {
Brian Wellington's avatar
Brian Wellington committed
392
		DESTROYLOCK(&task->lock);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
393
		isc_mem_put(manager->mctx, task, sizeof(*task));
394 395 396
		return (ISC_R_SHUTTINGDOWN);
	}

397 398 399 400
	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
401

Bob Halley's avatar
Bob Halley committed
402
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
403 404
}

405
void
406 407
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
408

409 410 411
	/*
	 * Attach *targetp to source.
	 */
Bob Halley's avatar
base  
Bob Halley committed
412

413 414
	REQUIRE(VALID_TASK(source));
	REQUIRE(targetp != NULL && *targetp == NULL);
Bob Halley's avatar
base  
Bob Halley committed
415

416
	XTTRACE(source, "isc_task_attach");
417

418 419 420 421
	LOCK(&source->lock);
	source->references++;
	UNLOCK(&source->lock);

422
	*targetp = (isc_task_t *)source;
Bob Halley's avatar
base  
Bob Halley committed
423 424
}

425
static inline isc_boolean_t
426
task_shutdown(isc__task_t *task) {
427 428
	isc_boolean_t was_idle = ISC_FALSE;
	isc_event_t *event, *prev;
429

430 431 432 433 434 435 436
	/*
	 * Caller must be holding the task's lock.
	 */

	XTRACE("task_shutdown");

	if (! TASK_SHUTTINGDOWN(task)) {
437 438
		XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
				      ISC_MSG_SHUTTINGDOWN, "shutting down"));
439 440 441 442 443 444 445 446
		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);
447

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

	return (was_idle);
}

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

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

	XTRACE("task_ready");

	LOCK(&manager->lock);
482
	push_readyq(manager, task);
483
#ifdef USE_WORKER_THREADS
484 485
	if (manager->mode == isc_taskmgrmode_normal || has_privilege)
		SIGNAL(&manager->work_available);
486
#endif /* USE_WORKER_THREADS */
487 488 489
	UNLOCK(&manager->lock);
}

Bob Halley's avatar
Bob Halley committed
490
static inline isc_boolean_t
491
task_detach(isc__task_t *task) {
492 493 494 495 496 497 498 499 500 501

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

	REQUIRE(task->references > 0);

	XTRACE("detach");

	task->references--;
Bob Halley's avatar
Bob Halley committed
502 503 504 505 506 507 508
	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
509 510
		 * 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
511 512 513
		 */
		task->state = task_state_ready;
		return (ISC_TRUE);
514 515
	}

Bob Halley's avatar
Bob Halley committed
516
	return (ISC_FALSE);
517 518
}

519
void
520 521
isc__task_detach(isc_task_t **taskp) {
	isc__task_t *task;
Bob Halley's avatar
Bob Halley committed
522
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
523

524 525 526 527
	/*
	 * Detach *taskp from its task.
	 */

Bob Halley's avatar
base  
Bob Halley committed
528
	REQUIRE(taskp != NULL);
529
	task = (isc__task_t *)*taskp;
Bob Halley's avatar
base  
Bob Halley committed
530 531
	REQUIRE(VALID_TASK(task));

532 533
	XTRACE("isc_task_detach");

Bob Halley's avatar
base  
Bob Halley committed
534
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
535
	was_idle = task_detach(task);
Bob Halley's avatar
base  
Bob Halley committed
536 537
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
538
	if (was_idle)
539
		task_ready(task);
Bob Halley's avatar
base  
Bob Halley committed
540 541 542 543

	*taskp = NULL;
}

Bob Halley's avatar
Bob Halley committed
544
static inline isc_boolean_t
545
task_send(isc__task_t *task, isc_event_t **eventp) {
Bob Halley's avatar
Bob Halley committed
546
	isc_boolean_t was_idle = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
547
	isc_event_t *event;
548

549
	/*
550
	 * Caller must be holding the task lock.
551 552
	 */

Bob Halley's avatar
update  
Bob Halley committed
553 554
	REQUIRE(eventp != NULL);
	event = *eventp;
Bob Halley's avatar
base  
Bob Halley committed
555
	REQUIRE(event != NULL);
556
	REQUIRE(event->ev_type > 0);
Bob Halley's avatar
Bob Halley committed
557
	REQUIRE(task->state != task_state_done);
Bob Halley's avatar
base  
Bob Halley committed
558

559 560
	XTRACE("task_send");

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

Bob Halley's avatar
Bob Halley committed
572
	return (was_idle);
573
}
574

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

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

617
void
618
isc__task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
Bob Halley's avatar
Bob Halley committed
619
	isc_boolean_t idle1, idle2;
620
	isc__task_t *task;
621 622 623 624 625 626 627

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

	REQUIRE(taskp != NULL);
628
	task = (isc__task_t *)*taskp;
629 630 631
	REQUIRE(VALID_TASK(task));

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

633
	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
634 635
	idle1 = task_send(task, eventp);
	idle2 = task_detach(task);
636 637
	UNLOCK(&task->lock);

Bob Halley's avatar
Bob Halley committed
638 639 640 641 642 643
	/*
	 * 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));
644

Bob Halley's avatar
Bob Halley committed
645 646
	if (idle1 || idle2)
		task_ready(task);
647 648

	*taskp = NULL;
Bob Halley's avatar
base  
Bob Halley committed
649 650
}

651
#define PURGE_OK(event)	(((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0)
652 653

static unsigned int
654
dequeue_events(isc__task_t *task, void *sender, isc_eventtype_t first,
655 656
	       isc_eventtype_t last, void *tag,
	       isc_eventlist_t *events, isc_boolean_t purging)
657
{
Bob Halley's avatar
Bob Halley committed
658
	isc_event_t *event, *next_event;
659
	unsigned int count = 0;
660

Bob Halley's avatar
Bob Halley committed
661
	REQUIRE(VALID_TASK(task));
662
	REQUIRE(last >= first);
Bob Halley's avatar
Bob Halley committed
663

664
	XTRACE("dequeue_events");
Bob Halley's avatar
Bob Halley committed
665

Bob Halley's avatar
Bob Halley committed
666
	/*
667 668 669
	 * 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.
670
	 *
671
	 * sender == NULL means "any sender", and tag == NULL means "any tag".
Bob Halley's avatar
Bob Halley committed
672 673 674
	 */

	LOCK(&task->lock);
675 676

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

Bob Halley's avatar
Bob Halley committed
689 690
	UNLOCK(&task->lock);

691 692 693
	return (count);
}

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

719 720 721 722 723
	/*
	 * Note that purging never changes the state of the task.
	 */

	return (count);
Bob Halley's avatar
Bob Halley committed
724 725
}

726
unsigned int
727 728
isc__task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
		void *tag)
Bob Halley's avatar
Bob Halley committed
729
{
730 731 732 733
	/*
	 * Purge events from a task's event queue.
	 */

734 735
	XTRACE("isc_task_purge");

736
	return (isc__task_purgerange(task, sender, type, type, tag));
737 738
}

739 740
isc_boolean_t
isc_task_purgeevent(isc_task_t *task0, isc_event_t *event) {
741
	isc__task_t *task = (isc__task_t *)task0;
Bob Halley's avatar
Bob Halley committed
742 743 744 745
	isc_event_t *curr_event, *next_event;

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

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

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

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

	XTRACE("isc_task_unsendrange");

794 795
	return (dequeue_events((isc__task_t *)task, sender, first,
			       last, tag, events, ISC_FALSE));
796 797
}

798
unsigned int
799 800
isc__task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type,
		 void *tag, isc_eventlist_t *events)
801 802 803 804 805 806 807
{
	/*
	 * Remove events from a task's event queue.
	 */

	XTRACE("isc_task_unsend");

808 809
	return (dequeue_events((isc__task_t *)task, sender, type,
			       type, tag, events, ISC_FALSE));
810 811
}

812
isc_result_t
813
isc__task_onshutdown(isc_task_t *task0, isc_taskaction_t action,
Mark Andrews's avatar
Mark Andrews committed
814
		     void *arg)
David Lawrence's avatar
David Lawrence committed
815
{
816
	isc__task_t *task = (isc__task_t *)task0;
817 818 819 820
	isc_boolean_t disallowed = ISC_FALSE;
	isc_result_t result = ISC_R_SUCCESS;
	isc_event_t *event;

821 822 823 824 825
	/*
	 * Send a shutdown event with action 'action' and argument 'arg' when
	 * 'task' is shutdown.
	 */

826
	REQUIRE(VALID_TASK(task));
827
	REQUIRE(action != NULL);
828

829
	event = isc_event_allocate(task->manager->mctx