X-Git-Url: http://git.tuebingen.mpg.de/?a=blobdiff_plain;f=sched.c;fp=sched.c;h=6b8e09108308336676e48c4d8c9e84eb6e388431;hb=8bf35b38357c3ce59f52ae87f6e84e4b6d183ac7;hp=101cc4c0dad3706e841881b2a6d2989825fc58b0;hpb=a96be5bc4fda8c0df5370d646defb5ff632ba391;p=paraslash.git diff --git a/sched.c b/sched.c index 101cc4c0..6b8e0910 100644 --- a/sched.c +++ b/sched.c @@ -75,8 +75,11 @@ static unsigned sched_post_select(struct sched *s) unsigned num_running_tasks = 0; list_for_each_entry_safe(t, tmp, &s->task_list, node) { - if (t->error < 0) + if (t->error < 0) { + if (t->dead) /* task has been reaped */ + unlink_and_free_task(t); continue; + } call_post_select(s, t); t->notification = 0; if (t->error < 0) { @@ -138,6 +141,53 @@ again: goto again; } +/** + * Obtain the error status of a task and deallocate its resources. + * + * \param tptr Identifies the task to reap. + * + * This function is similar to wait(2) in that it returns information about a + * terminated task and allows to release the resources associated with the + * task. Until this function is called, the terminated task remains in a zombie + * state. + * + * \return If \a tptr is \p NULL, or \a *tptr is \p NULL, the function does + * nothing and returns zero. Otherwise, it is checked whether the task + * identified by \a tptr is still running. If it is, the function returns zero + * and again, no action is taken. Otherwise the (negative) error code of the + * terminated task is returned and \a *tptr is set to \p NULL. The task will + * then be removed removed from the scheduler task list. + * + * \sa \ref sched_shutdown(), wait(2). + */ +int task_reap(struct task **tptr) +{ + struct task *t; + + if (!tptr) + return 0; + t = *tptr; + if (!t) + return 0; + if (!t->owned_by_sched) + return 0; + if (t->error >= 0) + return 0; + if (t->dead) /* will be freed in sched_post_select() */ + return 0; + /* + * With list_for_each_entry_safe() it is only safe to remove the + * _current_ list item. Since we are being called from the loop in + * schedule() via some task's ->post_select() function, freeing the + * given task here would result in use-after-free bugs in schedule(). + * So we only set t->dead which tells schedule() to free the task in + * the next iteration of its loop. + */ + t->dead = true; + *tptr = NULL; + return t->error; +} + /** * Deallocate all resources of all tasks of a scheduler instance. * @@ -199,6 +249,7 @@ struct task *task_register(struct task_info *info, struct sched *s) t->status[sizeof(t->status) - 1] = '\0'; t->notification = 0; t->error = 0; + t->dead = false; t->pre_select = info->pre_select; t->post_select = info->post_select; t->context = info->context; @@ -239,7 +290,7 @@ char *get_task_list(struct sched *s) list_for_each_entry_safe(t, tmp, &s->task_list, node) { char *tmp_msg; tmp_msg = make_message("%s%p\t%s\t%s\n", msg? msg : "", t, - t->error < 0? "zombie" : "running", + t->error < 0? (t->dead? "dead" : "zombie") : "running", t->status); free(msg); msg = tmp_msg;