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

Francis Dupont's avatar
Francis Dupont committed
43 44 45 46
#ifdef OPENSSL_LEAKS
#include <openssl/err.h>
#endif

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

67 68
#include "task_p.h"

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

Bob Halley's avatar
Bob Halley committed
82
/***
Bob Halley's avatar
Bob Halley committed
83
 *** Types.
Bob Halley's avatar
Bob Halley committed
84 85
 ***/

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

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

97 98
#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
99

100 101 102 103
typedef struct isc__task isc__task_t;
typedef struct isc__taskmgr isc__taskmgr_t;

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

Bob Halley's avatar
Bob Halley committed
125
#define TASK_F_SHUTTINGDOWN		0x01
126
#define TASK_F_PRIVILEGED		0x02
127

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

131 132
#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
133

134 135 136
typedef ISC_LIST(isc__task_t)	isc__tasklist_t;

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

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

170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
#ifdef USE_SHARED_MANAGER
static isc__taskmgr_t *taskmgr = NULL;
#endif /* USE_SHARED_MANAGER */

/*%
 * The following can be either static or public, depending on build environment.
 */

#ifdef BIND9
#define ISC_TASKFUNC_SCOPE
#else
#define ISC_TASKFUNC_SCOPE static
#endif

ISC_TASKFUNC_SCOPE isc_result_t
isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum,
		 isc_task_t **taskp);
ISC_TASKFUNC_SCOPE void
isc__task_attach(isc_task_t *source0, isc_task_t **targetp);
ISC_TASKFUNC_SCOPE void
isc__task_detach(isc_task_t **taskp);
ISC_TASKFUNC_SCOPE void
isc__task_send(isc_task_t *task0, isc_event_t **eventp);
ISC_TASKFUNC_SCOPE void
isc__task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp);
ISC_TASKFUNC_SCOPE unsigned int
isc__task_purgerange(isc_task_t *task0, void *sender, isc_eventtype_t first,
		     isc_eventtype_t last, void *tag);
ISC_TASKFUNC_SCOPE unsigned int
isc__task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
		void *tag);
ISC_TASKFUNC_SCOPE isc_boolean_t
isc__task_purgeevent(isc_task_t *task0, isc_event_t *event);
ISC_TASKFUNC_SCOPE unsigned int
isc__task_unsendrange(isc_task_t *task, void *sender, isc_eventtype_t first,
		      isc_eventtype_t last, void *tag,
		      isc_eventlist_t *events);
ISC_TASKFUNC_SCOPE unsigned int
isc__task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type,
		 void *tag, isc_eventlist_t *events);
ISC_TASKFUNC_SCOPE isc_result_t
isc__task_onshutdown(isc_task_t *task0, isc_taskaction_t action,
		     const void *arg);
ISC_TASKFUNC_SCOPE void
isc__task_shutdown(isc_task_t *task0);
ISC_TASKFUNC_SCOPE void
isc__task_destroy(isc_task_t **taskp);
ISC_TASKFUNC_SCOPE void
isc__task_setname(isc_task_t *task0, const char *name, void *tag);
ISC_TASKFUNC_SCOPE const char *
isc__task_getname(isc_task_t *task0);
ISC_TASKFUNC_SCOPE void *
isc__task_gettag(isc_task_t *task0);
ISC_TASKFUNC_SCOPE void
isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t);
ISC_TASKFUNC_SCOPE isc_result_t
isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers,
		    unsigned int default_quantum, isc_taskmgr_t **managerp);
ISC_TASKFUNC_SCOPE void
isc__taskmgr_destroy(isc_taskmgr_t **managerp);
ISC_TASKFUNC_SCOPE isc_result_t
isc__task_beginexclusive(isc_task_t *task);
ISC_TASKFUNC_SCOPE void
isc__task_endexclusive(isc_task_t *task0);
234 235 236 237 238 239 240 241 242
ISC_TASKFUNC_SCOPE void
isc__task_setprivilege(isc_task_t *task0, isc_boolean_t priv);
ISC_TASKFUNC_SCOPE isc_boolean_t
isc__task_privilege(isc_task_t *task0);
ISC_TASKFUNC_SCOPE void
isc__taskmgr_setmode(isc_taskmgr_t *manager0, isc_taskmgrmode_t mode);
ISC_TASKFUNC_SCOPE isc_taskmgrmode_t
isc__taskmgr_mode(isc_taskmgr_t *manager0);

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

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

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

static struct isc__taskmethods {
	isc_taskmethods_t methods;

	/*%
	 * The following are defined just for avoiding unused static functions.
	 */
258
#ifndef BIND9
259
	void *purgeevent, *unsendrange, *getname, *gettag, *getcurrenttime;
260
#endif
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
	}
#ifndef BIND9
	,
	(void *)isc__task_purgeevent, (void *)isc__task_unsendrange,
	(void *)isc__task_getname, (void *)isc__task_gettag,
283
	(void *)isc__task_getcurrenttime
284
#endif
285 286 287 288
};

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

402
	XTTRACE(source, "isc_task_attach");
403

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

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

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

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

	XTRACE("task_shutdown");

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

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

	return (was_idle);
}

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

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

	XTRACE("task_ready");

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

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

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

	REQUIRE(task->references > 0);

	XTRACE("detach");

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

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

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

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

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

518 519
	XTRACE("isc_task_detach");

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

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

	*taskp = NULL;
}

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

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

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

545 546
	XTRACE("task_send");

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	LOCK(&task->lock);
661 662

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

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

677 678 679
	return (count);
}

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

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

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

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

720 721
	XTRACE("isc_task_purge");

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

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

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

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

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

	isc_event_free(&curr_event);

	return (ISC_TRUE);
}

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

	XTRACE("isc_task_unsendrange");

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

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

	XTRACE("isc_task_unsend");

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

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

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

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

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

	LOCK(&task->lock);
Bob Halley's avatar
Bob Halley committed
825
	if (TASK_SHUTTINGDOWN(task)) {
826
		disallowed = ISC_TRUE;
827
		result = ISC_R_SHUTTINGDOWN;
828
	} else
829
		ENQUEUE(task->on_shutdown, event, ev_link);
830 831 832
	UNLOCK(&task->lock);

	if (disallowed)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
833
		isc_mem_put(task->manager->mctx, event, sizeof(*event));
834 835 836 837

	return (result);
}

838 839 840
ISC_TASKFUNC_SCOPE void
isc__task_shutdown(isc_task_t *task0) {
	isc__task_t *task = (isc__task_t *)task0;
841
	isc_boolean_t was_idle;
Bob Halley's avatar
base  
Bob Halley committed
842

843 844 845 846
	/*
	 * Shutdown 'task'.
	 */

Bob Halley's avatar
base  
Bob Halley committed
847 848 849
	REQUIRE(VALID_TASK(task));

	LOCK(&task->lock);
850
	was_idle = task_shutdown(task);
Bob Halley's avatar
base  
Bob Halley committed
851 852
	UNLOCK(&task->lock);

853 854
	if (was_idle)
		task_ready(task);
Bob Halley's avatar
Bob Halley committed
855
}
Bob Halley's avatar
base  
Bob Halley committed
856

857 858
ISC_TASKFUNC_SCOPE void
isc__task_destroy(isc_task_t **taskp) {
Bob Halley's avatar
Bob Halley committed
859

860 861 862 863
	/*
	 * Destroy '*taskp'.
	 */

Bob Halley's avatar
Bob Halley committed
864 865
	REQUIRE(taskp != NULL);

Bob Halley's avatar
Bob Halley committed
866 867
	isc_task_shutdown(*taskp);
	isc_task_detach(taskp);
Bob Halley's avatar
base  
Bob Halley committed
868 869
}

870 871 872
ISC_TASKFUNC_SCOPE void
isc__task_setname(isc_task_t *task0, const char *name, void *tag) {
	isc__task_t *task = (isc__task_t *)task0;
873 874 875 876 877 878 879 880 881 882 883 884 885

	/*
	 * Name 'task'.
	 */

	REQUIRE(VALID_TASK(task));

	LOCK(&task->lock);
	memset(task->name, 0, sizeof(task->name));
	strncpy(task->name, name, sizeof(task->name) - 1);
	task->tag = tag;
	UNLOCK(&task->lock);
}
Bob Halley's avatar
Bob Halley committed
886

887 888 889 890 891 892
ISC_TASKFUNC_SCOPE const char *
isc__task_getname(isc_task_t *task0) {
	isc__task_t *task = (isc__task_t *)task0;

	REQUIRE(VALID_TASK(task));

893 894 895
	return (task->name);
}

896 897 898 899 900 901
ISC_TASKFUNC_SCOPE void *
isc__task_gettag(isc_task_t *task0) {
	isc__task_t *task = (isc__task_t *)task0;

	REQUIRE(VALID_TASK(task));

902 903 904
	return (task->tag);
}

905 906 907 908
ISC_TASKFUNC_SCOPE void
isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t) {
	isc__task_t *task = (isc__task_t *)task0;

909 910 911 912 913 914 915
	REQUIRE(VALID_TASK(task));
	REQUIRE(t != NULL);

	LOCK(&task->lock);
	*t = task->now;
	UNLOCK(&task->lock);
}
916

Bob Halley's avatar
base  
Bob Halley committed
917 918 919
/***
 *** Task Manager.
 ***/
920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940