1998-08-17 22:05:58 +00:00
|
|
|
|
|
|
|
#include <isc/assertions.h>
|
|
|
|
|
1998-08-18 00:47:55 +00:00
|
|
|
#include <isc/thread.h>
|
1998-10-14 18:56:13 +00:00
|
|
|
#include <isc/mutex.h>
|
|
|
|
#include <isc/condition.h>
|
1998-12-04 20:00:26 +00:00
|
|
|
#include <isc/error.h>
|
1998-08-18 00:47:55 +00:00
|
|
|
#include <isc/task.h>
|
1998-08-17 22:05:58 +00:00
|
|
|
|
1998-08-19 21:46:15 +00:00
|
|
|
|
|
|
|
/***
|
|
|
|
*** General Macros.
|
|
|
|
***/
|
1998-08-17 22:05:58 +00:00
|
|
|
|
1998-08-18 08:05:45 +00:00
|
|
|
/*
|
1998-10-22 01:33:20 +00:00
|
|
|
* We use macros instead of calling the routines directly because
|
1998-08-18 19:28:30 +00:00
|
|
|
* the capital letters make the locking stand out.
|
|
|
|
*
|
|
|
|
* We INSIST that they succeed since there's no way for us to continue
|
|
|
|
* if they fail.
|
1998-08-18 08:05:45 +00:00
|
|
|
*/
|
1998-10-22 01:33:20 +00:00
|
|
|
|
|
|
|
#define LOCK(lp) \
|
|
|
|
INSIST(isc_mutex_lock((lp)) == ISC_R_SUCCESS);
|
|
|
|
#define UNLOCK(lp) \
|
|
|
|
INSIST(isc_mutex_unlock((lp)) == ISC_R_SUCCESS);
|
|
|
|
#define BROADCAST(cvp) \
|
|
|
|
INSIST(isc_condition_broadcast((cvp)) == ISC_R_SUCCESS);
|
1998-10-22 19:23:26 +00:00
|
|
|
#define SIGNAL(cvp) \
|
|
|
|
INSIST(isc_condition_signal((cvp)) == ISC_R_SUCCESS);
|
1998-10-22 01:33:20 +00:00
|
|
|
#define WAIT(cvp, lp) \
|
|
|
|
INSIST(isc_condition_wait((cvp), (lp)) == ISC_R_SUCCESS);
|
|
|
|
#define WAITUNTIL(cvp, lp, tp, bp) \
|
|
|
|
INSIST(isc_condition_waituntil((cvp), (lp), (tp), (bp)) == \
|
|
|
|
ISC_R_SUCCESS);
|
1998-08-17 22:05:58 +00:00
|
|
|
|
1998-10-21 02:26:57 +00:00
|
|
|
#ifdef ISC_TASK_TRACE
|
1998-10-23 05:45:26 +00:00
|
|
|
#define XTRACE(m) printf("%s task %p thread %lu\n", (m), \
|
1998-10-22 01:33:20 +00:00
|
|
|
task, isc_thread_self())
|
1998-08-17 23:15:50 +00:00
|
|
|
#else
|
|
|
|
#define XTRACE(m)
|
|
|
|
#endif
|
1998-08-17 22:05:58 +00:00
|
|
|
|
1998-08-18 00:29:57 +00:00
|
|
|
|
|
|
|
/***
|
1998-08-19 21:46:15 +00:00
|
|
|
*** Types.
|
1998-08-18 00:29:57 +00:00
|
|
|
***/
|
|
|
|
|
1998-08-19 21:46:15 +00:00
|
|
|
typedef enum {
|
|
|
|
task_state_idle, task_state_ready, task_state_running,
|
|
|
|
task_state_shutdown
|
|
|
|
} task_state_t;
|
|
|
|
|
|
|
|
#define TASK_MAGIC 0x5441534BU /* TASK. */
|
|
|
|
#define VALID_TASK(t) ((t) != NULL && \
|
|
|
|
(t)->magic == TASK_MAGIC)
|
|
|
|
|
1998-10-21 02:26:57 +00:00
|
|
|
struct isc_task {
|
1998-08-19 21:46:15 +00:00
|
|
|
/* Not locked. */
|
|
|
|
unsigned int magic;
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_taskmgr_t manager;
|
1998-10-22 01:33:20 +00:00
|
|
|
isc_mutex_t lock;
|
1998-08-19 21:46:15 +00:00
|
|
|
/* Locked by task lock. */
|
|
|
|
task_state_t state;
|
|
|
|
unsigned int references;
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_eventlist_t events;
|
1998-08-19 21:46:15 +00:00
|
|
|
unsigned int quantum;
|
1998-10-21 01:13:50 +00:00
|
|
|
isc_boolean_t enqueue_allowed;
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_event_t shutdown_event;
|
1998-08-19 21:46:15 +00:00
|
|
|
/* Locked by task manager lock. */
|
1998-10-21 02:26:57 +00:00
|
|
|
LINK(struct isc_task) link;
|
|
|
|
LINK(struct isc_task) ready_link;
|
1998-08-19 21:46:15 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#define TASK_MANAGER_MAGIC 0x54534B4DU /* TSKM. */
|
|
|
|
#define VALID_MANAGER(m) ((m) != NULL && \
|
|
|
|
(m)->magic == TASK_MANAGER_MAGIC)
|
|
|
|
|
1998-10-21 02:26:57 +00:00
|
|
|
struct isc_taskmgr {
|
1998-08-19 21:46:15 +00:00
|
|
|
/* Not locked. */
|
|
|
|
unsigned int magic;
|
1998-10-21 22:01:08 +00:00
|
|
|
isc_memctx_t mctx;
|
1998-10-22 01:33:20 +00:00
|
|
|
isc_mutex_t lock;
|
1998-10-23 23:50:15 +00:00
|
|
|
unsigned int workers;
|
|
|
|
isc_thread_t * threads;
|
1998-08-19 21:46:15 +00:00
|
|
|
/* Locked by task manager lock. */
|
|
|
|
unsigned int default_quantum;
|
1998-10-21 02:26:57 +00:00
|
|
|
LIST(struct isc_task) tasks;
|
|
|
|
LIST(struct isc_task) ready_tasks;
|
1998-10-22 01:33:20 +00:00
|
|
|
isc_condition_t work_available;
|
1998-10-21 01:13:50 +00:00
|
|
|
isc_boolean_t exiting;
|
1998-08-19 21:46:15 +00:00
|
|
|
};
|
1998-08-18 00:29:57 +00:00
|
|
|
|
1998-08-20 20:48:09 +00:00
|
|
|
#define DEFAULT_DEFAULT_QUANTUM 5
|
|
|
|
#define FINISHED(m) ((m)->exiting && EMPTY((m)->tasks))
|
1998-08-18 00:29:57 +00:00
|
|
|
|
1998-08-19 23:36:12 +00:00
|
|
|
|
|
|
|
/***
|
|
|
|
*** Events.
|
|
|
|
***/
|
|
|
|
|
1998-10-21 02:26:57 +00:00
|
|
|
static inline isc_event_t
|
1998-10-21 22:01:08 +00:00
|
|
|
event_allocate(isc_memctx_t mctx, void *sender, isc_eventtype_t type,
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_taskaction_t action, void *arg, size_t size)
|
1998-08-19 23:36:12 +00:00
|
|
|
{
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_event_t event;
|
1998-08-19 23:36:12 +00:00
|
|
|
|
1998-10-21 22:01:08 +00:00
|
|
|
event = isc_mem_get(mctx, size);
|
1998-08-19 23:36:12 +00:00
|
|
|
if (event == NULL)
|
|
|
|
return (NULL);
|
|
|
|
event->mctx = mctx;
|
|
|
|
event->size = size;
|
1998-10-13 20:22:22 +00:00
|
|
|
event->sender = sender;
|
1998-08-19 23:36:12 +00:00
|
|
|
event->type = type;
|
|
|
|
event->action = action;
|
|
|
|
event->arg = arg;
|
|
|
|
|
|
|
|
return (event);
|
|
|
|
}
|
|
|
|
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_event_t
|
1998-10-21 22:01:08 +00:00
|
|
|
isc_event_allocate(isc_memctx_t mctx, void *sender, isc_eventtype_t type,
|
|
|
|
isc_taskaction_t action, void *arg, size_t size)
|
1998-08-19 23:36:12 +00:00
|
|
|
{
|
1998-10-21 02:26:57 +00:00
|
|
|
if (size < sizeof (struct isc_event))
|
1998-08-19 23:36:12 +00:00
|
|
|
return (NULL);
|
|
|
|
if (type < 0)
|
|
|
|
return (NULL);
|
|
|
|
if (action == NULL)
|
|
|
|
return (NULL);
|
|
|
|
|
1998-10-13 20:22:22 +00:00
|
|
|
return (event_allocate(mctx, sender, type, action, arg, size));
|
1998-08-19 23:36:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_event_free(isc_event_t *eventp) {
|
|
|
|
isc_event_t event;
|
1998-08-19 23:36:12 +00:00
|
|
|
|
|
|
|
REQUIRE(eventp != NULL);
|
|
|
|
event = *eventp;
|
|
|
|
REQUIRE(event != NULL);
|
|
|
|
|
1998-11-03 19:05:12 +00:00
|
|
|
if (event->destroy != NULL)
|
|
|
|
(event->destroy)(event);
|
1998-10-21 22:01:08 +00:00
|
|
|
isc_mem_put(event->mctx, event, event->size);
|
1998-08-19 23:36:12 +00:00
|
|
|
|
|
|
|
*eventp = NULL;
|
|
|
|
}
|
|
|
|
|
1998-08-17 22:05:58 +00:00
|
|
|
/***
|
|
|
|
*** Tasks.
|
|
|
|
***/
|
|
|
|
|
|
|
|
static void
|
1998-10-21 02:26:57 +00:00
|
|
|
task_free(isc_task_t task) {
|
|
|
|
isc_taskmgr_t manager = task->manager;
|
1998-08-17 22:05:58 +00:00
|
|
|
|
1998-08-17 23:15:50 +00:00
|
|
|
XTRACE("free task");
|
1998-08-17 22:05:58 +00:00
|
|
|
REQUIRE(EMPTY(task->events));
|
|
|
|
|
|
|
|
LOCK(&manager->lock);
|
|
|
|
UNLINK(manager->tasks, task, link);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
UNLOCK(&manager->lock);
|
1998-10-22 01:33:20 +00:00
|
|
|
(void)isc_mutex_destroy(&task->lock);
|
1998-08-19 23:36:12 +00:00
|
|
|
if (task->shutdown_event != NULL)
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_event_free(&task->shutdown_event);
|
1998-08-17 22:05:58 +00:00
|
|
|
task->magic = 0;
|
1998-10-21 22:01:08 +00:00
|
|
|
isc_mem_put(manager->mctx, task, sizeof *task);
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
|
|
|
|
1998-10-22 01:33:20 +00:00
|
|
|
isc_result_t
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_task_create(isc_taskmgr_t manager, isc_taskaction_t shutdown_action,
|
|
|
|
void *shutdown_arg, unsigned int quantum, isc_task_t *taskp)
|
1998-08-17 23:15:50 +00:00
|
|
|
{
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_task_t task;
|
1998-08-17 22:05:58 +00:00
|
|
|
|
|
|
|
REQUIRE(VALID_MANAGER(manager));
|
|
|
|
REQUIRE(taskp != NULL && *taskp == NULL);
|
|
|
|
|
1998-10-21 22:01:08 +00:00
|
|
|
task = isc_mem_get(manager->mctx, sizeof *task);
|
1998-08-17 22:05:58 +00:00
|
|
|
if (task == NULL)
|
1998-10-22 01:33:20 +00:00
|
|
|
return (ISC_R_NOMEMORY);
|
1998-08-17 22:05:58 +00:00
|
|
|
|
|
|
|
task->magic = TASK_MAGIC;
|
|
|
|
task->manager = manager;
|
1998-10-22 01:33:20 +00:00
|
|
|
if (isc_mutex_init(&task->lock) != ISC_R_SUCCESS) {
|
1998-10-21 22:01:08 +00:00
|
|
|
isc_mem_put(manager->mctx, task, sizeof *task);
|
1998-10-22 01:33:20 +00:00
|
|
|
UNEXPECTED_ERROR(__FILE__, __LINE__,
|
|
|
|
"isc_mutex_init() failed");
|
|
|
|
return (ISC_R_UNEXPECTED);
|
1998-08-18 19:28:30 +00:00
|
|
|
}
|
1998-08-17 22:05:58 +00:00
|
|
|
task->state = task_state_idle;
|
|
|
|
task->references = 1;
|
|
|
|
INIT_LIST(task->events);
|
|
|
|
task->quantum = quantum;
|
1998-10-21 01:13:50 +00:00
|
|
|
task->enqueue_allowed = ISC_TRUE;
|
1998-08-19 23:36:12 +00:00
|
|
|
task->shutdown_event = event_allocate(manager->mctx,
|
1998-10-13 20:22:22 +00:00
|
|
|
NULL,
|
1998-10-21 02:26:57 +00:00
|
|
|
ISC_TASKEVENT_SHUTDOWN,
|
1998-08-19 23:36:12 +00:00
|
|
|
shutdown_action,
|
|
|
|
shutdown_arg,
|
|
|
|
sizeof *task->shutdown_event);
|
|
|
|
if (task->shutdown_event == NULL) {
|
1998-10-22 01:33:20 +00:00
|
|
|
(void)isc_mutex_destroy(&task->lock);
|
1998-10-21 22:01:08 +00:00
|
|
|
isc_mem_put(manager->mctx, task, sizeof *task);
|
1998-10-22 01:33:20 +00:00
|
|
|
return (ISC_R_NOMEMORY);
|
1998-08-19 23:36:12 +00:00
|
|
|
}
|
1998-08-17 22:05:58 +00:00
|
|
|
INIT_LINK(task, link);
|
|
|
|
INIT_LINK(task, ready_link);
|
|
|
|
|
|
|
|
LOCK(&manager->lock);
|
|
|
|
if (task->quantum == 0)
|
|
|
|
task->quantum = manager->default_quantum;
|
|
|
|
APPEND(manager->tasks, task, link);
|
|
|
|
UNLOCK(&manager->lock);
|
|
|
|
|
|
|
|
*taskp = task;
|
|
|
|
|
1998-10-22 01:33:20 +00:00
|
|
|
return (ISC_R_SUCCESS);
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
|
|
|
|
1998-08-18 19:28:30 +00:00
|
|
|
void
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_task_attach(isc_task_t task, isc_task_t *taskp) {
|
1998-08-17 22:05:58 +00:00
|
|
|
|
|
|
|
REQUIRE(VALID_TASK(task));
|
|
|
|
REQUIRE(taskp != NULL && *taskp == NULL);
|
|
|
|
|
|
|
|
LOCK(&task->lock);
|
|
|
|
task->references++;
|
|
|
|
UNLOCK(&task->lock);
|
|
|
|
|
|
|
|
*taskp = task;
|
|
|
|
}
|
|
|
|
|
1998-08-18 00:29:57 +00:00
|
|
|
void
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_task_detach(isc_task_t *taskp) {
|
1998-10-21 01:13:50 +00:00
|
|
|
isc_boolean_t free_task = ISC_FALSE;
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_task_t task;
|
1998-08-17 22:05:58 +00:00
|
|
|
|
1998-10-21 02:26:57 +00:00
|
|
|
XTRACE("isc_task_detach");
|
1998-08-17 22:05:58 +00:00
|
|
|
|
|
|
|
REQUIRE(taskp != NULL);
|
|
|
|
task = *taskp;
|
|
|
|
REQUIRE(VALID_TASK(task));
|
|
|
|
|
|
|
|
LOCK(&task->lock);
|
|
|
|
REQUIRE(task->references > 0);
|
|
|
|
task->references--;
|
1998-09-16 21:35:32 +00:00
|
|
|
if (task->state == task_state_shutdown && task->references == 0)
|
1998-10-21 01:13:50 +00:00
|
|
|
free_task = ISC_TRUE;
|
1998-08-17 22:05:58 +00:00
|
|
|
UNLOCK(&task->lock);
|
|
|
|
|
|
|
|
if (free_task)
|
|
|
|
task_free(task);
|
|
|
|
|
|
|
|
*taskp = NULL;
|
|
|
|
}
|
|
|
|
|
1998-11-10 11:39:13 +00:00
|
|
|
isc_result_t
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_task_send(isc_task_t task, isc_event_t *eventp) {
|
1998-10-21 01:13:50 +00:00
|
|
|
isc_boolean_t was_idle = ISC_FALSE;
|
|
|
|
isc_boolean_t discard = ISC_FALSE;
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_event_t event;
|
1998-08-17 22:05:58 +00:00
|
|
|
|
|
|
|
REQUIRE(VALID_TASK(task));
|
1998-08-18 19:28:30 +00:00
|
|
|
REQUIRE(eventp != NULL);
|
|
|
|
event = *eventp;
|
1998-08-17 22:05:58 +00:00
|
|
|
REQUIRE(event != NULL);
|
1998-10-13 20:22:22 +00:00
|
|
|
REQUIRE(event->sender != NULL);
|
|
|
|
REQUIRE(event->type > 0);
|
1998-08-17 22:05:58 +00:00
|
|
|
|
1998-08-17 23:15:50 +00:00
|
|
|
XTRACE("sending");
|
1998-08-17 22:05:58 +00:00
|
|
|
/*
|
|
|
|
* 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 a lock is released.
|
|
|
|
*/
|
|
|
|
LOCK(&task->lock);
|
1998-08-19 23:36:12 +00:00
|
|
|
if (task->enqueue_allowed) {
|
1998-08-17 22:05:58 +00:00
|
|
|
if (task->state == task_state_idle) {
|
1998-10-21 01:13:50 +00:00
|
|
|
was_idle = ISC_TRUE;
|
1998-08-17 22:05:58 +00:00
|
|
|
INSIST(EMPTY(task->events));
|
|
|
|
task->state = task_state_ready;
|
|
|
|
}
|
|
|
|
INSIST(task->state == task_state_ready ||
|
|
|
|
task->state == task_state_running);
|
|
|
|
ENQUEUE(task->events, event, link);
|
|
|
|
} else
|
1998-10-21 01:13:50 +00:00
|
|
|
discard = ISC_TRUE;
|
1998-08-17 22:05:58 +00:00
|
|
|
UNLOCK(&task->lock);
|
|
|
|
|
|
|
|
if (discard) {
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_event_free(&event);
|
1998-08-18 19:28:30 +00:00
|
|
|
*eventp = NULL;
|
1998-11-10 20:57:32 +00:00
|
|
|
return (ISC_R_TASKSHUTDOWN);
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (was_idle) {
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_taskmgr_t manager;
|
1998-08-17 22:05:58 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We need to add this task to the ready queue.
|
|
|
|
*
|
|
|
|
* We've waited until now to do it, rather than doing it
|
|
|
|
* while holding the task lock, because we don't want to
|
|
|
|
* block while holding the task lock.
|
|
|
|
*
|
|
|
|
* We've changed the state to ready, so no one else will
|
|
|
|
* be trying to add this task to the ready queue. It
|
|
|
|
* thus doesn't matter if more events have been added to
|
|
|
|
* the queue after we gave up the task lock.
|
|
|
|
*
|
|
|
|
* Shutting down a task requires posting a shutdown event
|
|
|
|
* to the task's queue and then executing it, so there's
|
|
|
|
* no way the task can disappear. A task is always on the
|
|
|
|
* task manager's 'tasks' list, so the task manager can
|
|
|
|
* always post a shutdown event to all tasks if it is
|
|
|
|
* requested to shutdown.
|
|
|
|
*/
|
|
|
|
manager = task->manager;
|
|
|
|
INSIST(VALID_MANAGER(manager));
|
|
|
|
LOCK(&manager->lock);
|
|
|
|
ENQUEUE(manager->ready_tasks, task, ready_link);
|
1998-10-22 19:23:26 +00:00
|
|
|
SIGNAL(&manager->work_available);
|
1998-08-17 22:05:58 +00:00
|
|
|
UNLOCK(&manager->lock);
|
|
|
|
}
|
|
|
|
|
1998-08-18 19:28:30 +00:00
|
|
|
*eventp = NULL;
|
|
|
|
|
1998-08-17 23:15:50 +00:00
|
|
|
XTRACE("sent");
|
1998-11-10 11:39:13 +00:00
|
|
|
|
|
|
|
return (ISC_R_SUCCESS);
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
|
|
|
|
1998-11-06 01:44:44 +00:00
|
|
|
unsigned int
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_task_purge(isc_task_t task, void *sender, isc_eventtype_t type) {
|
|
|
|
isc_event_t event, next_event;
|
|
|
|
isc_eventlist_t purgeable;
|
1998-11-06 01:44:44 +00:00
|
|
|
unsigned int purge_count;
|
1998-10-13 20:22:22 +00:00
|
|
|
|
|
|
|
REQUIRE(VALID_TASK(task));
|
|
|
|
REQUIRE(type >= 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Purge events matching 'sender' and 'type'. sender == NULL means
|
|
|
|
* "any sender". type == NULL means any type. Task manager events
|
|
|
|
* cannot be purged.
|
|
|
|
*/
|
|
|
|
|
|
|
|
INIT_LIST(purgeable);
|
1998-11-06 01:44:44 +00:00
|
|
|
purge_count = 0;
|
1998-10-13 20:22:22 +00:00
|
|
|
|
|
|
|
LOCK(&task->lock);
|
|
|
|
for (event = HEAD(task->events);
|
|
|
|
event != NULL;
|
|
|
|
event = next_event) {
|
|
|
|
next_event = NEXT(event, link);
|
|
|
|
if ((sender == NULL || event->sender == sender) &&
|
|
|
|
((type == 0 && event->type > 0) || event->type == type)) {
|
|
|
|
DEQUEUE(task->events, event, link);
|
|
|
|
ENQUEUE(purgeable, event, link);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
UNLOCK(&task->lock);
|
|
|
|
|
|
|
|
for (event = HEAD(purgeable);
|
|
|
|
event != NULL;
|
|
|
|
event = next_event) {
|
|
|
|
next_event = NEXT(event, link);
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_event_free(&event);
|
1998-11-06 01:44:44 +00:00
|
|
|
purge_count++;
|
1998-10-13 20:22:22 +00:00
|
|
|
}
|
1998-11-06 01:44:44 +00:00
|
|
|
|
|
|
|
return (purge_count);
|
1998-10-13 20:22:22 +00:00
|
|
|
}
|
|
|
|
|
1998-08-18 00:29:57 +00:00
|
|
|
void
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_task_shutdown(isc_task_t task) {
|
1998-10-21 01:13:50 +00:00
|
|
|
isc_boolean_t was_idle = ISC_FALSE;
|
|
|
|
isc_boolean_t discard = ISC_FALSE;
|
1998-08-17 22:05:58 +00:00
|
|
|
|
|
|
|
REQUIRE(VALID_TASK(task));
|
|
|
|
|
|
|
|
/*
|
1998-10-28 01:45:43 +00:00
|
|
|
* This routine is very similar to isc_task_send() above.
|
1998-08-17 22:05:58 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
LOCK(&task->lock);
|
1998-08-19 23:36:12 +00:00
|
|
|
if (task->enqueue_allowed) {
|
1998-08-17 22:05:58 +00:00
|
|
|
if (task->state == task_state_idle) {
|
1998-10-21 01:13:50 +00:00
|
|
|
was_idle = ISC_TRUE;
|
1998-08-17 22:05:58 +00:00
|
|
|
INSIST(EMPTY(task->events));
|
|
|
|
task->state = task_state_ready;
|
|
|
|
}
|
|
|
|
INSIST(task->state == task_state_ready ||
|
|
|
|
task->state == task_state_running);
|
1998-08-19 23:36:12 +00:00
|
|
|
INSIST(task->shutdown_event != NULL);
|
|
|
|
ENQUEUE(task->events, task->shutdown_event, link);
|
|
|
|
task->shutdown_event = NULL;
|
1998-10-21 01:13:50 +00:00
|
|
|
task->enqueue_allowed = ISC_FALSE;
|
1998-08-17 22:05:58 +00:00
|
|
|
} else
|
1998-10-21 01:13:50 +00:00
|
|
|
discard = ISC_TRUE;
|
1998-08-17 22:05:58 +00:00
|
|
|
UNLOCK(&task->lock);
|
|
|
|
|
1998-08-17 23:15:50 +00:00
|
|
|
if (discard)
|
1998-08-18 00:29:57 +00:00
|
|
|
return;
|
1998-08-17 22:05:58 +00:00
|
|
|
|
|
|
|
if (was_idle) {
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_taskmgr_t manager;
|
1998-08-17 22:05:58 +00:00
|
|
|
|
|
|
|
manager = task->manager;
|
|
|
|
INSIST(VALID_MANAGER(manager));
|
|
|
|
LOCK(&manager->lock);
|
|
|
|
ENQUEUE(manager->ready_tasks, task, ready_link);
|
1998-10-22 19:23:26 +00:00
|
|
|
SIGNAL(&manager->work_available);
|
1998-08-17 22:05:58 +00:00
|
|
|
UNLOCK(&manager->lock);
|
|
|
|
}
|
1998-08-18 00:29:57 +00:00
|
|
|
}
|
1998-08-17 22:05:58 +00:00
|
|
|
|
1998-08-18 00:29:57 +00:00
|
|
|
void
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_task_destroy(isc_task_t *taskp) {
|
1998-08-18 00:29:57 +00:00
|
|
|
|
|
|
|
REQUIRE(taskp != NULL);
|
|
|
|
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_task_shutdown(*taskp);
|
|
|
|
isc_task_detach(taskp);
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1998-08-18 00:29:57 +00:00
|
|
|
|
1998-08-17 22:05:58 +00:00
|
|
|
/***
|
|
|
|
*** Task Manager.
|
|
|
|
***/
|
|
|
|
|
1998-10-23 05:45:26 +00:00
|
|
|
static isc_threadresult_t
|
1998-10-23 22:59:02 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
WINAPI
|
|
|
|
#endif
|
1998-10-23 05:45:26 +00:00
|
|
|
run(void *uap) {
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_taskmgr_t manager = uap;
|
|
|
|
isc_task_t task;
|
1998-08-17 22:05:58 +00:00
|
|
|
|
1998-08-17 23:15:50 +00:00
|
|
|
XTRACE("start");
|
1998-08-17 22:05:58 +00:00
|
|
|
|
|
|
|
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
|
1998-10-28 01:45:43 +00:00
|
|
|
* isc_task_send() above, it is safe for us to dequeue
|
1998-08-17 22:05:58 +00:00
|
|
|
* 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)) {
|
1998-08-17 23:15:50 +00:00
|
|
|
XTRACE("wait");
|
1998-08-17 22:05:58 +00:00
|
|
|
WAIT(&manager->work_available, &manager->lock);
|
1998-08-17 23:15:50 +00:00
|
|
|
XTRACE("awake");
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
1998-08-17 23:15:50 +00:00
|
|
|
XTRACE("working");
|
1998-08-17 22:05:58 +00:00
|
|
|
|
|
|
|
task = HEAD(manager->ready_tasks);
|
|
|
|
if (task != NULL) {
|
1998-08-17 23:15:50 +00:00
|
|
|
unsigned int dispatch_count = 0;
|
1998-10-21 01:13:50 +00:00
|
|
|
isc_boolean_t done = ISC_FALSE;
|
|
|
|
isc_boolean_t requeue = ISC_FALSE;
|
|
|
|
isc_boolean_t wants_shutdown;
|
|
|
|
isc_boolean_t is_shutdown;
|
|
|
|
isc_boolean_t free_task = ISC_FALSE;
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_event_t event;
|
|
|
|
isc_eventlist_t remaining_events;
|
1998-10-21 01:13:50 +00:00
|
|
|
isc_boolean_t discard_remaining = ISC_FALSE;
|
1998-08-17 22:05:58 +00:00
|
|
|
|
|
|
|
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);
|
1998-10-13 20:22:22 +00:00
|
|
|
INSIST(task->state == task_state_ready);
|
|
|
|
if (EMPTY(task->events)) {
|
|
|
|
/*
|
|
|
|
* The task became runnable, but all events
|
|
|
|
* in the run queue were subsequently purged.
|
|
|
|
* Put the task to sleep.
|
|
|
|
*/
|
|
|
|
task->state = task_state_idle;
|
1998-10-21 01:13:50 +00:00
|
|
|
done = ISC_TRUE;
|
1998-10-13 20:22:22 +00:00
|
|
|
XTRACE("ready but empty");
|
|
|
|
} else
|
|
|
|
task->state = task_state_running;
|
1998-08-17 22:05:58 +00:00
|
|
|
while (!done) {
|
1998-08-19 23:36:12 +00:00
|
|
|
INSIST(!EMPTY(task->events));
|
|
|
|
event = HEAD(task->events);
|
|
|
|
DEQUEUE(task->events, event, link);
|
1998-08-17 22:05:58 +00:00
|
|
|
UNLOCK(&task->lock);
|
|
|
|
|
1998-10-21 02:26:57 +00:00
|
|
|
if (event->type == ISC_TASKEVENT_SHUTDOWN)
|
1998-10-21 01:13:50 +00:00
|
|
|
is_shutdown = ISC_TRUE;
|
1998-08-19 23:36:12 +00:00
|
|
|
else
|
1998-10-21 01:13:50 +00:00
|
|
|
is_shutdown = ISC_FALSE;
|
1998-08-19 23:36:12 +00:00
|
|
|
|
1998-08-17 22:05:58 +00:00
|
|
|
/*
|
|
|
|
* Execute the event action.
|
|
|
|
*/
|
1998-08-17 23:15:50 +00:00
|
|
|
XTRACE("execute action");
|
1998-08-19 23:36:12 +00:00
|
|
|
if (event->action != NULL)
|
|
|
|
wants_shutdown =
|
|
|
|
(event->action)(task, event);
|
1998-08-17 22:05:58 +00:00
|
|
|
else
|
1998-10-21 01:13:50 +00:00
|
|
|
wants_shutdown = ISC_FALSE;
|
1998-08-17 23:15:50 +00:00
|
|
|
dispatch_count++;
|
1998-08-19 23:36:12 +00:00
|
|
|
|
1998-08-17 22:05:58 +00:00
|
|
|
LOCK(&task->lock);
|
1998-08-19 23:36:12 +00:00
|
|
|
if (wants_shutdown || is_shutdown) {
|
1998-08-17 23:15:50 +00:00
|
|
|
/*
|
1998-08-19 23:36:12 +00:00
|
|
|
* The event action has either
|
|
|
|
* requested shutdown, or the event
|
|
|
|
* we just executed was the shutdown
|
|
|
|
* event.
|
1998-08-17 23:15:50 +00:00
|
|
|
*
|
|
|
|
* Since no more events can be
|
|
|
|
* delivered to the task, we purge
|
|
|
|
* any remaining events (but defer
|
|
|
|
* freeing them until we've released
|
|
|
|
* the lock).
|
|
|
|
*/
|
|
|
|
XTRACE("wants shutdown");
|
1998-08-17 22:05:58 +00:00
|
|
|
if (!EMPTY(task->events)) {
|
|
|
|
remaining_events =
|
|
|
|
task->events;
|
|
|
|
INIT_LIST(task->events);
|
1998-10-21 01:13:50 +00:00
|
|
|
discard_remaining = ISC_TRUE;
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
|
|
|
if (task->references == 0)
|
1998-10-21 01:13:50 +00:00
|
|
|
free_task = ISC_TRUE;
|
1998-08-17 23:15:50 +00:00
|
|
|
task->state = task_state_shutdown;
|
1998-10-21 01:13:50 +00:00
|
|
|
task->enqueue_allowed = ISC_FALSE;
|
|
|
|
done = ISC_TRUE;
|
1998-08-19 23:36:12 +00:00
|
|
|
} else if (EMPTY(task->events)) {
|
1998-08-17 23:15:50 +00:00
|
|
|
/*
|
|
|
|
* Nothing else to do for this task.
|
|
|
|
* Put it to sleep.
|
1998-10-20 00:52:19 +00:00
|
|
|
*
|
|
|
|
* XXX detect tasks with 0 references
|
|
|
|
* and do something about them.
|
1998-08-17 23:15:50 +00:00
|
|
|
*/
|
1998-10-13 20:22:22 +00:00
|
|
|
XTRACE("empty");
|
1998-08-17 22:05:58 +00:00
|
|
|
task->state = task_state_idle;
|
1998-10-21 01:13:50 +00:00
|
|
|
done = ISC_TRUE;
|
1998-08-17 22:05:58 +00:00
|
|
|
} 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.
|
|
|
|
*/
|
1998-10-13 20:22:22 +00:00
|
|
|
XTRACE("quantum");
|
1998-08-17 22:05:58 +00:00
|
|
|
task->state = task_state_ready;
|
1998-10-21 01:13:50 +00:00
|
|
|
requeue = ISC_TRUE;
|
|
|
|
done = ISC_TRUE;
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
UNLOCK(&task->lock);
|
|
|
|
|
|
|
|
if (discard_remaining) {
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_event_t next_event;
|
1998-08-17 22:05:58 +00:00
|
|
|
|
|
|
|
for (event = HEAD(remaining_events);
|
|
|
|
event != NULL;
|
|
|
|
event = next_event) {
|
|
|
|
next_event = NEXT(event, link);
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_event_free(&event);
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (free_task)
|
|
|
|
task_free(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);
|
|
|
|
|
1998-08-17 23:15:50 +00:00
|
|
|
XTRACE("exit");
|
1998-08-17 22:05:58 +00:00
|
|
|
|
1998-10-23 05:45:26 +00:00
|
|
|
return ((isc_threadresult_t)0);
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
1998-10-21 02:26:57 +00:00
|
|
|
manager_free(isc_taskmgr_t manager) {
|
1998-10-22 01:33:20 +00:00
|
|
|
(void)isc_condition_destroy(&manager->work_available);
|
|
|
|
(void)isc_mutex_destroy(&manager->lock);
|
1998-10-23 23:50:15 +00:00
|
|
|
isc_mem_put(manager->mctx, manager->threads,
|
|
|
|
manager->workers * sizeof (isc_thread_t));
|
1998-08-17 22:05:58 +00:00
|
|
|
manager->magic = 0;
|
1998-10-21 22:01:08 +00:00
|
|
|
isc_mem_put(manager->mctx, manager, sizeof *manager);
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
|
|
|
|
1998-10-22 01:33:20 +00:00
|
|
|
isc_result_t
|
1998-10-21 22:01:08 +00:00
|
|
|
isc_taskmgr_create(isc_memctx_t mctx, unsigned int workers,
|
|
|
|
unsigned int default_quantum, isc_taskmgr_t *managerp)
|
1998-08-17 23:15:50 +00:00
|
|
|
{
|
|
|
|
unsigned int i, started = 0;
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_taskmgr_t manager;
|
1998-10-23 23:50:15 +00:00
|
|
|
isc_thread_t *threads;
|
1998-10-22 01:33:20 +00:00
|
|
|
|
|
|
|
REQUIRE(workers > 0);
|
1998-08-17 22:05:58 +00:00
|
|
|
|
1998-10-21 22:01:08 +00:00
|
|
|
manager = isc_mem_get(mctx, sizeof *manager);
|
1998-08-17 22:05:58 +00:00
|
|
|
if (manager == NULL)
|
1998-10-22 01:33:20 +00:00
|
|
|
return (ISC_R_NOMEMORY);
|
1998-08-17 22:05:58 +00:00
|
|
|
manager->magic = TASK_MANAGER_MAGIC;
|
|
|
|
manager->mctx = mctx;
|
1998-10-23 23:50:15 +00:00
|
|
|
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;
|
1998-10-22 01:33:20 +00:00
|
|
|
if (isc_mutex_init(&manager->lock) != ISC_R_SUCCESS) {
|
1998-10-23 23:50:15 +00:00
|
|
|
isc_mem_put(mctx, threads, workers * sizeof (isc_thread_t));
|
1998-10-21 22:01:08 +00:00
|
|
|
isc_mem_put(mctx, manager, sizeof *manager);
|
1998-10-22 01:33:20 +00:00
|
|
|
UNEXPECTED_ERROR(__FILE__, __LINE__,
|
|
|
|
"isc_mutex_init() failed");
|
|
|
|
return (ISC_R_UNEXPECTED);
|
1998-08-18 19:28:30 +00:00
|
|
|
}
|
1998-08-17 22:05:58 +00:00
|
|
|
if (default_quantum == 0)
|
|
|
|
default_quantum = DEFAULT_DEFAULT_QUANTUM;
|
|
|
|
manager->default_quantum = default_quantum;
|
|
|
|
INIT_LIST(manager->tasks);
|
|
|
|
INIT_LIST(manager->ready_tasks);
|
1998-10-22 01:33:20 +00:00
|
|
|
if (isc_condition_init(&manager->work_available) != ISC_R_SUCCESS) {
|
|
|
|
(void)isc_mutex_destroy(&manager->lock);
|
1998-10-23 23:50:15 +00:00
|
|
|
isc_mem_put(mctx, threads, workers * sizeof (isc_thread_t));
|
1998-10-21 22:01:08 +00:00
|
|
|
isc_mem_put(mctx, manager, sizeof *manager);
|
1998-10-22 01:33:20 +00:00
|
|
|
UNEXPECTED_ERROR(__FILE__, __LINE__,
|
|
|
|
"isc_condition_init() failed");
|
|
|
|
return (ISC_R_UNEXPECTED);
|
1998-08-18 19:28:30 +00:00
|
|
|
}
|
1998-10-21 01:13:50 +00:00
|
|
|
manager->exiting = ISC_FALSE;
|
1998-08-17 22:05:58 +00:00
|
|
|
manager->workers = 0;
|
|
|
|
|
|
|
|
LOCK(&manager->lock);
|
|
|
|
/*
|
|
|
|
* Start workers.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < workers; i++) {
|
1998-10-23 23:50:15 +00:00
|
|
|
if (isc_thread_create(run, manager,
|
|
|
|
&manager->threads[manager->workers]) ==
|
1998-10-22 01:33:20 +00:00
|
|
|
ISC_R_SUCCESS) {
|
1998-08-17 22:05:58 +00:00
|
|
|
manager->workers++;
|
|
|
|
started++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
UNLOCK(&manager->lock);
|
|
|
|
|
|
|
|
if (started == 0) {
|
|
|
|
manager_free(manager);
|
1998-10-22 01:33:20 +00:00
|
|
|
return (ISC_R_NOTHREADS);
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
*managerp = manager;
|
|
|
|
|
1998-10-22 01:33:20 +00:00
|
|
|
return (ISC_R_SUCCESS);
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
|
|
|
|
1998-08-19 01:38:06 +00:00
|
|
|
void
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_taskmgr_destroy(isc_taskmgr_t *managerp) {
|
|
|
|
isc_taskmgr_t manager;
|
|
|
|
isc_task_t task;
|
1998-10-23 23:50:15 +00:00
|
|
|
unsigned int i;
|
1998-08-17 22:05:58 +00:00
|
|
|
|
|
|
|
REQUIRE(managerp != NULL);
|
|
|
|
manager = *managerp;
|
|
|
|
REQUIRE(VALID_MANAGER(manager));
|
|
|
|
|
1998-10-21 02:26:57 +00:00
|
|
|
XTRACE("isc_taskmgr_destroy");
|
1998-08-17 22:05:58 +00:00
|
|
|
/*
|
|
|
|
* 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
|
1998-10-21 02:26:57 +00:00
|
|
|
* isc_taskmgr_destroy(), e.g. by signalling a condition variable
|
1998-08-17 22:05:58 +00:00
|
|
|
* 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);
|
1998-10-21 01:13:50 +00:00
|
|
|
manager->exiting = ISC_TRUE;
|
1998-08-17 22:05:58 +00:00
|
|
|
|
|
|
|
/*
|
1998-08-19 23:36:12 +00:00
|
|
|
* Post the shutdown event to every task (if it hasn't already been
|
|
|
|
* posted).
|
1998-08-17 22:05:58 +00:00
|
|
|
*/
|
|
|
|
for (task = HEAD(manager->tasks);
|
|
|
|
task != NULL;
|
|
|
|
task = NEXT(task, link)) {
|
|
|
|
LOCK(&task->lock);
|
1998-08-19 23:36:12 +00:00
|
|
|
if (task->enqueue_allowed) {
|
|
|
|
INSIST(task->shutdown_event != NULL);
|
|
|
|
ENQUEUE(task->events, task->shutdown_event, link);
|
|
|
|
task->shutdown_event = NULL;
|
|
|
|
if (task->state == task_state_idle) {
|
|
|
|
task->state = task_state_ready;
|
|
|
|
ENQUEUE(manager->ready_tasks, task,
|
|
|
|
ready_link);
|
|
|
|
}
|
|
|
|
INSIST(task->state == task_state_ready ||
|
|
|
|
task->state == task_state_running);
|
1998-10-21 01:13:50 +00:00
|
|
|
task->enqueue_allowed = ISC_FALSE;
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
|
|
|
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);
|
1998-10-23 23:50:15 +00:00
|
|
|
UNLOCK(&manager->lock);
|
1998-08-17 22:05:58 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Wait for all the worker threads to exit.
|
|
|
|
*/
|
1998-10-23 23:50:15 +00:00
|
|
|
for (i = 0; i < manager->workers; i++)
|
|
|
|
(void)isc_thread_join(manager->threads[i], NULL);
|
1998-08-17 22:05:58 +00:00
|
|
|
|
|
|
|
manager_free(manager);
|
|
|
|
|
|
|
|
*managerp = NULL;
|
|
|
|
}
|