Add infrastructure to run a function asynchronously.
authorJohannes Sixt <johannes.sixt@telecom.at>
Fri, 19 Oct 2007 19:48:00 +0000 (21:48 +0200)
committerShawn O. Pearce <spearce@spearce.org>
Sun, 21 Oct 2007 05:30:41 +0000 (01:30 -0400)
This adds start_async() and finish_async(), which runs a function
asynchronously. Communication with the caller happens only via pipes.
For this reason, this implementation forks off a child process that runs
the function.

[sp: Style nit fixed by removing unnecessary block on if condition
     inside of start_async()]

Signed-off-by: Johannes Sixt <johannes.sixt@telecom.at>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
run-command.c
run-command.h

index d00c03b..d99a6c4 100644 (file)
@@ -127,16 +127,11 @@ int start_command(struct child_process *cmd)
        return 0;
 }
 
        return 0;
 }
 
-int finish_command(struct child_process *cmd)
+static int wait_or_whine(pid_t pid)
 {
 {
-       if (cmd->close_in)
-               close(cmd->in);
-       if (cmd->close_out)
-               close(cmd->out);
-
        for (;;) {
                int status, code;
        for (;;) {
                int status, code;
-               pid_t waiting = waitpid(cmd->pid, &status, 0);
+               pid_t waiting = waitpid(pid, &status, 0);
 
                if (waiting < 0) {
                        if (errno == EINTR)
 
                if (waiting < 0) {
                        if (errno == EINTR)
@@ -144,7 +139,7 @@ int finish_command(struct child_process *cmd)
                        error("waitpid failed (%s)", strerror(errno));
                        return -ERR_RUN_COMMAND_WAITPID;
                }
                        error("waitpid failed (%s)", strerror(errno));
                        return -ERR_RUN_COMMAND_WAITPID;
                }
-               if (waiting != cmd->pid)
+               if (waiting != pid)
                        return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
                if (WIFSIGNALED(status))
                        return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
                        return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
                if (WIFSIGNALED(status))
                        return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
@@ -158,6 +153,15 @@ int finish_command(struct child_process *cmd)
        }
 }
 
        }
 }
 
+int finish_command(struct child_process *cmd)
+{
+       if (cmd->close_in)
+               close(cmd->in);
+       if (cmd->close_out)
+               close(cmd->out);
+       return wait_or_whine(cmd->pid);
+}
+
 int run_command(struct child_process *cmd)
 {
        int code = start_command(cmd);
 int run_command(struct child_process *cmd)
 {
        int code = start_command(cmd);
@@ -200,3 +204,34 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const
        cmd.env = env;
        return run_command(&cmd);
 }
        cmd.env = env;
        return run_command(&cmd);
 }
+
+int start_async(struct async *async)
+{
+       int pipe_out[2];
+
+       if (pipe(pipe_out) < 0)
+               return error("cannot create pipe: %s", strerror(errno));
+
+       async->pid = fork();
+       if (async->pid < 0) {
+               error("fork (async) failed: %s", strerror(errno));
+               close_pair(pipe_out);
+               return -1;
+       }
+       if (!async->pid) {
+               close(pipe_out[0]);
+               exit(!!async->proc(pipe_out[1], async->data));
+       }
+       async->out = pipe_out[0];
+       close(pipe_out[1]);
+       return 0;
+}
+
+int finish_async(struct async *async)
+{
+       int ret = 0;
+
+       if (wait_or_whine(async->pid))
+               ret = error("waitpid (async) failed");
+       return ret;
+}
index 35b9fb6..94e1e9d 100644 (file)
@@ -43,4 +43,26 @@ int run_command_v_opt_cd(const char **argv, int opt, const char *dir);
  */
 int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env);
 
  */
 int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env);
 
+/*
+ * The purpose of the following functions is to feed a pipe by running
+ * a function asynchronously and providing output that the caller reads.
+ *
+ * It is expected that no synchronization and mutual exclusion between
+ * the caller and the feed function is necessary so that the function
+ * can run in a thread without interfering with the caller.
+ */
+struct async {
+       /*
+        * proc writes to fd and closes it;
+        * returns 0 on success, non-zero on failure
+        */
+       int (*proc)(int fd, void *data);
+       void *data;
+       int out;        /* caller reads from here and closes it */
+       pid_t pid;
+};
+
+int start_async(struct async *async);
+int finish_async(struct async *async);
+
 #endif
 #endif