sched: Introduce task notifications.
authorAndre Noll <maan@systemlinux.org>
Sun, 8 Jul 2012 18:57:16 +0000 (18:57 +0000)
committerAndre Noll <maan@systemlinux.org>
Sun, 18 Nov 2012 19:28:27 +0000 (20:28 +0100)
Currently there is no canonical way to perform inter-task
communications. This leads to ugly hacks like kill_all_decoders()
of audiod which is called when audiod switches to "off" or "standby"
mode. This function sets the error state of the receiver task to a
negative value and removes the receiver's buffer tree node, magically
knowing that this is the correct action to take in order to shut down
the receiver cleanly.

This patch provides a simple infrastructure for task communications
which makes such hacks unnecessary. Tasks may send a notification
value, which is a standard error code, to another task by calling the
new task_notify() function. The receiving task is supposed to check
for any pending notifications in its ->post_select function.

As a first application of the notification concept, the above mentioned
kill_all_decoders() function is changed to use notifications instead.

audiod.c
dccp_recv.c
http_recv.c
sched.c
sched.h
udp_recv.c

index 4fc04b5120829d057617d68d7854453c2612c6cc..f8dde0c2a2f1427ac38e6e598b7e169ff7d626b0 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -454,7 +454,7 @@ static void kill_btrn(struct btr_node **btrnp, struct task *t, int error)
        btr_remove_node(btrnp);
 }
 
-static void kill_all_decoders(int error)
+static void notify_receivers(int error)
 {
        int i;
 
@@ -464,8 +464,7 @@ static void kill_all_decoders(int error)
                        continue;
                if (!s->receiver_node)
                        continue;
-               kill_btrn(&s->receiver_node->btrn, &s->receiver_node->task,
-                               error);
+               task_notify(&s->receiver_node->task, error);
        }
 }
 
@@ -1144,7 +1143,7 @@ static void start_stop_decoders(void)
                try_to_close_slot(i);
        if (audiod_status != AUDIOD_ON ||
                        !(stat_task->vss_status & VSS_STATUS_FLAG_PLAYING))
-               return kill_all_decoders(-E_NOT_PLAYING);
+               return notify_receivers(E_NOT_PLAYING);
        if (!must_start_decoder())
                return;
        ret = open_receiver(stat_task->current_audio_format_num);
index ea6884b16a1ffcda3002eb6b6340b2cff257b7be..ff9b347b6bf8fb52cfdb473d74126c839fcc110f 100644 (file)
@@ -132,6 +132,9 @@ static void dccp_recv_post_select(struct sched *s, struct task *t)
        int ret, iovcnt;
        size_t num_bytes;
 
+       ret = task_get_notification(t);
+       if (ret < 0)
+               goto out;
        ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
        if (ret <= 0)
                goto out;
index 11602f9544e34c36eb54763852d1d5cf63d7f2cc..c19facf70a39b1503deaea28008fc4b7a8b73f97 100644 (file)
@@ -83,7 +83,9 @@ static void http_recv_post_select(struct sched *s, struct task *t)
        struct iovec iov[2];
        size_t num_bytes;
 
-       t->error = 0;
+       ret = task_get_notification(t);
+       if (ret < 0)
+               goto out;
        ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
        if (ret < 0)
                goto out;
diff --git a/sched.c b/sched.c
index 2b54ce194fae897842ac94b39ff597828c8de8c4..e67579b32339c17101bbe2e3cedc45c9410cc7bc 100644 (file)
--- a/sched.c
+++ b/sched.c
@@ -55,6 +55,10 @@ static void sched_preselect(struct sched *s)
                        unregister_task(t);
                        continue;
                }
+               if (t->notification != 0) {
+                       sched_min_delay(s);
+                       break;
+               }
                if (!t->pre_select)
                        continue;
                t->pre_select(s, t);
@@ -91,6 +95,7 @@ static void sched_post_select(struct sched *s)
                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)
                        continue;
                /*
@@ -175,6 +180,7 @@ again:
 void register_task(struct sched *s, struct task *t)
 {
        PARA_INFO_LOG("registering %s (%p)\n", t->status, t);
+       t->notification = 0;
        if (!s->pre_select_list.next)
                INIT_LIST_HEAD(&s->pre_select_list);
        if (!s->post_select_list.next)
@@ -245,6 +251,50 @@ char *get_task_list(struct sched *s)
        return msg;
 }
 
+/**
+ * Set the notification value of a task.
+ *
+ * \param t The task to notify.
+ * \param err A positive error code.
+ *
+ * Tasks which honor notifications are supposed to call \ref
+ * task_get_notification() in their post_select function and act on the
+ * returned notification value.
+ *
+ * If the scheduler detects during its pre_select loop that at least one task
+ * has been notified, the loop terminates, and the post_select methods of all
+ * taks are immediately called again.
+ *
+ * The notification for a task is reset after the call to its post_select
+ * method.
+ *
+ * \sa \ref task_get_notification().
+ */
+void task_notify(struct task *t, int err)
+{
+       assert(err > 0);
+       if (t->notification == -err) /* ignore subsequent notifications */
+               return;
+       PARA_INFO_LOG("notifying task %s: %s\n", t->status, para_strerror(err));
+       t->notification = -err;
+}
+
+/**
+ * Return the notification value of a task.
+ *
+ * \param t The task to get the notification value from.
+ *
+ * \return The notification value. If this is negative, the task has been
+ * notified by another task. Tasks are supposed to check for notifications by
+ * calling this function from their post_select method.
+ *
+ * \sa \ref task_notify().
+ */
+int task_get_notification(struct task *t)
+{
+       return t->notification;
+}
+
 /**
  * Set the select timeout to the minimal possible value.
  *
diff --git a/sched.h b/sched.h
index 49c1c085d9e09c4d847ce3002ab641c7a265689a..9d4d52e2118aa2c412cfbe8c9e86acda28ddbacc 100644 (file)
--- a/sched.h
+++ b/sched.h
@@ -68,6 +68,8 @@ struct task {
        struct list_head post_select_node;
        /** Descriptive text and current status of the task. */
        char status[255];
+       /** If less than zero, the task was notified by another task. */
+       int notification;
 };
 
 /**
@@ -81,7 +83,9 @@ extern struct timeval *now;
 void register_task(struct sched *s, struct task *t);
 int schedule(struct sched *s);
 char *get_task_list(struct sched *s);
+void task_notify(struct task *t, int err);
 void sched_shutdown(struct sched *s);
+int task_get_notification(struct task *t);
 void sched_min_delay(struct sched *s);
 void sched_request_timeout(struct timeval *to, struct sched *s);
 void sched_request_timeout_ms(long unsigned ms, struct sched *s);
index 615553c586064ed39bfb29e31c3c8fff18faf852..f4912e1e5322f1adf79cb805a2782ba7edbecdad 100644 (file)
@@ -57,7 +57,9 @@ static void udp_recv_post_select(__a_unused struct sched *s, struct task *t)
        struct iovec iov[2];
        int ret, readv_ret, iovcnt;
 
-       t->error = 0;
+       ret = task_get_notification(t);
+       if (ret < 0)
+               goto out;
        ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
        if (ret <= 0)
                goto out;