Commit 3bff571a authored by Bob Halley's avatar Bob Halley

Simply task API:

	Tasks now terminate when

		Any shutdown events have been posted

		There are no references

		The event queue is empty

	If a task has no references and the event queue
	is empty, then a shutdown will be triggered if
	it hasn't been already.

	allowdone and allowsend are gone

	sending events can no longer fail
parent 0bee410b
......@@ -161,7 +161,7 @@ isc_task_mem(isc_task_t *task);
* The memory context specified when the task was created.
*/
isc_result_t
void
isc_task_send(isc_task_t *task, isc_event_t **eventp);
/*
* Send '*event' to 'task'.
......@@ -173,16 +173,9 @@ isc_task_send(isc_task_t *task, isc_event_t **eventp);
* Ensures
*
* On success, *eventp == NULL
*
* Returns:
*
* ISC_R_SUCCESS
* ISC_R_TASKDONE The task is done.
* ISC_R_TASKNOSEND Sending events to the task is not
* currently allowed.
*/
isc_result_t
void
isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp);
/*
* Send '*event' to '*taskp' and then detach '*taskp' from its
......@@ -194,27 +187,18 @@ isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp);
*
* Ensures
*
* On success,
* *eventp == NULL
*
* *eventp == NULL
*
* *taskp == NULL
*
* If '*taskp' is the last reference to the task, the task is
* idle (has an empty event queue), and has not been shutdown,
* the task will be shutdown.
*
* If '*taskp' is the last reference to the task and
* the task has been shutdown,
* *taskp == NULL
*
* All resources used by the task will be freed.
* If '*taskp' is the last reference to the task, the task is
* idle (has an empty event queue), and has not been shutdown,
* the task will be shutdown.
*
* Returns:
* If '*taskp' is the last reference to the task and
* the task has been shutdown,
*
* ISC_R_SUCCESS
* ISC_R_TASKDONE The task is done.
* ISC_R_TASKNOSEND Sending events to the task is not
* currently allowed.
* All resources used by the task will be freed.
*/
/*
......@@ -387,73 +371,6 @@ isc_task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type,
* The number of events unsent.
*/
isc_result_t
isc_task_allowsend(isc_task_t *task, isc_boolean_t allow);
/*
* Allow or disallow sending events to 'task'.
*
* XXXRTH: WARNING: This method may be removed before beta.
*
* Notes:
*
* Sending events is allowed when a task is created.
*
* This functionality will always be available, but the interface
* may change in the future. In particular, it may be unified with
* isc_task_allowdone() in an isc_task_getoptions()/isc_task_setoptions()
* block.
*
* Requires:
*
* 'task' is a valid task.
*
* Ensures:
*
* On success,
*
* If 'allow' is ISC_TRUE, then events may be send to the task.
* Otherwise, any attempt to send an event to the task will be
* disallowed and return ISC_R_TASKNOSEND.
*
* Returns:
*
* ISC_R_SUCCESS
* ISC_R_TASKDONE The task is done.
*/
isc_result_t
isc_task_allowdone(isc_task_t *task, isc_boolean_t allow);
/*
* Allow or disallow automatic termination of 'task'.
*
* Notes:
*
* Automatic task termination is allowed when a task is created.
*
* This functionality will always be available, but the interface
* may change in the future. In particular, it may be unified with
* isc_task_allowsend() in an isc_task_getoptions()/isc_task_setoptions()
* block.
*
* Requires:
*
* 'task' is a valid task.
*
* Ensures:
*
* On success,
*
* If 'allow' is ISC_TRUE, then when a task has been shutdown
* and its event queue becomes empty, the task will enter the
* done state. Otherwise, a task that is shutting down will not
* exit, even if its event queue becomes empty.
*
* Returns:
*
* ISC_R_SUCCESS
* ISC_R_TASKDONE The task is done.
*/
isc_result_t
isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, void *arg);
/*
......@@ -480,7 +397,6 @@ isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, void *arg);
* ISC_R_SUCCESS
* ISC_R_NOMEMORY
* ISC_R_TASKSHUTTINGDOWN Task is shutting down.
* ISC_R_TASKSHUTDOWN Task is shut down.
*/
void
......@@ -493,9 +409,7 @@ isc_task_shutdown(isc_task_t *task);
* Shutting down a task causes any shutdown events requested with
* isc_task_onshutdown() to be posted (in LIFO order). The task
* moves into a "shutting down" mode which prevents further calls
* to isc_task_onshutdown(). If automatic task termination is allowed,
* the task will enter the done state (i.e. terminate) when the event
* queue becomes empty.
* to isc_task_onshutdown().
*
* Trying to shutdown a task that has already been shutdown has no
* effect.
......
......@@ -54,10 +54,6 @@
*** Types.
***/
typedef enum {
detach_result_ok, detach_result_finished, detach_result_wasidle
} detach_result_t;
typedef enum {
task_state_idle, task_state_ready, task_state_running,
task_state_done
......@@ -85,13 +81,8 @@ struct isc_task {
LINK(isc_task_t) ready_link;
};
#define TASK_F_DONEOK 0x01
#define TASK_F_SENDOK 0x02
#define TASK_F_SHUTTINGDOWN 0x04
#define TASK_F_SHUTTINGDOWN 0x01
#define DONE_FLAGS (TASK_F_DONEOK|TASK_F_SHUTTINGDOWN)
#define TASK_WANTDONE(t) (((t)->flags & DONE_FLAGS) == \
DONE_FLAGS)
#define TASK_SHUTTINGDOWN(t) (((t)->flags & TASK_F_SHUTTINGDOWN) \
!= 0)
......@@ -178,7 +169,7 @@ isc_task_create(isc_taskmgr_t *manager, isc_mem_t *mctx, unsigned int quantum,
INIT_LIST(task->events);
INIT_LIST(task->on_shutdown);
task->quantum = quantum;
task->flags = (TASK_F_DONEOK|TASK_F_SENDOK);
task->flags = 0;
INIT_LINK(task, link);
INIT_LINK(task, ready_link);
......@@ -267,9 +258,8 @@ task_ready(isc_task_t *task) {
UNLOCK(&manager->lock);
}
static inline detach_result_t
static inline isc_boolean_t
task_detach(isc_task_t *task) {
detach_result_t dresult = detach_result_ok;
/*
* Caller must be holding the task lock.
......@@ -280,29 +270,27 @@ task_detach(isc_task_t *task) {
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;
}
if (task->references == 0 && task->state == task_state_idle) {
INSIST(EMPTY(task->events));
/*
* There are no references to this task, and no
* pending events. We could try to optimize and
* either initiate shutdown or clean up the task,
* depending on its state, but it's easier to just
* make the task ready and allow run() to deal with
* shutting down and termination.
*/
task->state = task_state_ready;
return (ISC_TRUE);
}
return (dresult);
return (ISC_FALSE);
}
void
isc_task_detach(isc_task_t **taskp) {
isc_task_t *task;
detach_result_t dresult;
isc_boolean_t was_idle;
/*
* Detach *taskp from its task.
......@@ -315,12 +303,10 @@ isc_task_detach(isc_task_t **taskp) {
XTRACE("isc_task_detach");
LOCK(&task->lock);
dresult = task_detach(task);
was_idle = task_detach(task);
UNLOCK(&task->lock);
if (dresult == detach_result_finished)
task_finished(task);
else if (dresult == detach_result_wasidle)
if (was_idle)
task_ready(task);
*taskp = NULL;
......@@ -338,9 +324,9 @@ isc_task_mem(isc_task_t *task) {
return (task->mctx);
}
static inline isc_result_t
task_send(isc_task_t *task, isc_event_t **eventp, isc_boolean_t *was_idlep) {
isc_result_t result = ISC_R_SUCCESS;
static inline isc_boolean_t
task_send(isc_task_t *task, isc_event_t **eventp) {
isc_boolean_t was_idle = ISC_FALSE;
isc_event_t *event;
/*
......@@ -352,39 +338,26 @@ task_send(isc_task_t *task, isc_event_t **eventp, isc_boolean_t *was_idlep) {
REQUIRE(event != NULL);
REQUIRE(event->sender != NULL);
REQUIRE(event->type > 0);
REQUIRE(task->state != task_state_done);
XTRACE("task_send");
/*
* Note: we require that task->state == task_state_done implies
* (task->flags & TASK_F_SENDOK) == 0.
*/
*was_idlep = ISC_FALSE;
if ((task->flags & TASK_F_SENDOK) != 0) {
if (task->state == task_state_idle) {
*was_idlep = ISC_TRUE;
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);
*eventp = NULL;
} else {
if (task->state == task_state_done)
result = ISC_R_TASKDONE;
else
result = ISC_R_TASKNOSEND;
if (task->state == task_state_idle) {
was_idle = ISC_TRUE;
INSIST(EMPTY(task->events));
task->state = task_state_ready;
}
return (result);
}
INSIST(task->state == task_state_ready ||
task->state == task_state_running);
ENQUEUE(task->events, event, link);
*eventp = NULL;
return (was_idle);
}
isc_result_t
void
isc_task_send(isc_task_t *task, isc_event_t **eventp) {
isc_boolean_t was_idle;
isc_result_t result;
/*
* Send '*event' to 'task'.
......@@ -400,12 +373,9 @@ isc_task_send(isc_task_t *task, isc_event_t **eventp) {
* some processing is deferred until after the lock is released.
*/
LOCK(&task->lock);
result = task_send(task, eventp, &was_idle);
was_idle = task_send(task, eventp);
UNLOCK(&task->lock);
if (result != ISC_R_SUCCESS)
return (result);
if (was_idle) {
/*
* We need to add this task to the ready queue.
......@@ -424,16 +394,12 @@ isc_task_send(isc_task_t *task, isc_event_t **eventp) {
*/
task_ready(task);
}
return (ISC_R_SUCCESS);
}
isc_result_t
void
isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
isc_boolean_t was_idle;
isc_result_t result;
isc_boolean_t idle1, idle2;
isc_task_t *task;
detach_result_t dresult = detach_result_ok;
/*
* Send '*event' to '*taskp' and then detach '*taskp' from its
......@@ -447,40 +413,21 @@ isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
XTRACE("isc_task_sendanddetach");
LOCK(&task->lock);
result = task_send(task, eventp, &was_idle);
if (result == ISC_R_SUCCESS)
dresult = task_detach(task);
idle1 = task_send(task, eventp);
idle2 = task_detach(task);
UNLOCK(&task->lock);
if (result != ISC_R_SUCCESS)
return (result);
/*
* If idle1, then idle2 shouldn't be true as well since we're holding
* the task lock, and thus the task cannot switch from ready back to
* idle.
*/
INSIST(!(idle1 && idle2));
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);
}
if (idle1 || idle2)
task_ready(task);
*taskp = NULL;
return (ISC_R_SUCCESS);
}
#define PURGE_OK(event) (((event)->attributes & ISC_EVENTATTR_NOPURGE) == 0)
......@@ -640,71 +587,6 @@ isc_task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type,
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;
......@@ -729,10 +611,7 @@ isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, void *arg) {
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)) {
if (TASK_SHUTTINGDOWN(task)) {
disallowed = ISC_TRUE;
result = ISC_R_SHUTTINGDOWN;
} else
......@@ -898,38 +777,51 @@ run(void *uap) {
}
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 (task->references == 0 &&
EMPTY(task->events) &&
!TASK_SHUTTINGDOWN(task)) {
isc_boolean_t was_idle;
/*
* There are no references and no
* pending events for this task,
* which means it will not become
* runnable again via an external
* action (such as sending an event
* or detaching).
*
* We initiate shutdown to prevent
* it from becoming a zombie.
*
* We do this here instead of in
* the "if EMPTY(task->events)" block
* below because:
*
* If we post no shutdown events,
* we want the task to finish.
*
* If we did post shutdown events,
* will still want the task's
* quantum to be applied.
*/
was_idle = task_shutdown(task);
INSIST(!was_idle);
}
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.
* right now.
*/
XTRACE("empty");
if (TASK_WANTDONE(task)) {
if (task->references == 0 &&
TASK_SHUTTINGDOWN(task)) {
/*
* The task is done.
*/
XTRACE("done");
if (task->references == 0)
finished = ISC_TRUE;
task->flags &=
~TASK_F_SENDOK;
finished = ISC_TRUE;
task->state = task_state_done;
} else
task->state = task_state_idle;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment