2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-09-02 07:35:26 +00:00

Shutdown of a task now works as follows:

When isc_task_shutdown() is called, any shutdown events are posted
	and the task goes into shutting down state.  isc_task_onshutdown()
	may no longer be called.

	If the task allows transition to the done state (the default), then
	as soon as the task's event queue is empty the task will enter the
	done state.  Once the done state has been entered, events may no
	longer be posted (regardless of the allowsend state).  If transition
	to the done state is not allowed, then the task will continue to
	exist and be able to receive events.

Moved event support to event.[ch].
The final state of a task is now called "done" not "shutdown".
Created a flags variable instead of having separate booleans.
Added isc_task_allowdone() and isc_task_purgerange().
Minor tracing fixes.
This commit is contained in:
Bob Halley
1999-05-10 23:00:30 +00:00
parent 853befffcf
commit 4f39360a7f
2 changed files with 154 additions and 197 deletions

View File

@@ -52,66 +52,17 @@
*** Imports. *** Imports.
***/ ***/
#include <stddef.h>
#include <isc/lang.h> #include <isc/lang.h>
#include <isc/list.h> #include <isc/types.h>
#include <isc/eventclass.h>
#include <isc/mem.h> #include <isc/mem.h>
#include <isc/result.h> #include <isc/result.h>
ISC_LANG_BEGINDECLS ISC_LANG_BEGINDECLS
/*** #define ISC_TASKEVENT_FIRSTEVENT (ISC_EVENTCLASS_TASK + 0)
*** Core Types. #define ISC_TASKEVENT_SHUTDOWN (ISC_EVENTCLASS_TASK + 1)
***/ #define ISC_TASKEVENT_LASTEVENT (ISC_EVENTCLASS_TASK + 65535)
typedef struct isc_event isc_event_t;
typedef struct isc_task isc_task_t;
typedef struct isc_taskmgr isc_taskmgr_t;
/*****
***** Events.
*****/
/*
* Negative event types are reserved for use by the task manager.
*
* Type 0 means "any type".
*/
typedef int isc_eventtype_t;
typedef void (*isc_taskaction_t)(isc_task_t *, isc_event_t *);
typedef void (*isc_eventdestructor_t)(isc_event_t *);
/*
* This structure is public because "subclassing" it may be useful when
* defining new event types.
*/
struct isc_event {
isc_mem_t * mctx;
size_t size;
void * sender;
isc_eventtype_t type;
isc_taskaction_t action;
void * arg;
isc_eventdestructor_t destroy;
ISC_LINK(struct isc_event) link;
};
#define ISC_TASKEVENT_ANYEVENT 0
#define ISC_TASKEVENT_SHUTDOWN (-1)
typedef ISC_LIST(struct isc_event) isc_eventlist_t;
isc_event_t * isc_event_allocate(isc_mem_t *,
void *,
isc_eventtype_t,
isc_taskaction_t,
void *arg,
size_t);
void isc_event_free(isc_event_t **);
/***** /*****
***** Tasks. ***** Tasks.
@@ -129,8 +80,14 @@ isc_result_t isc_task_send(isc_task_t *,
isc_event_t **); isc_event_t **);
unsigned int isc_task_purge(isc_task_t *, void *, unsigned int isc_task_purge(isc_task_t *, void *,
isc_eventtype_t); isc_eventtype_t);
unsigned int isc_task_purgerange(isc_task_t *,
void *,
isc_eventtype_t,
isc_eventtype_t);
isc_result_t isc_task_allowsend(isc_task_t *, isc_result_t isc_task_allowsend(isc_task_t *,
isc_boolean_t); isc_boolean_t);
isc_result_t isc_task_allowdone(isc_task_t *,
isc_boolean_t);
isc_result_t isc_task_onshutdown(isc_task_t *, isc_result_t isc_task_onshutdown(isc_task_t *,
isc_taskaction_t, isc_taskaction_t,
void *); void *);

View File

@@ -27,6 +27,7 @@
#include <isc/mutex.h> #include <isc/mutex.h>
#include <isc/condition.h> #include <isc/condition.h>
#include <isc/error.h> #include <isc/error.h>
#include <isc/event.h>
#include <isc/task.h> #include <isc/task.h>
#include "util.h" #include "util.h"
@@ -34,8 +35,11 @@
#ifdef ISC_TASK_TRACE #ifdef ISC_TASK_TRACE
#define XTRACE(m) printf("%s task %p thread %lu\n", (m), \ #define XTRACE(m) printf("%s task %p thread %lu\n", (m), \
task, isc_thread_self()) task, isc_thread_self())
#define XTHREADTRACE(m) printf("%s thread %lu\n", (m), \
isc_thread_self())
#else #else
#define XTRACE(m) #define XTRACE(m)
#define XTHREADTRACE(m)
#endif #endif
@@ -45,7 +49,7 @@
typedef enum { typedef enum {
task_state_idle, task_state_ready, task_state_running, task_state_idle, task_state_ready, task_state_running,
task_state_shutdown task_state_done
} task_state_t; } task_state_t;
#define TASK_MAGIC 0x5441534BU /* TASK. */ #define TASK_MAGIC 0x5441534BU /* TASK. */
@@ -64,13 +68,20 @@ struct isc_task {
isc_eventlist_t events; isc_eventlist_t events;
isc_eventlist_t on_shutdown; isc_eventlist_t on_shutdown;
unsigned int quantum; unsigned int quantum;
isc_boolean_t enqueue_allowed; unsigned int flags;
isc_boolean_t shutting_down;
/* Locked by task manager lock. */ /* Locked by task manager lock. */
LINK(isc_task_t) link; LINK(isc_task_t) link;
LINK(isc_task_t) ready_link; LINK(isc_task_t) ready_link;
}; };
#define TASK_F_DONEOK 0x01
#define TASK_F_SENDOK 0x02
#define TASK_F_SHUTTINGDOWN 0x04
#define DONE_FLAGS (TASK_F_DONEOK|TASK_F_SHUTTINGDOWN)
#define TASK_DONE(t) (((t)->flags & DONE_FLAGS) == \
DONE_FLAGS)
#define TASK_MANAGER_MAGIC 0x54534B4DU /* TSKM. */ #define TASK_MANAGER_MAGIC 0x54534B4DU /* TSKM. */
#define VALID_MANAGER(m) ((m) != NULL && \ #define VALID_MANAGER(m) ((m) != NULL && \
(m)->magic == TASK_MANAGER_MAGIC) (m)->magic == TASK_MANAGER_MAGIC)
@@ -94,60 +105,6 @@ struct isc_taskmgr {
#define FINISHED(m) ((m)->exiting && EMPTY((m)->tasks)) #define FINISHED(m) ((m)->exiting && EMPTY((m)->tasks))
/***
*** Events.
***/
static inline isc_event_t *
event_allocate(isc_mem_t *mctx, void *sender, isc_eventtype_t type,
isc_taskaction_t action, void *arg, size_t size)
{
isc_event_t *event;
event = isc_mem_get(mctx, size);
if (event == NULL)
return (NULL);
event->mctx = mctx;
event->size = size;
event->sender = sender;
event->type = type;
event->action = action;
event->arg = arg;
event->destroy = NULL;
INIT_LINK(event, link);
return (event);
}
isc_event_t *
isc_event_allocate(isc_mem_t *mctx, void *sender, isc_eventtype_t type,
isc_taskaction_t action, void *arg, size_t size)
{
if (size < sizeof (struct isc_event))
return (NULL);
if (type < 0)
return (NULL);
if (action == NULL)
return (NULL);
return (event_allocate(mctx, sender, type, action, arg, size));
}
void
isc_event_free(isc_event_t **eventp) {
isc_event_t *event;
REQUIRE(eventp != NULL);
event = *eventp;
REQUIRE(event != NULL);
if (event->destroy != NULL)
(event->destroy)(event);
isc_mem_put(event->mctx, event, event->size);
*eventp = NULL;
}
/*** /***
*** Tasks. *** Tasks.
***/ ***/
@@ -204,8 +161,7 @@ isc_task_create(isc_taskmgr_t *manager, isc_mem_t *mctx, unsigned int quantum,
INIT_LIST(task->events); INIT_LIST(task->events);
INIT_LIST(task->on_shutdown); INIT_LIST(task->on_shutdown);
task->quantum = quantum; task->quantum = quantum;
task->enqueue_allowed = ISC_TRUE; task->flags = (TASK_F_DONEOK|TASK_F_SENDOK);
task->shutting_down = ISC_FALSE;
INIT_LINK(task, link); INIT_LINK(task, link);
INIT_LINK(task, ready_link); INIT_LINK(task, ready_link);
@@ -240,16 +196,16 @@ isc_task_detach(isc_task_t **taskp) {
isc_boolean_t free_task = ISC_FALSE; isc_boolean_t free_task = ISC_FALSE;
isc_task_t *task; isc_task_t *task;
XTRACE("isc_task_detach");
REQUIRE(taskp != NULL); REQUIRE(taskp != NULL);
task = *taskp; task = *taskp;
REQUIRE(VALID_TASK(task)); REQUIRE(VALID_TASK(task));
XTRACE("isc_task_detach");
LOCK(&task->lock); LOCK(&task->lock);
REQUIRE(task->references > 0); REQUIRE(task->references > 0);
task->references--; task->references--;
if (task->state == task_state_shutdown && task->references == 0) if (task->state == task_state_done && task->references == 0)
free_task = ISC_TRUE; free_task = ISC_TRUE;
UNLOCK(&task->lock); UNLOCK(&task->lock);
@@ -289,10 +245,10 @@ isc_task_send(isc_task_t *task, isc_event_t **eventp) {
*/ */
LOCK(&task->lock); LOCK(&task->lock);
/* /*
* Note: we require that task->shutting_down implies * Note: we require that task->state == task_state_done implies
* !task->enqueue_allowed. * (task->flags & TASK_F_SENDOK) == 0.
*/ */
if (task->enqueue_allowed) { if ((task->flags & TASK_F_SENDOK) != 0) {
if (task->state == task_state_idle) { if (task->state == task_state_idle) {
was_idle = ISC_TRUE; was_idle = ISC_TRUE;
INSIST(EMPTY(task->events)); INSIST(EMPTY(task->events));
@@ -303,10 +259,8 @@ isc_task_send(isc_task_t *task, isc_event_t **eventp) {
ENQUEUE(task->events, event, link); ENQUEUE(task->events, event, link);
} else { } else {
disallowed = ISC_TRUE; disallowed = ISC_TRUE;
if (task->state == task_state_shutdown) if (task->state == task_state_done)
result = ISC_R_TASKSHUTDOWN; result = ISC_R_TASKDONE;
else if (task->shutting_down)
result = ISC_R_TASKSHUTTINGDOWN;
else else
result = ISC_R_TASKNOSEND; result = ISC_R_TASKNOSEND;
} }
@@ -326,16 +280,12 @@ isc_task_send(isc_task_t *task, isc_event_t **eventp) {
* block while holding the task lock. * block while holding the task lock.
* *
* 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. It * be trying to add this task to the ready queue. The
* thus doesn't matter if more events have been added to * only way to leave the ready state is by executing the
* the queue after we gave up the task lock. * task. It thus doesn't matter if events are added,
* * removed, or shutting_down is started in the interval
* Shutting down a task requires posting a shutdown event * between the time we released the task lock, and the time
* to the task's queue and then executing it, so there's * we add the task to the ready queue.
* 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; manager = task->manager;
INSIST(VALID_MANAGER(manager)); INSIST(VALID_MANAGER(manager));
@@ -354,17 +304,22 @@ isc_task_send(isc_task_t *task, isc_event_t **eventp) {
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) {
return (isc_task_purgerange(task, sender, type, type));
}
unsigned int
isc_task_purgerange(isc_task_t *task, void *sender, isc_eventtype_t first,
isc_eventtype_t last)
{
isc_event_t *event, *next_event; isc_event_t *event, *next_event;
isc_eventlist_t purgeable; isc_eventlist_t purgeable;
unsigned int purge_count; unsigned int purge_count;
REQUIRE(VALID_TASK(task)); REQUIRE(VALID_TASK(task));
REQUIRE(type >= 0);
/* /*
* Purge events matching 'sender' and 'type'. sender == NULL means * Purge events matching 'sender' and whose type is >= first and
* "any sender". type == NULL means any type. Task manager events * <= last. sender == NULL means "any sender".
* cannot be purged.
* *
* Purging never changes the state of the task. * Purging never changes the state of the task.
*/ */
@@ -378,7 +333,7 @@ isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type) {
event = next_event) { event = next_event) {
next_event = NEXT(event, link); next_event = NEXT(event, link);
if ((sender == NULL || event->sender == sender) && if ((sender == NULL || event->sender == sender) &&
((type == 0 && event->type > 0) || event->type == type)) { event->type >= first && event->type <= last) {
DEQUEUE(task->events, event, link); DEQUEUE(task->events, event, link);
ENQUEUE(purgeable, event, link); ENQUEUE(purgeable, event, link);
} }
@@ -397,18 +352,45 @@ isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type) {
} }
isc_result_t isc_result_t
isc_task_allowsend(isc_task_t *task, isc_boolean_t enqueue_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;
REQUIRE(VALID_TASK(task)); REQUIRE(VALID_TASK(task));
LOCK(&task->lock); LOCK(&task->lock);
if (task->state == task_state_shutdown) if (task->state == task_state_done)
result = ISC_R_TASKSHUTDOWN; result = ISC_R_TASKDONE;
else if (task->shutting_down) else {
result = ISC_R_TASKSHUTTINGDOWN; if (allowed)
else task->flags |= TASK_F_SENDOK;
task->enqueue_allowed = enqueue_allowed; 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;
REQUIRE(VALID_TASK(task));
LOCK(&task->lock);
if (task->state == task_state_done)
result = ISC_R_TASKDONE;
else if (allowed &&
(task->flags & TASK_F_SHUTTINGDOWN) != 0 &&
task->state == task_state_idle) {
task->flags &= ~TASK_F_SENDOK;
task->state = task_state_done;
} else {
if (allowed)
task->flags |= TASK_F_DONEOK;
else
task->flags &= ~TASK_F_DONEOK;
}
UNLOCK(&task->lock); UNLOCK(&task->lock);
return (result); return (result);
@@ -422,20 +404,20 @@ isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, void *arg) {
REQUIRE(VALID_TASK(task)); REQUIRE(VALID_TASK(task));
event = event_allocate(task->mctx, event = isc_event_allocate(task->mctx,
NULL, NULL,
ISC_TASKEVENT_SHUTDOWN, ISC_TASKEVENT_SHUTDOWN,
action, action,
arg, arg,
sizeof *event); sizeof *event);
if (event == NULL) if (event == NULL)
return (ISC_R_NOMEMORY); return (ISC_R_NOMEMORY);
LOCK(&task->lock); LOCK(&task->lock);
if (task->state == task_state_shutdown) { if (task->state == task_state_done) {
disallowed = ISC_TRUE; disallowed = ISC_TRUE;
result = ISC_R_TASKSHUTDOWN; result = ISC_R_TASKDONE;
} else if (task->shutting_down) { } else if ((task->flags & TASK_F_SHUTTINGDOWN) != 0) {
disallowed = ISC_TRUE; disallowed = ISC_TRUE;
result = ISC_R_TASKSHUTTINGDOWN; result = ISC_R_TASKSHUTTINGDOWN;
} else } else
@@ -461,27 +443,34 @@ isc_task_shutdown(isc_task_t *task) {
*/ */
LOCK(&task->lock); LOCK(&task->lock);
if (!task->shutting_down) { if ((task->flags & TASK_F_SHUTTINGDOWN) == 0) {
XTRACE("shutting down");
task->flags |= TASK_F_SHUTTINGDOWN;
if (task->state == task_state_idle) { if (task->state == task_state_idle) {
was_idle = ISC_TRUE; was_idle = ISC_TRUE;
INSIST(EMPTY(task->events)); INSIST(EMPTY(task->events));
task->state = task_state_ready; 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 || INSIST(task->state == task_state_ready ||
task->state == task_state_running); task->state == task_state_running ||
/* task->state == task_state_done);
* Note that we post shutdown events LIFO. if (task->state != task_state_done) {
*/ /*
for (event = TAIL(task->on_shutdown); * Note that we post shutdown events LIFO.
event != NULL; */
event = prev) { for (event = TAIL(task->on_shutdown);
prev = PREV(event, link); event != NULL;
DEQUEUE(task->on_shutdown, event, link); event = prev) {
ENQUEUE(task->events, event, link); prev = PREV(event, link);
queued_something = ISC_TRUE; DEQUEUE(task->on_shutdown, event, link);
ENQUEUE(task->events, event, link);
queued_something = ISC_TRUE;
}
} }
task->enqueue_allowed = ISC_FALSE;
task->shutting_down = ISC_TRUE;
} }
UNLOCK(&task->lock); UNLOCK(&task->lock);
@@ -520,7 +509,7 @@ run(void *uap) {
isc_taskmgr_t *manager = uap; isc_taskmgr_t *manager = uap;
isc_task_t *task; isc_task_t *task;
XTRACE("start"); XTHREADTRACE("start");
REQUIRE(VALID_MANAGER(manager)); REQUIRE(VALID_MANAGER(manager));
@@ -584,11 +573,11 @@ run(void *uap) {
* task lock. * task lock.
*/ */
while (EMPTY(manager->ready_tasks) && !FINISHED(manager)) { while (EMPTY(manager->ready_tasks) && !FINISHED(manager)) {
XTRACE("wait"); XTHREADTRACE("wait");
WAIT(&manager->work_available, &manager->lock); WAIT(&manager->work_available, &manager->lock);
XTRACE("awake"); XTHREADTRACE("awake");
} }
XTRACE("working"); XTHREADTRACE("working");
task = HEAD(manager->ready_tasks); task = HEAD(manager->ready_tasks);
if (task != NULL) { if (task != NULL) {
@@ -645,12 +634,13 @@ run(void *uap) {
* put it to sleep. * put it to sleep.
*/ */
XTRACE("empty"); XTRACE("empty");
if (task->shutting_down) { if (TASK_DONE(task)) {
XTRACE("shutdown"); XTRACE("done");
if (task->references == 0) if (task->references == 0)
free_task = ISC_TRUE; free_task = ISC_TRUE;
task->state = task->flags &=
task_state_shutdown; ~TASK_F_SENDOK;
task->state = task_state_done;
} else } else
task->state = task_state_idle; task->state = task_state_idle;
done = ISC_TRUE; done = ISC_TRUE;
@@ -704,7 +694,7 @@ run(void *uap) {
} }
UNLOCK(&manager->lock); UNLOCK(&manager->lock);
XTRACE("exit"); XTHREADTRACE("exit");
return ((isc_threadresult_t)0); return ((isc_threadresult_t)0);
} }
@@ -800,7 +790,7 @@ isc_taskmgr_destroy(isc_taskmgr_t **managerp) {
manager = *managerp; manager = *managerp;
REQUIRE(VALID_MANAGER(manager)); REQUIRE(VALID_MANAGER(manager));
XTRACE("isc_taskmgr_destroy"); XTHREADTRACE("isc_taskmgr_destroy");
/* /*
* Only one non-worker thread may ever call this routine. * Only one non-worker thread may ever call this routine.
* If a worker thread wants to initiate shutdown of the * If a worker thread wants to initiate shutdown of the
@@ -827,33 +817,43 @@ isc_taskmgr_destroy(isc_taskmgr_t **managerp) {
manager->exiting = ISC_TRUE; manager->exiting = ISC_TRUE;
/* /*
* Post the shutdown event to every task (if it hasn't already been * Post shutdown event(s) to every task (if they haven't already been
* posted). * posted).
*/ */
for (task = HEAD(manager->tasks); for (task = HEAD(manager->tasks);
task != NULL; task != NULL;
task = NEXT(task, link)) { task = NEXT(task, link)) {
LOCK(&task->lock); LOCK(&task->lock);
if (!task->shutting_down) { if ((task->flags & TASK_F_SHUTTINGDOWN) == 0) {
/* task->flags |= TASK_F_SHUTTINGDOWN;
* 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);
}
if (task->state == task_state_idle) { if (task->state == task_state_idle) {
task->state = task_state_ready; INSIST(EMPTY(task->events));
ENQUEUE(manager->ready_tasks, task, if (EMPTY(task->on_shutdown) &&
ready_link); 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 || INSIST(task->state == task_state_ready ||
task->state == task_state_running); task->state == task_state_running ||
task->enqueue_allowed = ISC_FALSE; task->state == task_state_done);
task->shutting_down = ISC_TRUE; 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);
} }