From: Andre Noll Date: Sun, 8 Jul 2012 18:57:16 +0000 (+0000) Subject: sched: Introduce task notifications. X-Git-Tag: v0.4.12~7^2~20 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=58ce610c5a46eb03810d8a93b10d5bc51bc10b12 sched: Introduce task notifications. 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. --- diff --git a/audiod.c b/audiod.c index 4fc04b51..f8dde0c2 100644 --- 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); diff --git a/dccp_recv.c b/dccp_recv.c index ea6884b1..ff9b347b 100644 --- a/dccp_recv.c +++ b/dccp_recv.c @@ -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; diff --git a/http_recv.c b/http_recv.c index 11602f95..c19facf7 100644 --- a/http_recv.c +++ b/http_recv.c @@ -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 2b54ce19..e67579b3 100644 --- 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 49c1c085..9d4d52e2 100644 --- 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); diff --git a/udp_recv.c b/udp_recv.c index 615553c5..f4912e1e 100644 --- a/udp_recv.c +++ b/udp_recv.c @@ -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;