}
}
+static void unlink_and_free_task(struct task *t)
+{
+ PARA_INFO_LOG("freeing task %s\n", t->status);
+ list_del(&t->node);
+ if (t->owned_by_sched)
+ free(t);
+}
+
//#define SCHED_DEBUG 1
static inline void call_post_select(struct sched *s, struct task *t)
{
#endif
}
-static void sched_post_select(struct sched *s)
+static unsigned sched_post_select(struct sched *s)
{
struct task *t, *tmp;
+ unsigned num_running_tasks = 0;
list_for_each_entry_safe(t, tmp, &s->task_list, node) {
+ if (t->error < 0)
+ continue;
call_post_select(s, t);
t->notification = 0;
- if (t->error < 0)
- list_del(&t->node);
+ if (t->error < 0) {
+ if (!t->owned_by_sched)
+ list_del(&t->node);
+ } else
+ num_running_tasks++;
}
+ return num_running_tasks;
}
/**
int schedule(struct sched *s)
{
int ret;
+ unsigned num_running_tasks;
if (!s->select_function)
s->select_function = para_select;
FD_ZERO(&s->wfds);
}
clock_get_realtime(now);
- sched_post_select(s);
- if (list_empty(&s->task_list))
+ num_running_tasks = sched_post_select(s);
+ if (num_running_tasks == 0)
return 0;
goto again;
}
/**
- * Add a task to the scheduler.
+ * Deallocate all resources of all tasks of a scheduler instance.
+ *
+ * \param s The scheduler instance.
+ *
+ * This should only be called after \ref schedule() has returned.
+ */
+void sched_shutdown(struct sched *s)
+{
+ struct task *t, *tmp;
+
+ list_for_each_entry_safe(t, tmp, &s->task_list, node) {
+ if (t->error >= 0)
+ /* The task list should contain only terminated tasks. */
+ PARA_WARNING_LOG("shutting down running task %s\n",
+ t->status);
+ unlink_and_free_task(t);
+ }
+}
+
+/**
+ * Add a task to the scheduler. Deprecated.
*
* \param t The task to add.
* \param s The scheduler instance to add the task to.
PARA_INFO_LOG("registering %s (%p)\n", t->status, t);
assert(t->post_select);
t->notification = 0;
+ t->owned_by_sched = false;
+ if (!s->task_list.next)
+ INIT_LIST_HEAD(&s->task_list);
+ list_add_tail(&t->node, &s->task_list);
+}
+
+/**
+ * Add a task to the scheduler task list.
+ *
+ * \param info Task information supplied by the caller.
+ * \param s The scheduler instance.
+ *
+ * \return A pointer to a newly allocated task structure. It will be
+ * freed by sched_shutdown().
+ */
+struct task *task_register(struct task_info *info, struct sched *s)
+{
+ struct task *t = para_malloc(sizeof(*t));
+
+ assert(info->post_select);
+
if (!s->task_list.next)
INIT_LIST_HEAD(&s->task_list);
+
+ snprintf(t->status, sizeof(t->status) - 1, "%s", info->name);
+ t->status[sizeof(t->status) - 1] = '\0';
+ t->notification = 0;
+ t->error = 0;
+ t->pre_select = info->pre_select;
+ t->post_select = info->post_select;
+ t->context = info->context;
+ t->owned_by_sched = true;
list_add_tail(&t->node, &s->task_list);
+ return t;
+}
+
+/**
+ * Obtain the context pointer of a task.
+ *
+ * \param t Return this task's context pointer.
+ *
+ * \return A pointer to the memory location specified previously as \a
+ * task_info->context when the task was registered with \ref task_register().
+ */
+void *task_context(struct task *t)
+{
+ assert(t->owned_by_sched);
+ return t->context;
}
/**
list_for_each_entry_safe(t, tmp, &s->task_list, node) {
char *tmp_msg;
- tmp_msg = make_message("%s%p\t%s\n", msg? msg : "", t, t->status);
+ tmp_msg = make_message("%s%p\t%s\t%s\n", msg? msg : "", t,
+ t->error < 0? "zombie" : "running",
+ t->status);
free(msg);
msg = tmp_msg;
}