diff --git a/lib/isc/include/isc/task.h b/lib/isc/include/isc/task.h index 14495b2491..ef6aba7961 100644 --- a/lib/isc/include/isc/task.h +++ b/lib/isc/include/isc/task.h @@ -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 * - * *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, 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, * - * If '*taskp' is the last reference to the task and - * the task has been shutdown, - * - * All resources used by the task will be freed. - * - * Returns: - * - * 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. diff --git a/lib/isc/task.c b/lib/isc/task.c index 9a07966a8d..02ab1bc4d7 100644 --- a/lib/isc/task.c +++ b/lib/isc/task.c @@ -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;