]> git.tuebingen.mpg.de Git - paraslash.git/blobdiff - sched.c
sched: Introduce alternative task API.
[paraslash.git] / sched.c
diff --git a/sched.c b/sched.c
index 8b68667bf453c481df6950ec727be3b134037f99..101cc4c0dad3706e841881b2a6d2989825fc58b0 100644 (file)
--- a/sched.c
+++ b/sched.c
 static struct timeval now_struct;
 struct timeval *now = &now_struct;
 
-/*
- * Remove a task from the scheduler.
- *
- * \param t The task to remove.
- *
- * If the pre_select pointer of \a t is not \p NULL, it is removed from
- * the pre_select list of the scheduler. Same goes for \a post_select.
- */
-static void unregister_task(struct task *t)
-{
-       assert(t->error < 0);
-       PARA_INFO_LOG("unregistering %s (%s)\n", t->status,
-               para_strerror(-t->error));
-       if (t->pre_select)
-               list_del(&t->pre_select_node);
-       if (t->post_select)
-               list_del(&t->post_select_node);
-}
-
 static inline bool timeout_is_zero(struct sched *s)
 {
        struct timeval *tv = &s->select_timeout;
@@ -50,7 +31,9 @@ static void sched_preselect(struct sched *s)
 {
        struct task *t, *tmp;
 
-       list_for_each_entry_safe(t, tmp, &s->pre_select_list, pre_select_node) {
+       list_for_each_entry_safe(t, tmp, &s->task_list, node) {
+               if (t->error < 0)
+                       continue;
                if (t->notification != 0)
                        sched_min_delay(s);
                if (t->pre_select)
@@ -58,6 +41,14 @@ static void sched_preselect(struct sched *s)
        }
 }
 
+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)
 {
@@ -78,23 +69,27 @@ 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->post_select_list, post_select_node) {
-               if (t->error >= 0)
-                       call_post_select(s, t);
-//             PARA_INFO_LOG("%s: %d\n", t->status, t->ret);
-               t->notification = 0;
-               if (t->error >= 0)
+       list_for_each_entry_safe(t, tmp, &s->task_list, node) {
+               if (t->error < 0)
                        continue;
-               unregister_task(t);
+               call_post_select(s, t);
+               t->notification = 0;
+               if (t->error < 0) {
+                       if (!t->owned_by_sched)
+                               list_del(&t->node);
+               } else
+                       num_running_tasks++;
        }
+       return num_running_tasks;
 }
 
 /**
- * The core function for all paraslash programs.
+ * The core function of all paraslash programs.
  *
  * \param s Pointer to the scheduler struct.
  *
@@ -103,14 +98,15 @@ static void sched_post_select(struct sched *s)
  * the fd sets of \a s.  Next, it calls para_select() and makes the result available
  * to the registered tasks by calling their post_select hook.
  *
- * \return Zero if no more tasks are left in either of the two lists, negative
- * if para_select returned an error.
+ * \return Zero if no more tasks are left in the task list, negative if the
+ * select function returned an error.
  *
- * \sa task, now.
+ * \sa \ref task, \ref now.
  */
 int schedule(struct sched *s)
 {
        int ret;
+       unsigned num_running_tasks;
 
        if (!s->select_function)
                s->select_function = para_select;
@@ -136,39 +132,93 @@ again:
                FD_ZERO(&s->wfds);
        }
        clock_get_realtime(now);
-       sched_post_select(s);
-       if (list_empty(&s->pre_select_list) && list_empty(&s->post_select_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.
  *
- * If the pre_select pointer of \a t is not \p NULL, it is added to
- * the pre_select list of the scheduler. Same goes for post_select.
- *
  * \sa task::pre_select, task::post_select
  */
 void register_task(struct sched *s, struct task *t)
 {
        PARA_INFO_LOG("registering %s (%p)\n", t->status, t);
+       assert(t->post_select);
        t->notification = 0;
-       if (!s->pre_select_list.next)
-               INIT_LIST_HEAD(&s->pre_select_list);
-       if (!s->post_select_list.next)
-               INIT_LIST_HEAD(&s->post_select_list);
-       if (t->pre_select) {
-               PARA_DEBUG_LOG("pre_select: %p\n", &t->pre_select);
-               list_add_tail(&t->pre_select_node, &s->pre_select_list);
-       }
-       if (t->post_select) {
-               PARA_DEBUG_LOG("post_select: %p\n", &t->post_select);
-               list_add_tail(&t->post_select_node, &s->post_select_list);
-       }
+       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;
 }
 
 /**
@@ -186,21 +236,14 @@ char *get_task_list(struct sched *s)
        struct task *t, *tmp;
        char *msg = NULL;
 
-       list_for_each_entry_safe(t, tmp, &s->pre_select_list, pre_select_node) {
-               char *tmp_msg;
-               tmp_msg = make_message("%s%p\tpre\t%s\n", msg? msg : "", t, t->status);
-               free(msg);
-               msg = tmp_msg;
-       }
-       list_for_each_entry_safe(t, tmp, &s->post_select_list, post_select_node) {
+       list_for_each_entry_safe(t, tmp, &s->task_list, node) {
                char *tmp_msg;
-//             if (t->pre_select)
-//                     continue;
-               tmp_msg = make_message("%s%p\tpost\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;
        }
-       //PARA_DEBUG_LOG("task list:\n%s", msg);
        return msg;
 }
 
@@ -243,7 +286,7 @@ void task_notify(struct task *t, int err)
  *
  * \sa \ref task_notify().
  */
-int task_get_notification(struct task *t)
+int task_get_notification(const struct task *t)
 {
        return t->notification;
 }
@@ -261,9 +304,7 @@ void task_notify_all(struct sched *s, int err)
 {
        struct task *t;
 
-       list_for_each_entry(t, &s->pre_select_list, pre_select_node)
-               task_notify(t, err);
-       list_for_each_entry(t, &s->post_select_list, post_select_node)
+       list_for_each_entry(t, &s->task_list, node)
                task_notify(t, err);
 }