2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-09-02 23:55:27 +00:00

Shutdown now implicitly occurs if there are no references to a task

and its event queue is empty.
The DONEOK flag is forced to true if there are no references to a task,
its event queue is empty, and it is shutting down.
Add isc_task_unsend() and isc_task_unsendrange().
Add isc_task_sendanddetach().
Event tags are now void *.
Code cleanups.
Various shutdown bug fixes.
Make tracing messages prettier.
This commit is contained in:
Bob Halley
1999-07-10 01:00:05 +00:00
parent 9db3eaed95
commit ad555ae689

View File

@@ -38,12 +38,15 @@
#include "util.h" #include "util.h"
#ifdef ISC_TASK_TRACE #ifdef ISC_TASK_TRACE
#define XTRACE(m) printf("%s task %p thread %lu\n", (m), \ #define XTRACE(m) printf("task %p thread %lu: %s\n", \
task, isc_thread_self()) task, isc_thread_self(), (m))
#define XTHREADTRACE(m) printf("%s thread %lu\n", (m), \ #define XTTRACE(t, m) printf("task %p thread %lu: %s\n", \
isc_thread_self()) (t), isc_thread_self(), (m))
#define XTHREADTRACE(m) printf("thread %lu: %s\n", \
isc_thread_self(), (m))
#else #else
#define XTRACE(m) #define XTRACE(m)
#define XTTRACE(t, m)
#define XTHREADTRACE(m) #define XTHREADTRACE(m)
#endif #endif
@@ -51,6 +54,10 @@
*** Types. *** Types.
***/ ***/
typedef enum {
detach_result_ok, detach_result_finished, detach_result_wasidle
} detach_result_t;
typedef enum { typedef enum {
task_state_idle, task_state_ready, task_state_running, task_state_idle, task_state_ready, task_state_running,
task_state_done task_state_done
@@ -83,8 +90,10 @@ struct isc_task {
#define TASK_F_SHUTTINGDOWN 0x04 #define TASK_F_SHUTTINGDOWN 0x04
#define DONE_FLAGS (TASK_F_DONEOK|TASK_F_SHUTTINGDOWN) #define DONE_FLAGS (TASK_F_DONEOK|TASK_F_SHUTTINGDOWN)
#define TASK_DONE(t) (((t)->flags & DONE_FLAGS) == \ #define TASK_WANTDONE(t) (((t)->flags & DONE_FLAGS) == \
DONE_FLAGS) DONE_FLAGS)
#define TASK_SHUTTINGDOWN(t) (((t)->flags & TASK_F_SHUTTINGDOWN) \
!= 0)
#define TASK_MANAGER_MAGIC 0x54534B4DU /* TSKM. */ #define TASK_MANAGER_MAGIC 0x54534B4DU /* TSKM. */
#define VALID_MANAGER(m) ((m) != NULL && \ #define VALID_MANAGER(m) ((m) != NULL && \
@@ -108,18 +117,20 @@ struct isc_taskmgr {
#define DEFAULT_DEFAULT_QUANTUM 5 #define DEFAULT_DEFAULT_QUANTUM 5
#define FINISHED(m) ((m)->exiting && EMPTY((m)->tasks)) #define FINISHED(m) ((m)->exiting && EMPTY((m)->tasks))
/*** /***
*** Tasks. *** Tasks.
***/ ***/
static void static void
task_free(isc_task_t *task) { task_finished(isc_task_t *task) {
isc_taskmgr_t *manager = task->manager; isc_taskmgr_t *manager = task->manager;
XTRACE("free task");
REQUIRE(EMPTY(task->events)); REQUIRE(EMPTY(task->events));
REQUIRE(EMPTY(task->on_shutdown)); REQUIRE(EMPTY(task->on_shutdown));
REQUIRE(task->references == 0);
REQUIRE(task->state == task_state_done);
XTRACE("task_finished");
LOCK(&manager->lock); LOCK(&manager->lock);
UNLINK(manager->tasks, task, link); UNLINK(manager->tasks, task, link);
@@ -133,6 +144,7 @@ task_free(isc_task_t *task) {
BROADCAST(&manager->work_available); BROADCAST(&manager->work_available);
} }
UNLOCK(&manager->lock); UNLOCK(&manager->lock);
(void)isc_mutex_destroy(&task->lock); (void)isc_mutex_destroy(&task->lock);
task->magic = 0; task->magic = 0;
isc_mem_put(task->mctx, task, sizeof *task); isc_mem_put(task->mctx, task, sizeof *task);
@@ -152,6 +164,7 @@ isc_task_create(isc_taskmgr_t *manager, isc_mem_t *mctx, unsigned int quantum,
task = isc_mem_get(mctx, sizeof *task); task = isc_mem_get(mctx, sizeof *task);
if (task == NULL) if (task == NULL)
return (ISC_R_NOMEMORY); return (ISC_R_NOMEMORY);
XTRACE("create");
task->manager = manager; task->manager = manager;
task->mctx = mctx; task->mctx = mctx;
if (isc_mutex_init(&task->lock) != ISC_R_SUCCESS) { if (isc_mutex_init(&task->lock) != ISC_R_SUCCESS) {
@@ -192,6 +205,8 @@ isc_task_attach(isc_task_t *source, isc_task_t **targetp) {
REQUIRE(VALID_TASK(source)); REQUIRE(VALID_TASK(source));
REQUIRE(targetp != NULL && *targetp == NULL); REQUIRE(targetp != NULL && *targetp == NULL);
XTTRACE(source, "attach");
LOCK(&source->lock); LOCK(&source->lock);
source->references++; source->references++;
UNLOCK(&source->lock); UNLOCK(&source->lock);
@@ -199,10 +214,95 @@ isc_task_attach(isc_task_t *source, isc_task_t **targetp) {
*targetp = source; *targetp = source;
} }
static inline isc_boolean_t
task_shutdown(isc_task_t *task) {
isc_boolean_t was_idle = ISC_FALSE;
isc_event_t *event, *prev;
/*
* Caller must be holding the task's lock.
*/
XTRACE("task_shutdown");
if (! TASK_SHUTTINGDOWN(task)) {
XTRACE("shutting down");
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);
/*
* Note that we post shutdown events LIFO.
*/
for (event = TAIL(task->on_shutdown);
event != NULL;
event = prev) {
prev = PREV(event, link);
DEQUEUE(task->on_shutdown, event, link);
ENQUEUE(task->events, event, link);
}
}
return (was_idle);
}
static inline void
task_ready(isc_task_t *task) {
isc_taskmgr_t *manager = task->manager;
REQUIRE(VALID_MANAGER(manager));
REQUIRE(task->state == task_state_ready);
XTRACE("task_ready");
LOCK(&manager->lock);
ENQUEUE(manager->ready_tasks, task, ready_link);
SIGNAL(&manager->work_available);
UNLOCK(&manager->lock);
}
static inline detach_result_t
task_detach(isc_task_t *task) {
detach_result_t dresult = detach_result_ok;
/*
* Caller must be holding the task lock.
*/
REQUIRE(task->references > 0);
XTRACE("detach");
task->references--;
if (task->references == 0) {
if (task->state == task_state_done)
dresult = detach_result_finished;
else if (task->state == task_state_idle) {
INSIST(EMPTY(task->events));
/*
* There are no references to this task, and no
* pending events. We initiate shutdown, since
* otherwise this task would just sit around until
* the task manager was destroyed.
*/
if (task_shutdown(task))
dresult = detach_result_wasidle;
}
}
return (dresult);
}
void void
isc_task_detach(isc_task_t **taskp) { isc_task_detach(isc_task_t **taskp) {
isc_boolean_t free_task = ISC_FALSE;
isc_task_t *task; isc_task_t *task;
detach_result_t dresult;
/* /*
* Detach *taskp from its task. * Detach *taskp from its task.
@@ -215,21 +315,13 @@ isc_task_detach(isc_task_t **taskp) {
XTRACE("isc_task_detach"); XTRACE("isc_task_detach");
LOCK(&task->lock); LOCK(&task->lock);
REQUIRE(task->references > 0); dresult = task_detach(task);
task->references--;
if (task->state == task_state_done && task->references == 0)
free_task = ISC_TRUE;
/*
* XXXRTH It is currently possible to detach the last
* reference from a task that has not been shutdown. This
* will prevent the task from being shutdown until the
* task manager is destroyed. Should there be an
* automatic shutdown on last detach?
*/
UNLOCK(&task->lock); UNLOCK(&task->lock);
if (free_task) if (dresult == detach_result_finished)
task_free(task); task_finished(task);
else if (dresult == detach_result_wasidle)
task_ready(task);
*taskp = NULL; *taskp = NULL;
} }
@@ -246,152 +338,234 @@ isc_task_mem(isc_task_t *task) {
return (task->mctx); return (task->mctx);
} }
isc_result_t static inline isc_result_t
isc_task_send(isc_task_t *task, isc_event_t **eventp) { task_send(isc_task_t *task, isc_event_t **eventp, isc_boolean_t *was_idlep) {
isc_boolean_t was_idle = ISC_FALSE;
isc_boolean_t disallowed = ISC_FALSE;
isc_result_t result = ISC_R_SUCCESS; isc_result_t result = ISC_R_SUCCESS;
isc_event_t *event; isc_event_t *event;
/* /*
* Send '*event' to 'task'. * Caller must be holding the task lock.
*/ */
REQUIRE(VALID_TASK(task));
REQUIRE(eventp != NULL); REQUIRE(eventp != NULL);
event = *eventp; event = *eventp;
REQUIRE(event != NULL); REQUIRE(event != NULL);
REQUIRE(event->sender != NULL); REQUIRE(event->sender != NULL);
REQUIRE(event->type > 0); REQUIRE(event->type > 0);
XTRACE("sending"); XTRACE("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 a lock is released.
*/
LOCK(&task->lock);
/* /*
* Note: we require that task->state == task_state_done implies * Note: we require that task->state == task_state_done implies
* (task->flags & TASK_F_SENDOK) == 0. * (task->flags & TASK_F_SENDOK) == 0.
*/ */
*was_idlep = ISC_FALSE;
if ((task->flags & TASK_F_SENDOK) != 0) { if ((task->flags & TASK_F_SENDOK) != 0) {
if (task->state == task_state_idle) { if (task->state == task_state_idle) {
was_idle = ISC_TRUE; *was_idlep = ISC_TRUE;
INSIST(EMPTY(task->events)); INSIST(EMPTY(task->events));
task->state = task_state_ready; task->state = task_state_ready;
} }
INSIST(task->state == task_state_ready || INSIST(task->state == task_state_ready ||
task->state == task_state_running); task->state == task_state_running);
ENQUEUE(task->events, event, link); ENQUEUE(task->events, event, link);
*eventp = NULL;
} else { } else {
disallowed = ISC_TRUE;
if (task->state == task_state_done) if (task->state == task_state_done)
result = ISC_R_TASKDONE; result = ISC_R_TASKDONE;
else else
result = ISC_R_TASKNOSEND; result = ISC_R_TASKNOSEND;
} }
return (result);
}
isc_result_t
isc_task_send(isc_task_t *task, isc_event_t **eventp) {
isc_boolean_t was_idle;
isc_result_t result;
/*
* 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);
result = task_send(task, eventp, &was_idle);
UNLOCK(&task->lock); UNLOCK(&task->lock);
if (disallowed) if (result != ISC_R_SUCCESS)
return (result); return (result);
if (was_idle) { if (was_idle) {
isc_taskmgr_t *manager;
/* /*
* We need to add this task to the ready queue. * We need to add this task to the ready queue.
* *
* We've waited until now to do it, rather than doing it * We've waited until now to do it because making a task
* while holding the task lock, because we don't want to * ready requires locking the manager. If we tried to do
* block while holding the task lock. * this while holding the task lock, we could deadlock.
* *
* We've changed the state to ready, so no one else will * We've changed the state to ready, so no one else will
* be trying to add this task to the ready queue. The * be trying to add this task to the ready queue. The
* only way to leave the ready state is by executing the * only way to leave the ready state is by executing the
* task. It thus doesn't matter if events are added, * task. It thus doesn't matter if events are added,
* removed, or shutting_down is started in the interval * removed, or a shutdown is started in the interval
* between the time we released the task lock, and the time * between the time we released the task lock, and the time
* we add the task to the ready queue. * we add the task to the ready queue.
*/ */
manager = task->manager; task_ready(task);
INSIST(VALID_MANAGER(manager));
LOCK(&manager->lock);
ENQUEUE(manager->ready_tasks, task, ready_link);
SIGNAL(&manager->work_available);
UNLOCK(&manager->lock);
} }
*eventp = NULL;
XTRACE("sent");
return (ISC_R_SUCCESS); return (ISC_R_SUCCESS);
} }
unsigned int isc_result_t
isc_task_purgerange(isc_task_t *task, void *sender, isc_eventtype_t first, isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
isc_eventtype_t last, unsigned int tag) isc_boolean_t was_idle;
{ isc_result_t result;
isc_event_t *event, *next_event; isc_task_t *task;
isc_eventlist_t purgeable; detach_result_t dresult = detach_result_ok;
unsigned int purge_count;
/* /*
* Purge events from a task's event queue. * Send '*event' to '*taskp' and then detach '*taskp' from its
* task.
*/ */
REQUIRE(taskp != NULL);
task = *taskp;
REQUIRE(VALID_TASK(task));
XTRACE("isc_task_sendanddetach");
LOCK(&task->lock);
result = task_send(task, eventp, &was_idle);
if (result == ISC_R_SUCCESS)
dresult = task_detach(task);
UNLOCK(&task->lock);
if (result != ISC_R_SUCCESS)
return (result);
if (dresult == detach_result_finished) {
/*
* If was_idle is true, then the task is ready with at least
* one event in the queue, and nothing will happen until
* we call task_ready(). In particular, the task cannot
* be executing or have entered the done state, so if
* dresult is detach_result_finished, was_idle must have been
* false. We INSIST on it.
*/
INSIST(!was_idle);
task_finished(task);
} else {
/*
* If was_idle, then dresult shouldn't be
* detach_result_wasidle, since that would mean someone else
* changed the task's state from ready back to idle, which
* should never happen. We INSIST on it.
*/
INSIST(!(was_idle && dresult == detach_result_wasidle));
if (was_idle || dresult == detach_result_wasidle)
task_ready(task);
}
*taskp = NULL;
return (ISC_R_SUCCESS);
}
#define PURGE_OK(event) (((event)->attributes & ISC_EVENTATTR_NOPURGE) == 0)
static unsigned int
dequeue_events(isc_task_t *task, void *sender, isc_eventtype_t first,
isc_eventtype_t last, void *tag,
isc_eventlist_t *events, isc_boolean_t purging)
{
isc_event_t *event, *next_event;
unsigned int count = 0;
REQUIRE(VALID_TASK(task)); REQUIRE(VALID_TASK(task));
REQUIRE(last >= first); REQUIRE(last >= first);
XTRACE("dequeue_events");
XTRACE("purgerange");
/* /*
* Events matching 'sender' and whose type is >= first and * Events matching 'sender', whose type is >= first and <= last, and
* <= last will be purged, unless they are marked as unpurgable. * whose tag is 'tag' will be dequeued. If 'purging', matching events
* sender == NULL means "any sender". * which are marked as unpurgable will not be dequeued.
* *
* Purging never changes the state of the task. * sender == NULL means "any sender", and tag == NULL means "any tag".
*/ */
INIT_LIST(purgeable);
purge_count = 0;
LOCK(&task->lock); LOCK(&task->lock);
for (event = HEAD(task->events);
event != NULL; for (event = HEAD(task->events); event != NULL; event = next_event) {
event = next_event) {
next_event = NEXT(event, link); next_event = NEXT(event, link);
if (event->type >= first && event->type <= last && if (event->type >= first && event->type <= last &&
(sender == NULL || event->sender == sender) && (sender == NULL || event->sender == sender) &&
(tag == 0 || event->tag == tag) && (tag == NULL || event->tag == tag) &&
(event->attributes & ISC_EVENTATTR_NOPURGE) == 0) { (!purging || PURGE_OK(event))) {
DEQUEUE(task->events, event, link); DEQUEUE(task->events, event, link);
ENQUEUE(purgeable, event, link); ENQUEUE(*events, event, link);
count++;
} }
} }
UNLOCK(&task->lock); UNLOCK(&task->lock);
for (event = HEAD(purgeable); return (count);
event != NULL; }
event = next_event) {
unsigned int
isc_task_purgerange(isc_task_t *task, void *sender, isc_eventtype_t first,
isc_eventtype_t last, void *tag)
{
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) {
next_event = NEXT(event, link); next_event = NEXT(event, link);
isc_event_free(&event); isc_event_free(&event);
purge_count++;
} }
return (purge_count); /*
* Note that purging never changes the state of the task.
*/
return (count);
} }
unsigned int unsigned int
isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type, isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
unsigned int tag) void *tag)
{ {
/* /*
* Purge events from a task's event queue. * Purge events from a task's event queue.
*/ */
XTRACE("isc_task_purge");
return (isc_task_purgerange(task, sender, type, type, tag)); return (isc_task_purgerange(task, sender, type, type, tag));
} }
@@ -401,6 +575,8 @@ isc_task_purgeevent(isc_task_t *task, isc_event_t *event) {
/* /*
* Purge 'event' from a task's event queue. * Purge 'event' from a task's event queue.
*
* XXXRTH: WARNING: This method may be removed before beta.
*/ */
REQUIRE(VALID_TASK(task)); REQUIRE(VALID_TASK(task));
@@ -420,8 +596,7 @@ isc_task_purgeevent(isc_task_t *task, isc_event_t *event) {
curr_event != NULL; curr_event != NULL;
curr_event = next_event) { curr_event = next_event) {
next_event = NEXT(curr_event, link); next_event = NEXT(curr_event, link);
if (curr_event == event && if (curr_event == event && PURGE_OK(event)) {
(event->attributes & ISC_EVENTATTR_NOPURGE) == 0) {
DEQUEUE(task->events, curr_event, link); DEQUEUE(task->events, curr_event, link);
break; break;
} }
@@ -436,12 +611,43 @@ isc_task_purgeevent(isc_task_t *task, isc_event_t *event) {
return (ISC_TRUE); 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_result_t
isc_task_allowsend(isc_task_t *task, isc_boolean_t allowed) { isc_task_allowsend(isc_task_t *task, isc_boolean_t allowed) {
isc_result_t result = ISC_R_SUCCESS; isc_result_t result = ISC_R_SUCCESS;
/* /*
* Allow or disallow sending events to 'task'. * Allow or disallow sending events to 'task'.
*
* XXXRTH: WARNING: This method may be removed before beta.
*/ */
REQUIRE(VALID_TASK(task)); REQUIRE(VALID_TASK(task));
@@ -463,6 +669,7 @@ isc_task_allowsend(isc_task_t *task, isc_boolean_t allowed) {
isc_result_t isc_result_t
isc_task_allowdone(isc_task_t *task, isc_boolean_t allowed) { isc_task_allowdone(isc_task_t *task, isc_boolean_t allowed) {
isc_result_t result = ISC_R_SUCCESS; isc_result_t result = ISC_R_SUCCESS;
isc_boolean_t was_idle = ISC_FALSE;
/* /*
* Allow or disallow automatic termination of 'task'. * Allow or disallow automatic termination of 'task'.
@@ -473,19 +680,28 @@ isc_task_allowdone(isc_task_t *task, isc_boolean_t allowed) {
LOCK(&task->lock); LOCK(&task->lock);
if (task->state == task_state_done) if (task->state == task_state_done)
result = ISC_R_TASKDONE; result = ISC_R_TASKDONE;
else if (allowed && else {
(task->flags & TASK_F_SHUTTINGDOWN) != 0 && if (allowed) {
task->state == task_state_idle) {
task->flags &= ~TASK_F_SENDOK;
task->state = task_state_done;
} else {
if (allowed)
task->flags |= TASK_F_DONEOK; task->flags |= TASK_F_DONEOK;
else /*
* 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; task->flags &= ~TASK_F_DONEOK;
} }
UNLOCK(&task->lock); UNLOCK(&task->lock);
if (was_idle)
task_ready(task);
return (result); return (result);
} }
@@ -516,7 +732,7 @@ isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, void *arg) {
if (task->state == task_state_done) { if (task->state == task_state_done) {
disallowed = ISC_TRUE; disallowed = ISC_TRUE;
result = ISC_R_TASKDONE; result = ISC_R_TASKDONE;
} else if ((task->flags & TASK_F_SHUTTINGDOWN) != 0) { } else if (TASK_SHUTTINGDOWN(task)) {
disallowed = ISC_TRUE; disallowed = ISC_TRUE;
result = ISC_R_TASKSHUTTINGDOWN; result = ISC_R_TASKSHUTTINGDOWN;
} else } else
@@ -531,9 +747,7 @@ isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, void *arg) {
void void
isc_task_shutdown(isc_task_t *task) { isc_task_shutdown(isc_task_t *task) {
isc_boolean_t was_idle = ISC_FALSE; isc_boolean_t was_idle;
isc_boolean_t queued_something = ISC_FALSE;
isc_event_t *event, *prev;
/* /*
* Shutdown 'task'. * Shutdown 'task'.
@@ -541,52 +755,12 @@ isc_task_shutdown(isc_task_t *task) {
REQUIRE(VALID_TASK(task)); REQUIRE(VALID_TASK(task));
/*
* This routine is very similar to isc_task_send() above.
*/
LOCK(&task->lock); LOCK(&task->lock);
if ((task->flags & TASK_F_SHUTTINGDOWN) == 0) { was_idle = task_shutdown(task);
XTRACE("shutting down");
task->flags |= TASK_F_SHUTTINGDOWN;
if (task->state == task_state_idle) {
was_idle = ISC_TRUE;
INSIST(EMPTY(task->events));
if (EMPTY(task->on_shutdown) && TASK_DONE(task)) {
task->flags &= ~TASK_F_SENDOK;
task->state = task_state_done;
} else
task->state = task_state_ready;
}
INSIST(task->state == task_state_ready ||
task->state == task_state_running ||
task->state == task_state_done);
if (task->state != task_state_done) {
/*
* Note that we post shutdown events LIFO.
*/
for (event = TAIL(task->on_shutdown);
event != NULL;
event = prev) {
prev = PREV(event, link);
DEQUEUE(task->on_shutdown, event, link);
ENQUEUE(task->events, event, link);
queued_something = ISC_TRUE;
}
}
}
UNLOCK(&task->lock); UNLOCK(&task->lock);
if (was_idle && queued_something) { if (was_idle)
isc_taskmgr_t *manager; task_ready(task);
manager = task->manager;
INSIST(VALID_MANAGER(manager));
LOCK(&manager->lock);
ENQUEUE(manager->ready_tasks, task, ready_link);
SIGNAL(&manager->work_available);
UNLOCK(&manager->lock);
}
} }
void void
@@ -691,7 +865,7 @@ run(void *uap) {
unsigned int dispatch_count = 0; unsigned int dispatch_count = 0;
isc_boolean_t done = ISC_FALSE; isc_boolean_t done = ISC_FALSE;
isc_boolean_t requeue = ISC_FALSE; isc_boolean_t requeue = ISC_FALSE;
isc_boolean_t free_task = ISC_FALSE; isc_boolean_t finished = ISC_FALSE;
isc_event_t *event; isc_event_t *event;
INSIST(VALID_TASK(task)); INSIST(VALID_TASK(task));
@@ -706,33 +880,42 @@ run(void *uap) {
LOCK(&task->lock); LOCK(&task->lock);
INSIST(task->state == task_state_ready); INSIST(task->state == task_state_ready);
if (EMPTY(task->events)) { task->state = task_state_running;
/* XTRACE("running");
* The task became runnable, but all events do {
* in the run queue were subsequently purged. if (!EMPTY(task->events)) {
* Put the task to sleep. event = HEAD(task->events);
*/ DEQUEUE(task->events, event, link);
task->state = task_state_idle;
done = ISC_TRUE;
XTRACE("ready but empty");
} else
task->state = task_state_running;
while (!done) {
INSIST(!EMPTY(task->events));
event = HEAD(task->events);
DEQUEUE(task->events, event, link);
/* /*
* Execute the event action. * Execute the event action.
*/ */
XTRACE("execute action"); XTRACE("execute action");
if (event->action != NULL) { if (event->action != NULL) {
UNLOCK(&task->lock); UNLOCK(&task->lock);
(event->action)(task, event); (event->action)(task, event);
LOCK(&task->lock); LOCK(&task->lock);
}
dispatch_count++;
} }
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)) { if (EMPTY(task->events)) {
/* /*
* Nothing else to do for this task * Nothing else to do for this task
@@ -741,10 +924,10 @@ run(void *uap) {
* put it to sleep. * put it to sleep.
*/ */
XTRACE("empty"); XTRACE("empty");
if (TASK_DONE(task)) { if (TASK_WANTDONE(task)) {
XTRACE("done"); XTRACE("done");
if (task->references == 0) if (task->references == 0)
free_task = ISC_TRUE; finished = ISC_TRUE;
task->flags &= task->flags &=
~TASK_F_SENDOK; ~TASK_F_SENDOK;
task->state = task_state_done; task->state = task_state_done;
@@ -767,11 +950,11 @@ run(void *uap) {
requeue = ISC_TRUE; requeue = ISC_TRUE;
done = ISC_TRUE; done = ISC_TRUE;
} }
} } while (!done);
UNLOCK(&task->lock); UNLOCK(&task->lock);
if (free_task) if (finished)
task_free(task); task_finished(task);
LOCK(&manager->lock); LOCK(&manager->lock);
if (requeue) { if (requeue) {
@@ -894,7 +1077,6 @@ void
isc_taskmgr_destroy(isc_taskmgr_t **managerp) { isc_taskmgr_destroy(isc_taskmgr_t **managerp) {
isc_taskmgr_t *manager; isc_taskmgr_t *manager;
isc_task_t *task; isc_task_t *task;
isc_event_t *event, *prev;
unsigned int i; unsigned int i;
/* /*
@@ -939,37 +1121,8 @@ isc_taskmgr_destroy(isc_taskmgr_t **managerp) {
task != NULL; task != NULL;
task = NEXT(task, link)) { task = NEXT(task, link)) {
LOCK(&task->lock); LOCK(&task->lock);
if ((task->flags & TASK_F_SHUTTINGDOWN) == 0) { if (task_shutdown(task))
task->flags |= TASK_F_SHUTTINGDOWN; ENQUEUE(manager->ready_tasks, task, ready_link);
if (task->state == task_state_idle) {
INSIST(EMPTY(task->events));
if (EMPTY(task->on_shutdown) &&
TASK_DONE(task)) {
task->flags &= ~TASK_F_SENDOK;
task->state = task_state_done;
} else {
task->state = task_state_ready;
ENQUEUE(manager->ready_tasks, task,
ready_link);
}
}
INSIST(task->state == task_state_ready ||
task->state == task_state_running ||
task->state == task_state_done);
if (task->state != task_state_done) {
/*
* Note that we post shutdown events LIFO.
*/
for (event = TAIL(task->on_shutdown);
event != NULL;
event = prev) {
prev = PREV(event, link);
DEQUEUE(task->on_shutdown, event,
link);
ENQUEUE(task->events, event, link);
}
}
}
UNLOCK(&task->lock); UNLOCK(&task->lock);
} }