client: Only start stdin task for addblob commands.
authorAndre Noll <maan@systemlinux.org>
Wed, 26 Jun 2013 18:28:58 +0000 (20:28 +0200)
committerAndre Noll <maan@systemlinux.org>
Wed, 26 Jun 2013 20:18:11 +0000 (22:18 +0200)
Currently the command

para_client -- ls -lv | grep a

fails in a rather strange way, emitting many error messages like

grep: writing output: Resource temporarily unavailable

This is because O_NONBLOCK is a file status flag rather than a file
descriptor flag, i.e. nonblocking mode is a property of the file
description rather than the file descriptor.

In the above command both stdin of the para_client process and
stdout of the grep process refer to the same file description (the
terminal). para_client sets stdin to nonblocking mode, hence stdout
of the grep process is also in nonblocking mode.

We avoid this problem by changing client.c to only set stdin to
nonblocking mode if we actually need to read from stdin, i.e. only
for the addblob commands. This is achieved by registering the stdin
task (which sets the O_NONBLOCK flag) only if the client status
is CL_SENDING.

For addblob commands the client status changes from CL_EXECUTING to
CL_SENDING, so we can not simply terminate the supervisor task any more
after the stdout task has been registered. Instead we must keep this
task alive and (a) remember that stdout has already been started, and
(b) start the stdin task in case the client status becomes CL_SENDING.

Unfortunately, there is no simple way to store this bit of information
as we don't have a dedicated supervisor structure yet. Therefore this
patch introduces this structure as a task struct plus a single boolean.

client.c

index 413d08c..44862ab 100644 (file)
--- a/client.c
+++ b/client.c
@@ -531,25 +531,39 @@ __noreturn static void print_completions(void)
 
 #endif /* HAVE_READLINE */
 
+struct supervisor_task {
+       bool stdout_task_started;
+       struct task task;
+};
+
 static void supervisor_post_select(struct sched *s, struct task *t)
 {
+       struct supervisor_task *svt = container_of(t, struct supervisor_task,
+               task);
+
        if (ct->task.error < 0) {
                t->error = ct->task.error;
                return;
        }
-       if (ct->status == CL_EXECUTING) {
-               stdin_set_defaults(&sit);
-               register_task(s, &sit.task);
+       if (!svt->stdout_task_started && ct->status == CL_EXECUTING) {
                stdout_set_defaults(&sot);
                register_task(s, &sot.task);
+               svt->stdout_task_started = true;
+               return;
+       }
+       if (ct->status == CL_SENDING) {
+               stdin_set_defaults(&sit);
+               register_task(s, &sit.task);
                t->error = -E_TASK_STARTED;
                return;
        }
 }
 
-static struct task svt = {
-       .post_select = supervisor_post_select,
-       .status = "supervisor task"
+static struct supervisor_task supervisor_task = {
+       .task = {
+               .post_select = supervisor_post_select,
+               .status = "supervisor task"
+       }
 };
 
 /**
@@ -598,7 +612,7 @@ int main(int argc, char *argv[])
                goto out;
        sot.btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.name = "stdout", .parent = ct->btrn[0]));
-       register_task(&sched, &svt);
+       register_task(&sched, &supervisor_task.task);
        ret = schedule(&sched);
        if (ret >= 0 && ct->task.error < 0) {
                switch(ct->task.error) {