task.c revision ad555ae689d3bb4fb76426dee43946686e7e3621
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley/*
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * Copyright (C) 1998, 1999 Internet Software Consortium.
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews *
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence * Permission to use, copy, modify, and distribute this software for any
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley * purpose with or without fee is hereby granted, provided that the above
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley * copyright notice and this permission notice appear in all copies.
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley *
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * SOFTWARE.
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley */
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley
d0eb2cc33c5db3366a16b1cb0abcca6ec7c8ee3cTatuya JINMEI 神明達哉/*
9c3531d72aeaad6c5f01efe6a1c82023e1379e4dDavid Lawrence * Principal Author: Bob Halley
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley */
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley
f1788d67add6bf3d301e91b3f54fa3c90a87328eBrian Wellington/*
364a82f7c25b62967678027043425201a5e5171aBob Halley * XXXRTH Need to document the states a task can be in, and the rules
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley * for changing states.
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley */
1a69a1a78cfaa86f3b68bbc965232b7876d4da2aDavid Lawrence
1a69a1a78cfaa86f3b68bbc965232b7876d4da2aDavid Lawrence#include <config.h>
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#include <isc/assertions.h>
1a69a1a78cfaa86f3b68bbc965232b7876d4da2aDavid Lawrence#include <isc/boolean.h>
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#include <isc/thread.h>
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#include <isc/mutex.h>
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#include <isc/condition.h>
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#include <isc/error.h>
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#include <isc/event.h>
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#include <isc/task.h>
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#include "util.h"
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#ifdef ISC_TASK_TRACE
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#define XTRACE(m) printf("task %p thread %lu: %s\n", \
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley task, isc_thread_self(), (m))
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#define XTTRACE(t, m) printf("task %p thread %lu: %s\n", \
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley (t), isc_thread_self(), (m))
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#define XTHREADTRACE(m) printf("thread %lu: %s\n", \
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley isc_thread_self(), (m))
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#else
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#define XTRACE(m)
c03bb27f0675a6e60ceea66b451548e8481bc05cMark Andrews#define XTTRACE(t, m)
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#define XTHREADTRACE(m)
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#endif
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley/***
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence *** Types.
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley ***/
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halleytypedef enum {
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley detach_result_ok, detach_result_finished, detach_result_wasidle
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley} detach_result_t;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halleytypedef enum {
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley task_state_idle, task_state_ready, task_state_running,
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley task_state_done
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley} task_state_t;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence#define TASK_MAGIC 0x5441534BU /* TASK. */
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#define VALID_TASK(t) ((t) != NULL && \
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley (t)->magic == TASK_MAGIC)
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halleystruct isc_task {
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley /* Not locked. */
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley unsigned int magic;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley isc_taskmgr_t * manager;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley isc_mutex_t lock;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley isc_mem_t * mctx;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley /* Locked by task lock. */
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley task_state_t state;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley unsigned int references;
368b37b616234fce3d23099eb180f1dd38e1fb62Mark Andrews isc_eventlist_t events;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley isc_eventlist_t on_shutdown;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley unsigned int quantum;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley unsigned int flags;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley /* Locked by task manager lock. */
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley LINK(isc_task_t) link;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley LINK(isc_task_t) ready_link;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley};
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#define TASK_F_DONEOK 0x01
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#define TASK_F_SENDOK 0x02
7cd4c3ddd1baf5f2b204562fdba3da37c716cc78Andreas Gustafsson#define TASK_F_SHUTTINGDOWN 0x04
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#define DONE_FLAGS (TASK_F_DONEOK|TASK_F_SHUTTINGDOWN)
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#define TASK_WANTDONE(t) (((t)->flags & DONE_FLAGS) == \
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley DONE_FLAGS)
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#define TASK_SHUTTINGDOWN(t) (((t)->flags & TASK_F_SHUTTINGDOWN) \
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley != 0)
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#define TASK_MANAGER_MAGIC 0x54534B4DU /* TSKM. */
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#define VALID_MANAGER(m) ((m) != NULL && \
66b2f0d4bfa342770aa5e26a005a0c0ec5071231Bob Halley (m)->magic == TASK_MANAGER_MAGIC)
c03bb27f0675a6e60ceea66b451548e8481bc05cMark Andrews
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halleystruct isc_taskmgr {
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley /* Not locked. */
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley unsigned int magic;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley isc_mem_t * mctx;
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley isc_mutex_t lock;
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley unsigned int workers;
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley isc_thread_t * threads;
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley /* Locked by task manager lock. */
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley unsigned int default_quantum;
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley LIST(isc_task_t) tasks;
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley LIST(isc_task_t) ready_tasks;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley isc_condition_t work_available;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley isc_boolean_t exiting;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley};
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#define DEFAULT_DEFAULT_QUANTUM 5
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley#define FINISHED(m) ((m)->exiting && EMPTY((m)->tasks))
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley/***
7cd4c3ddd1baf5f2b204562fdba3da37c716cc78Andreas Gustafsson *** Tasks.
66b2f0d4bfa342770aa5e26a005a0c0ec5071231Bob Halley ***/
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence
4607e7a9b8dfb1b41c70e51c2b603daaf22cf302Mark Andrewsstatic void
4607e7a9b8dfb1b41c70e51c2b603daaf22cf302Mark Andrewstask_finished(isc_task_t *task) {
4607e7a9b8dfb1b41c70e51c2b603daaf22cf302Mark Andrews isc_taskmgr_t *manager = task->manager;
4607e7a9b8dfb1b41c70e51c2b603daaf22cf302Mark Andrews
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley REQUIRE(EMPTY(task->events));
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley REQUIRE(EMPTY(task->on_shutdown));
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley REQUIRE(task->references == 0);
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley REQUIRE(task->state == task_state_done);
66b2f0d4bfa342770aa5e26a005a0c0ec5071231Bob Halley
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley XTRACE("task_finished");
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley LOCK(&manager->lock);
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley UNLINK(manager->tasks, task, link);
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley if (FINISHED(manager)) {
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley /*
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley * All tasks have completed and the
93d6dfaf66258337985427c86181f01fc51f0bb4Mark Andrews * task manager is exiting. Wake up
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley * any idle worker threads so they
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley * can exit.
93d6dfaf66258337985427c86181f01fc51f0bb4Mark Andrews */
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley BROADCAST(&manager->work_available);
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley }
66b2f0d4bfa342770aa5e26a005a0c0ec5071231Bob Halley UNLOCK(&manager->lock);
66b2f0d4bfa342770aa5e26a005a0c0ec5071231Bob Halley
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley (void)isc_mutex_destroy(&task->lock);
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley task->magic = 0;
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley isc_mem_put(task->mctx, task, sizeof *task);
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley}
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halleyisc_result_t
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halleyisc_task_create(isc_taskmgr_t *manager, isc_mem_t *mctx, unsigned int quantum,
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley isc_task_t **taskp)
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley{
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley isc_task_t *task;
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley
1a69a1a78cfaa86f3b68bbc965232b7876d4da2aDavid Lawrence REQUIRE(VALID_MANAGER(manager));
1a69a1a78cfaa86f3b68bbc965232b7876d4da2aDavid Lawrence REQUIRE(taskp != NULL && *taskp == NULL);
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley if (mctx == NULL)
b5fff54fe9335b20c02d749831fc0eaeda97198fBrian Wellington mctx = manager->mctx;
b5fff54fe9335b20c02d749831fc0eaeda97198fBrian Wellington task = isc_mem_get(mctx, sizeof *task);
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley if (task == NULL)
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley return (ISC_R_NOMEMORY);
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley XTRACE("create");
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley task->manager = manager;
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley task->mctx = mctx;
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley if (isc_mutex_init(&task->lock) != ISC_R_SUCCESS) {
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley isc_mem_put(mctx, task, sizeof *task);
e0df061f35a26d2bbd0986aa889f88b3710b32d4Bob Halley UNEXPECTED_ERROR(__FILE__, __LINE__,
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley "isc_mutex_init() failed");
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley return (ISC_R_UNEXPECTED);
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley }
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley task->state = task_state_idle;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley task->references = 1;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley INIT_LIST(task->events);
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley INIT_LIST(task->on_shutdown);
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley task->quantum = quantum;
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley task->flags = (TASK_F_DONEOK|TASK_F_SENDOK);
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley INIT_LINK(task, link);
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley INIT_LINK(task, ready_link);
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley LOCK(&manager->lock);
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley /* XXX Should disallow if task manager is exiting. */
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley if (task->quantum == 0)
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley task->quantum = manager->default_quantum;
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley APPEND(manager->tasks, task, link);
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley UNLOCK(&manager->lock);
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley task->magic = TASK_MAGIC;
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley *taskp = task;
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley return (ISC_R_SUCCESS);
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley}
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halleyvoid
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halleyisc_task_attach(isc_task_t *source, isc_task_t **targetp) {
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley /*
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley * Attach *targetp to source.
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley */
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence REQUIRE(VALID_TASK(source));
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley REQUIRE(targetp != NULL && *targetp == NULL);
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley XTTRACE(source, "attach");
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley LOCK(&source->lock);
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley source->references++;
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley UNLOCK(&source->lock);
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley *targetp = source;
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley}
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halleystatic inline isc_boolean_t
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halleytask_shutdown(isc_task_t *task) {
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley isc_boolean_t was_idle = ISC_FALSE;
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley isc_event_t *event, *prev;
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley /*
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley * Caller must be holding the task's lock.
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley */
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley XTRACE("task_shutdown");
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley if (! TASK_SHUTTINGDOWN(task)) {
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley XTRACE("shutting down");
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley task->flags |= TASK_F_SHUTTINGDOWN;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley if (task->state == task_state_idle) {
66b2f0d4bfa342770aa5e26a005a0c0ec5071231Bob Halley INSIST(EMPTY(task->events));
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence task->state = task_state_ready;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley was_idle = ISC_TRUE;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley }
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley INSIST(task->state == task_state_ready ||
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley task->state == task_state_running);
c03bb27f0675a6e60ceea66b451548e8481bc05cMark Andrews /*
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley * Note that we post shutdown events LIFO.
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley */
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley for (event = TAIL(task->on_shutdown);
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley event != NULL;
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley event = prev) {
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley prev = PREV(event, link);
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley DEQUEUE(task->on_shutdown, event, link);
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley ENQUEUE(task->events, event, link);
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley }
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley }
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley
1f1d36a87b65186d9f89aac7f456ab1fd2a39ef6Andreas Gustafsson return (was_idle);
1f1d36a87b65186d9f89aac7f456ab1fd2a39ef6Andreas Gustafsson}
66b2f0d4bfa342770aa5e26a005a0c0ec5071231Bob Halley
421e4cf66e4cba0b0751a34a9c027e39fe0474f9Mark Andrewsstatic inline void
421e4cf66e4cba0b0751a34a9c027e39fe0474f9Mark Andrewstask_ready(isc_task_t *task) {
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley isc_taskmgr_t *manager = task->manager;
55254a46f91419b92eee0d20dfb958e8dd52526cBob Halley
08af8bf5ade4131fe44926ad04fd489e64a620bbBob Halley REQUIRE(VALID_MANAGER(manager));
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley REQUIRE(task->state == task_state_ready);
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley XTRACE("task_ready");
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley
b2ca6fd3a8293440b4d263723525396059cf2400Brian Wellington LOCK(&manager->lock);
84185d19c7a9ef1ac23cc6236c8773697d4efeb1Brian Wellington
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley ENQUEUE(manager->ready_tasks, task, ready_link);
c03bb27f0675a6e60ceea66b451548e8481bc05cMark Andrews SIGNAL(&manager->work_available);
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley
8569ab045a4cf6ecd1b5a3354ddb1c93ef34ea57Brian Wellington UNLOCK(&manager->lock);
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley}
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halleystatic inline detach_result_t
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halleytask_detach(isc_task_t *task) {
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley detach_result_t dresult = detach_result_ok;
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley /*
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley * Caller must be holding the task lock.
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley */
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley REQUIRE(task->references > 0);
569d094440399b000e059d4cb3434391c2c4d330Michael Graff
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley XTRACE("detach");
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley task->references--;
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley if (task->references == 0) {
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley if (task->state == task_state_done)
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley dresult = detach_result_finished;
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley else if (task->state == task_state_idle) {
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence INSIST(EMPTY(task->events));
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley /*
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley * There are no references to this task, and no
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley * pending events. We initiate shutdown, since
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley * otherwise this task would just sit around until
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley * the task manager was destroyed.
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley */
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley if (task_shutdown(task))
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence dresult = detach_result_wasidle;
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley }
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley }
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley return (dresult);
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley}
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halleyvoid
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halleyisc_task_detach(isc_task_t **taskp) {
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halley isc_task_t *task;
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley detach_result_t dresult;
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley /*
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley * Detach *taskp from its task.
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley */
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley REQUIRE(taskp != NULL);
368b37b616234fce3d23099eb180f1dd38e1fb62Mark Andrews task = *taskp;
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley REQUIRE(VALID_TASK(task));
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley XTRACE("isc_task_detach");
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley LOCK(&task->lock);
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley dresult = task_detach(task);
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley UNLOCK(&task->lock);
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley
b2ca6fd3a8293440b4d263723525396059cf2400Brian Wellington if (dresult == detach_result_finished)
b2ca6fd3a8293440b4d263723525396059cf2400Brian Wellington task_finished(task);
84185d19c7a9ef1ac23cc6236c8773697d4efeb1Brian Wellington else if (dresult == detach_result_wasidle)
84185d19c7a9ef1ac23cc6236c8773697d4efeb1Brian Wellington task_ready(task);
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley *taskp = NULL;
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley}
94a08e09db3dc844b6ee4841c368a2d7074a9c3fAndreas Gustafsson
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halleyisc_mem_t *
419590499823ce15b5d2ad4fe71eaf04bd5a86c0Michael Graffisc_task_mem(isc_task_t *task) {
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley /*
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley * Get the task's memory context.
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley */
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley REQUIRE(VALID_TASK(task));
8569ab045a4cf6ecd1b5a3354ddb1c93ef34ea57Brian Wellington
8569ab045a4cf6ecd1b5a3354ddb1c93ef34ea57Brian Wellington return (task->mctx);
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley}
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halleystatic inline isc_result_t
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halleytask_send(isc_task_t *task, isc_event_t **eventp, isc_boolean_t *was_idlep) {
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley isc_result_t result = ISC_R_SUCCESS;
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley isc_event_t *event;
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley /*
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley * Caller must be holding the task lock.
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley */
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley REQUIRE(eventp != NULL);
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence event = *eventp;
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley REQUIRE(event != NULL);
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley REQUIRE(event->sender != NULL);
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley REQUIRE(event->type > 0);
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley
419590499823ce15b5d2ad4fe71eaf04bd5a86c0Michael Graff XTRACE("task_send");
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley /*
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley * Note: we require that task->state == task_state_done implies
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley * (task->flags & TASK_F_SENDOK) == 0.
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley */
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley *was_idlep = ISC_FALSE;
c866769e664ba0a6a5e6f9375245f5ccca393009David Lawrence if ((task->flags & TASK_F_SENDOK) != 0) {
c866769e664ba0a6a5e6f9375245f5ccca393009David Lawrence if (task->state == task_state_idle) {
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley *was_idlep = ISC_TRUE;
c866769e664ba0a6a5e6f9375245f5ccca393009David Lawrence INSIST(EMPTY(task->events));
c866769e664ba0a6a5e6f9375245f5ccca393009David Lawrence task->state = task_state_ready;
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley }
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley INSIST(task->state == task_state_ready ||
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley task->state == task_state_running);
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence ENQUEUE(task->events, event, link);
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley *eventp = NULL;
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley } else {
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley if (task->state == task_state_done)
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence result = ISC_R_TASKDONE;
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley else
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley result = ISC_R_TASKNOSEND;
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley }
c866769e664ba0a6a5e6f9375245f5ccca393009David Lawrence
c866769e664ba0a6a5e6f9375245f5ccca393009David Lawrence return (result);
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley}
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halley
948eabe2a254a8a278ef6325f3790e75329ee656Bob Halleyisc_result_t
4e142a5bccd2944174ad9ae58d86cf03e170054dBob Halleyisc_task_send(isc_task_t *task, isc_event_t **eventp) {
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews isc_boolean_t was_idle;
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews isc_result_t result;
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews /*
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * Send '*event' to 'task'.
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews */
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews REQUIRE(VALID_TASK(task));
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews XTRACE("isc_task_send");
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews /*
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * We're trying hard to hold locks for as short a time as possible.
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * We're also trying to hold as few locks as possible. This is why
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * some processing is deferred until after the lock is released.
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews */
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews LOCK(&task->lock);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews result = task_send(task, eventp, &was_idle);
80b782f356f0692c11b4e52e8dd46ec41704e5a2Mark Andrews UNLOCK(&task->lock);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews if (result != ISC_R_SUCCESS)
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews return (result);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
80b782f356f0692c11b4e52e8dd46ec41704e5a2Mark Andrews if (was_idle) {
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews /*
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * We need to add this task to the ready queue.
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews *
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * We've waited until now to do it because making a task
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * ready requires locking the manager. If we tried to do
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * this while holding the task lock, we could deadlock.
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews *
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * We've changed the state to ready, so no one else will
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * be trying to add this task to the ready queue. The
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * only way to leave the ready state is by executing the
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * task. It thus doesn't matter if events are added,
80b782f356f0692c11b4e52e8dd46ec41704e5a2Mark Andrews * removed, or a shutdown is started in the interval
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * between the time we released the task lock, and the time
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * we add the task to the ready queue.
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews */
80b782f356f0692c11b4e52e8dd46ec41704e5a2Mark Andrews task_ready(task);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews }
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews return (ISC_R_SUCCESS);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews}
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrewsisc_result_t
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrewsisc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews isc_boolean_t was_idle;
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews isc_result_t result;
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews isc_task_t *task;
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews detach_result_t dresult = detach_result_ok;
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews /*
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * Send '*event' to '*taskp' and then detach '*taskp' from its
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * task.
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews */
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews REQUIRE(taskp != NULL);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews task = *taskp;
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews REQUIRE(VALID_TASK(task));
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews XTRACE("isc_task_sendanddetach");
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews LOCK(&task->lock);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews result = task_send(task, eventp, &was_idle);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews if (result == ISC_R_SUCCESS)
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews dresult = task_detach(task);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews UNLOCK(&task->lock);
80b782f356f0692c11b4e52e8dd46ec41704e5a2Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews if (result != ISC_R_SUCCESS)
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews return (result);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews if (dresult == detach_result_finished) {
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews /*
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * If was_idle is true, then the task is ready with at least
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * one event in the queue, and nothing will happen until
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * we call task_ready(). In particular, the task cannot
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * be executing or have entered the done state, so if
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * dresult is detach_result_finished, was_idle must have been
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * false. We INSIST on it.
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews */
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews INSIST(!was_idle);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews task_finished(task);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews } else {
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews /*
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * If was_idle, then dresult shouldn't be
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * detach_result_wasidle, since that would mean someone else
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * changed the task's state from ready back to idle, which
7ac0df532272d803c3f72ff7a109587e92622f5aMark Andrews * should never happen. We INSIST on it.
7ac0df532272d803c3f72ff7a109587e92622f5aMark Andrews */
d0eb2cc33c5db3366a16b1cb0abcca6ec7c8ee3cTatuya JINMEI 神明達哉 INSIST(!(was_idle && dresult == detach_result_wasidle));
d0eb2cc33c5db3366a16b1cb0abcca6ec7c8ee3cTatuya JINMEI 神明達哉 if (was_idle || dresult == detach_result_wasidle)
d0eb2cc33c5db3366a16b1cb0abcca6ec7c8ee3cTatuya JINMEI 神明達哉 task_ready(task);
7ac0df532272d803c3f72ff7a109587e92622f5aMark Andrews }
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews *taskp = NULL;
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews return (ISC_R_SUCCESS);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews}
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews#define PURGE_OK(event) (((event)->attributes & ISC_EVENTATTR_NOPURGE) == 0)
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrewsstatic unsigned int
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrewsdequeue_events(isc_task_t *task, void *sender, isc_eventtype_t first,
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews isc_eventtype_t last, void *tag,
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews isc_eventlist_t *events, isc_boolean_t purging)
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews{
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews isc_event_t *event, *next_event;
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews unsigned int count = 0;
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews REQUIRE(VALID_TASK(task));
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews REQUIRE(last >= first);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
93d6dfaf66258337985427c86181f01fc51f0bb4Mark Andrews XTRACE("dequeue_events");
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews /*
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * Events matching 'sender', whose type is >= first and <= last, and
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * whose tag is 'tag' will be dequeued. If 'purging', matching events
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * which are marked as unpurgable will not be dequeued.
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews *
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * sender == NULL means "any sender", and tag == NULL means "any tag".
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews */
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews LOCK(&task->lock);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews for (event = HEAD(task->events); event != NULL; event = next_event) {
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews next_event = NEXT(event, link);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews if (event->type >= first && event->type <= last &&
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews (sender == NULL || event->sender == sender) &&
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews (tag == NULL || event->tag == tag) &&
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews (!purging || PURGE_OK(event))) {
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews DEQUEUE(task->events, event, link);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews ENQUEUE(*events, event, link);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews count++;
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews }
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews }
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews UNLOCK(&task->lock);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews return (count);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews}
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrewsunsigned int
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrewsisc_task_purgerange(isc_task_t *task, void *sender, isc_eventtype_t first,
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews isc_eventtype_t last, void *tag)
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews{
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews unsigned int count;
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews isc_eventlist_t events;
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews isc_event_t *event, *next_event;
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews /*
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * Purge events from a task's event queue.
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews */
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews XTRACE("isc_task_purgerange");
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews ISC_LIST_INIT(events);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews count = dequeue_events(task, sender, first, last, tag, &events,
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews ISC_TRUE);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews for (event = HEAD(events); event != NULL; event = next_event) {
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews next_event = NEXT(event, link);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews isc_event_free(&event);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews }
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews /*
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews * Note that purging never changes the state of the task.
80b782f356f0692c11b4e52e8dd46ec41704e5a2Mark Andrews */
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews return (count);
ff30cdeb783ca7ffe69b222c56197828e882c229Mark Andrews}
unsigned int
isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
void *tag)
{
/*
* Purge events from a task's event queue.
*/
XTRACE("isc_task_purge");
return (isc_task_purgerange(task, sender, type, type, tag));
}
isc_boolean_t
isc_task_purgeevent(isc_task_t *task, isc_event_t *event) {
isc_event_t *curr_event, *next_event;
/*
* Purge 'event' from a task's event queue.
*
* XXXRTH: WARNING: This method may be removed before beta.
*/
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) {
next_event = NEXT(curr_event, link);
if (curr_event == event && PURGE_OK(event)) {
DEQUEUE(task->events, curr_event, link);
break;
}
}
UNLOCK(&task->lock);
if (curr_event == NULL)
return (ISC_FALSE);
isc_event_free(&curr_event);
return (ISC_TRUE);
}
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)
{
/*
* Remove events from a task's event queue.
*/
XTRACE("isc_task_unsendrange");
return (dequeue_events(task, sender, first, last, tag, events,
ISC_FALSE));
}
unsigned int
isc_task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type,
void *tag, isc_eventlist_t *events)
{
/*
* Remove events from a task's event queue.
*/
XTRACE("isc_task_unsend");
return (dequeue_events(task, sender, type, type, tag, events,
ISC_FALSE));
}
isc_result_t
isc_task_allowsend(isc_task_t *task, isc_boolean_t allowed) {
isc_result_t result = ISC_R_SUCCESS;
/*
* Allow or disallow sending events to 'task'.
*
* XXXRTH: WARNING: This method may be removed before beta.
*/
REQUIRE(VALID_TASK(task));
LOCK(&task->lock);
if (task->state == task_state_done)
result = ISC_R_TASKDONE;
else {
if (allowed)
task->flags |= TASK_F_SENDOK;
else
task->flags &= ~TASK_F_SENDOK;
}
UNLOCK(&task->lock);
return (result);
}
isc_result_t
isc_task_allowdone(isc_task_t *task, isc_boolean_t allowed) {
isc_result_t result = ISC_R_SUCCESS;
isc_boolean_t was_idle = ISC_FALSE;
/*
* Allow or disallow automatic termination of 'task'.
*/
REQUIRE(VALID_TASK(task));
LOCK(&task->lock);
if (task->state == task_state_done)
result = ISC_R_TASKDONE;
else {
if (allowed) {
task->flags |= TASK_F_DONEOK;
/*
* To simply things, transition to the done state
* only occurs after running the task, so we do not
* attempt to go directly to the done state here.
*/
if (TASK_WANTDONE(task) &&
task->state == task_state_idle) {
INSIST(EMPTY(task->events));
task->state = task_state_ready;
was_idle = ISC_TRUE;
}
} else
task->flags &= ~TASK_F_DONEOK;
}
UNLOCK(&task->lock);
if (was_idle)
task_ready(task);
return (result);
}
isc_result_t
isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, void *arg) {
isc_boolean_t disallowed = ISC_FALSE;
isc_result_t result = ISC_R_SUCCESS;
isc_event_t *event;
/*
* Send a shutdown event with action 'action' and argument 'arg' when
* 'task' is shutdown.
*/
REQUIRE(VALID_TASK(task));
REQUIRE(action != NULL);
event = isc_event_allocate(task->mctx,
NULL,
ISC_TASKEVENT_SHUTDOWN,
action,
arg,
sizeof *event);
if (event == NULL)
return (ISC_R_NOMEMORY);
LOCK(&task->lock);
if (task->state == task_state_done) {
disallowed = ISC_TRUE;
result = ISC_R_TASKDONE;
} else if (TASK_SHUTTINGDOWN(task)) {
disallowed = ISC_TRUE;
result = ISC_R_TASKSHUTTINGDOWN;
} else
ENQUEUE(task->on_shutdown, event, link);
UNLOCK(&task->lock);
if (disallowed)
isc_mem_put(task->mctx, event, sizeof *event);
return (result);
}
void
isc_task_shutdown(isc_task_t *task) {
isc_boolean_t was_idle;
/*
* Shutdown 'task'.
*/
REQUIRE(VALID_TASK(task));
LOCK(&task->lock);
was_idle = task_shutdown(task);
UNLOCK(&task->lock);
if (was_idle)
task_ready(task);
}
void
isc_task_destroy(isc_task_t **taskp) {
/*
* Destroy '*taskp'.
*/
REQUIRE(taskp != NULL);
isc_task_shutdown(*taskp);
isc_task_detach(taskp);
}
/***
*** Task Manager.
***/
static isc_threadresult_t
#ifdef _WIN32
WINAPI
#endif
run(void *uap) {
isc_taskmgr_t *manager = uap;
isc_task_t *task;
XTHREADTRACE("start");
REQUIRE(VALID_MANAGER(manager));
/*
* Again we're trying to hold the lock for as short a time as possible
* and to do as little locking and unlocking as possible.
*
* In both while loops, the appropriate lock must be held before the
* while body starts. Code which acquired the lock at the top of
* the loop would be more readable, but would result in a lot of
* extra locking. Compare:
*
* Straightforward:
*
* LOCK();
* ...
* UNLOCK();
* while (expression) {
* LOCK();
* ...
* UNLOCK();
*
* Unlocked part here...
*
* LOCK();
* ...
* UNLOCK();
* }
*
* Note how if the loop continues we unlock and then immediately lock.
* For N iterations of the loop, this code does 2N+1 locks and 2N+1
* unlocks. Also note that the lock is not held when the while
* condition is tested, which may or may not be important, depending
* on the expression.
*
* As written:
*
* LOCK();
* while (expression) {
* ...
* UNLOCK();
*
* Unlocked part here...
*
* LOCK();
* ...
* }
* UNLOCK();
*
* For N iterations of the loop, this code does N+1 locks and N+1
* unlocks. The while expression is always protected by the lock.
*/
LOCK(&manager->lock);
while (!FINISHED(manager)) {
/*
* For reasons similar to those given in the comment in
* isc_task_send() above, it is safe for us to dequeue
* the task while only holding the manager lock, and then
* change the task to running state while only holding the
* task lock.
*/
while (EMPTY(manager->ready_tasks) && !FINISHED(manager)) {
XTHREADTRACE("wait");
WAIT(&manager->work_available, &manager->lock);
XTHREADTRACE("awake");
}
XTHREADTRACE("working");
task = HEAD(manager->ready_tasks);
if (task != NULL) {
unsigned int dispatch_count = 0;
isc_boolean_t done = ISC_FALSE;
isc_boolean_t requeue = ISC_FALSE;
isc_boolean_t finished = ISC_FALSE;
isc_event_t *event;
INSIST(VALID_TASK(task));
/*
* Note we only unlock the manager lock if we actually
* have a task to do. We must reacquire the manager
* lock before exiting the 'if (task != NULL)' block.
*/
DEQUEUE(manager->ready_tasks, task, ready_link);
UNLOCK(&manager->lock);
LOCK(&task->lock);
INSIST(task->state == task_state_ready);
task->state = task_state_running;
XTRACE("running");
do {
if (!EMPTY(task->events)) {
event = HEAD(task->events);
DEQUEUE(task->events, event, link);
/*
* Execute the event action.
*/
XTRACE("execute action");
if (event->action != NULL) {
UNLOCK(&task->lock);
(event->action)(task, event);
LOCK(&task->lock);
}
dispatch_count++;
}
if (task->references == 0 &&
EMPTY(task->events)) {
if (! TASK_SHUTTINGDOWN(task)) {
isc_boolean_t was_idle;
was_idle = task_shutdown(task);
INSIST(!was_idle);
} else {
/*
* We force the DONEOK flag
* to true so this task does
* not become a zombie.
*/
task->flags |= TASK_F_DONEOK;
}
}
if (EMPTY(task->events)) {
/*
* Nothing else to do for this task
* right now. If it is shutting down,
* then it is done, otherwise we just
* put it to sleep.
*/
XTRACE("empty");
if (TASK_WANTDONE(task)) {
XTRACE("done");
if (task->references == 0)
finished = ISC_TRUE;
task->flags &=
~TASK_F_SENDOK;
task->state = task_state_done;
} else
task->state = task_state_idle;
done = ISC_TRUE;
} else if (dispatch_count >= task->quantum) {
/*
* Our quantum has expired, but
* there is more work to be done.
* We'll requeue it to the ready
* queue later.
*
* We don't check quantum until
* dispatching at least one event,
* so the minimum quantum is one.
*/
XTRACE("quantum");
task->state = task_state_ready;
requeue = ISC_TRUE;
done = ISC_TRUE;
}
} while (!done);
UNLOCK(&task->lock);
if (finished)
task_finished(task);
LOCK(&manager->lock);
if (requeue) {
/*
* We know we're awake, so we don't have
* to wakeup any sleeping threads if the
* ready queue is empty before we requeue.
*
* A possible optimization if the queue is
* empty is to 'goto' the 'if (task != NULL)'
* block, avoiding the ENQUEUE of the task
* and the subsequent immediate DEQUEUE
* (since it is the only executable task).
* We don't do this because then we'd be
* skipping the exit_requested check. The
* cost of ENQUEUE is low anyway, especially
* when you consider that we'd have to do
* an extra EMPTY check to see if we could
* do the optimization. If the ready queue
* were usually nonempty, the 'optimization'
* might even hurt rather than help.
*/
ENQUEUE(manager->ready_tasks, task,
ready_link);
}
}
}
UNLOCK(&manager->lock);
XTHREADTRACE("exit");
return ((isc_threadresult_t)0);
}
static void
manager_free(isc_taskmgr_t *manager) {
(void)isc_condition_destroy(&manager->work_available);
(void)isc_mutex_destroy(&manager->lock);
isc_mem_put(manager->mctx, manager->threads,
manager->workers * sizeof (isc_thread_t));
manager->magic = 0;
isc_mem_put(manager->mctx, manager, sizeof *manager);
}
isc_result_t
isc_taskmgr_create(isc_mem_t *mctx, unsigned int workers,
unsigned int default_quantum, isc_taskmgr_t **managerp)
{
unsigned int i, started = 0;
isc_taskmgr_t *manager;
isc_thread_t *threads;
/*
* Create a new task manager.
*/
REQUIRE(workers > 0);
REQUIRE(managerp != NULL && *managerp == NULL);
manager = isc_mem_get(mctx, sizeof *manager);
if (manager == NULL)
return (ISC_R_NOMEMORY);
manager->magic = TASK_MANAGER_MAGIC;
manager->mctx = mctx;
threads = isc_mem_get(mctx, workers * sizeof (isc_thread_t));
if (threads == NULL) {
isc_mem_put(mctx, manager, sizeof *manager);
return (ISC_R_NOMEMORY);
}
manager->threads = threads;
manager->workers = 0;
if (isc_mutex_init(&manager->lock) != ISC_R_SUCCESS) {
isc_mem_put(mctx, threads, workers * sizeof (isc_thread_t));
isc_mem_put(mctx, manager, sizeof *manager);
UNEXPECTED_ERROR(__FILE__, __LINE__,
"isc_mutex_init() failed");
return (ISC_R_UNEXPECTED);
}
if (default_quantum == 0)
default_quantum = DEFAULT_DEFAULT_QUANTUM;
manager->default_quantum = default_quantum;
INIT_LIST(manager->tasks);
INIT_LIST(manager->ready_tasks);
if (isc_condition_init(&manager->work_available) != ISC_R_SUCCESS) {
(void)isc_mutex_destroy(&manager->lock);
isc_mem_put(mctx, threads, workers * sizeof (isc_thread_t));
isc_mem_put(mctx, manager, sizeof *manager);
UNEXPECTED_ERROR(__FILE__, __LINE__,
"isc_condition_init() failed");
return (ISC_R_UNEXPECTED);
}
manager->exiting = ISC_FALSE;
manager->workers = 0;
LOCK(&manager->lock);
/*
* Start workers.
*/
for (i = 0; i < workers; i++) {
if (isc_thread_create(run, manager,
&manager->threads[manager->workers]) ==
ISC_R_SUCCESS) {
manager->workers++;
started++;
}
}
UNLOCK(&manager->lock);
if (started == 0) {
manager_free(manager);
return (ISC_R_NOTHREADS);
}
*managerp = manager;
return (ISC_R_SUCCESS);
}
void
isc_taskmgr_destroy(isc_taskmgr_t **managerp) {
isc_taskmgr_t *manager;
isc_task_t *task;
unsigned int i;
/*
* Destroy '*managerp'.
*/
REQUIRE(managerp != NULL);
manager = *managerp;
REQUIRE(VALID_MANAGER(manager));
XTHREADTRACE("isc_taskmgr_destroy");
/*
* Only one non-worker thread may ever call this routine.
* If a worker thread wants to initiate shutdown of the
* task manager, it should ask some non-worker thread to call
* isc_taskmgr_destroy(), e.g. by signalling a condition variable
* that the startup thread is sleeping on.
*/
/*
* Unlike elsewhere, we're going to hold this lock a long time.
* We need to do so, because otherwise the list of tasks could
* change while we were traversing it.
*
* This is also the only function where we will hold both the
* task manager lock and a task lock at the same time.
*/
LOCK(&manager->lock);
/*
* Make sure we only get called once.
*/
INSIST(!manager->exiting);
manager->exiting = ISC_TRUE;
/*
* Post shutdown event(s) to every task (if they haven't already been
* posted).
*/
for (task = HEAD(manager->tasks);
task != NULL;
task = NEXT(task, link)) {
LOCK(&task->lock);
if (task_shutdown(task))
ENQUEUE(manager->ready_tasks, task, ready_link);
UNLOCK(&task->lock);
}
/*
* Wake up any sleeping workers. This ensures we get work done if
* there's work left to do, and if there are already no tasks left
* it will cause the workers to see manager->exiting.
*/
BROADCAST(&manager->work_available);
UNLOCK(&manager->lock);
/*
* Wait for all the worker threads to exit.
*/
for (i = 0; i < manager->workers; i++)
(void)isc_thread_join(manager->threads[i], NULL);
manager_free(manager);
*managerp = NULL;
}