Move the sub-proces functions into sub-process.h/c.  Add documentation
for the new module in Documentation/technical/api-sub-process.txt

Signed-off-by: Ben Peart <benpe...@microsoft.com>
---
 Documentation/technical/api-sub-process.txt |  59 ++++++++++++++++
 Makefile                                    |   1 +
 convert.c                                   | 104 +---------------------------
 sub-process.c                               | 102 +++++++++++++++++++++++++++
 sub-process.h                               |  49 +++++++++++++
 5 files changed, 212 insertions(+), 103 deletions(-)
 create mode 100644 Documentation/technical/api-sub-process.txt
 create mode 100644 sub-process.c
 create mode 100644 sub-process.h

diff --git a/Documentation/technical/api-sub-process.txt 
b/Documentation/technical/api-sub-process.txt
new file mode 100644
index 0000000000..793508cf3e
--- /dev/null
+++ b/Documentation/technical/api-sub-process.txt
@@ -0,0 +1,59 @@
+sub-process API
+===============
+
+The sub-process API makes it possible to run background sub-processes
+for the entire lifetime of a Git invocation. If Git needs to communicate
+with an external process multiple times, then this can reduces the process
+invocation overhead. Git and the sub-process communicate through stdin and
+stdout.
+
+The sub-processes are kept in a hashmap by command name and looked up
+via the subprocess_find_entry function.  If an existing instance can not
+be found then a new process should be created and started.  When the
+parent git command terminates, all sub-processes are also terminated.
+
+This API is based on the run-command API.
+
+Data structures
+---------------
+
+* `struct subprocess_entry`
+
+The sub-process structure.  Members should not be accessed directly.
+
+Types
+-----
+
+'int(*subprocess_start_fn)(struct subprocess_entry *entry)'::
+
+       User-supplied function to initialize the sub-process.  This is
+       typically used to negotiate the interface version and capabilities.
+
+
+Functions
+---------
+
+`cmd2process_cmp`::
+
+       Function to test two subprocess hashmap entries for equality.
+
+`subprocess_start`::
+
+       Start a subprocess and add it to the subprocess hashmap.
+
+`subprocess_stop`::
+
+       Kill a subprocess and remove it from the subprocess hashmap.
+
+`subprocess_find_entry`::
+
+       Find a subprocess in the subprocess hashmap.
+
+`subprocess_get_child_process`::
+
+       Get the underlying `struct child_process` from a subprocess.
+
+`subprocess_read_status`::
+
+       Helper function to read packets looking for the last "status=<foo>"
+       key/value pair.
diff --git a/Makefile b/Makefile
index eb1a1a7cff..97c87fb647 100644
--- a/Makefile
+++ b/Makefile
@@ -840,6 +840,7 @@ LIB_OBJS += streaming.o
 LIB_OBJS += string-list.o
 LIB_OBJS += submodule.o
 LIB_OBJS += submodule-config.o
+LIB_OBJS += sub-process.o
 LIB_OBJS += symlinks.o
 LIB_OBJS += tag.o
 LIB_OBJS += tempfile.o
diff --git a/convert.c b/convert.c
index cfae45aeee..79d04907a5 100644
--- a/convert.c
+++ b/convert.c
@@ -4,6 +4,7 @@
 #include "quote.h"
 #include "sigchain.h"
 #include "pkt-line.h"
+#include "sub-process.h"
 
 /*
  * convert.c - convert a file when checking it out and checking it in.
@@ -496,12 +497,6 @@ static int apply_single_file_filter(const char *path, 
const char *src, size_t le
 #define CAP_CLEAN    (1u<<0)
 #define CAP_SMUDGE   (1u<<1)
 
-struct subprocess_entry {
-       struct hashmap_entry ent; /* must be the first member! */
-       const char *cmd;
-       struct child_process process;
-};
-
 struct cmd2process {
        struct subprocess_entry subprocess; /* must be the first member! */
        unsigned int supported_capabilities;
@@ -510,65 +505,6 @@ struct cmd2process {
 static int subprocess_map_initialized;
 static struct hashmap subprocess_map;
 
-static int cmd2process_cmp(const struct subprocess_entry *e1,
-                          const struct subprocess_entry *e2,
-                          const void *unused)
-{
-       return strcmp(e1->cmd, e2->cmd);
-}
-
-static struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, 
const char *cmd)
-{
-       struct subprocess_entry key;
-
-       hashmap_entry_init(&key, strhash(cmd));
-       key.cmd = cmd;
-       return hashmap_get(hashmap, &key, NULL);
-}
-
-static void subprocess_read_status(int fd, struct strbuf *status)
-{
-       struct strbuf **pair;
-       char *line;
-       for (;;) {
-               line = packet_read_line(fd, NULL);
-               if (!line)
-                       break;
-               pair = strbuf_split_str(line, '=', 2);
-               if (pair[0] && pair[0]->len && pair[1]) {
-                       /* the last "status=<foo>" line wins */
-                       if (!strcmp(pair[0]->buf, "status=")) {
-                               strbuf_reset(status);
-                               strbuf_addbuf(status, pair[1]);
-                       }
-               }
-               strbuf_list_free(pair);
-       }
-}
-
-static void subprocess_stop(struct hashmap *hashmap, struct subprocess_entry 
*entry)
-{
-       if (!entry)
-               return;
-
-       entry->process.clean_on_exit = 0;
-       kill(entry->process.pid, SIGTERM);
-       finish_command(&entry->process);
-
-       hashmap_remove(hashmap, entry, NULL);
-}
-
-static void subprocess_exit_handler(struct child_process *process)
-{
-       sigchain_push(SIGPIPE, SIG_IGN);
-       /* Closing the pipe signals the subprocess to initiate a shutdown. */
-       close(process->in);
-       close(process->out);
-       sigchain_pop(SIGPIPE);
-       /* Finish command will wait until the shutdown is complete. */
-       finish_command(process);
-}
-
 static int start_multi_file_filter_fn(struct subprocess_entry *subprocess)
 {
        int err;
@@ -629,44 +565,6 @@ static int start_multi_file_filter_fn(struct 
subprocess_entry *subprocess)
        return err;
 }
 
-typedef int(*subprocess_start_fn)(struct subprocess_entry *entry);
-int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, 
const char *cmd,
-       subprocess_start_fn startfn)
-{
-       int err;
-       struct child_process *process;
-       const char *argv[] = { cmd, NULL };
-
-       entry->cmd = cmd;
-       process = &entry->process;
-
-       child_process_init(process);
-       process->argv = argv;
-       process->use_shell = 1;
-       process->in = -1;
-       process->out = -1;
-       process->clean_on_exit = 1;
-       process->clean_on_exit_handler = subprocess_exit_handler;
-
-       err = start_command(process);
-       if (err) {
-               error("cannot fork to run subprocess '%s'", cmd);
-               return err;
-       }
-
-       hashmap_entry_init(entry, strhash(cmd));
-
-       err = startfn(entry);
-       if (err) {
-               error("initialization for subprocess '%s' failed", cmd);
-               subprocess_stop(hashmap, entry);
-               return err;
-       }
-
-       hashmap_add(hashmap, entry);
-       return 0;
-}
-
 static int apply_multi_file_filter(const char *path, const char *src, size_t 
len,
                                   int fd, struct strbuf *dst, const char *cmd,
                                   const unsigned int wanted_capability)
diff --git a/sub-process.c b/sub-process.c
new file mode 100644
index 0000000000..536b60cced
--- /dev/null
+++ b/sub-process.c
@@ -0,0 +1,102 @@
+/*
+ * Generic implementation of background process infrastructure.
+ */
+#include "sub-process.h"
+#include "sigchain.h"
+#include "pkt-line.h"
+
+int cmd2process_cmp(const struct subprocess_entry *e1,
+                          const struct subprocess_entry *e2,
+                          const void *unused)
+{
+       return strcmp(e1->cmd, e2->cmd);
+}
+
+struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const 
char *cmd)
+{
+       struct subprocess_entry key;
+
+       hashmap_entry_init(&key, strhash(cmd));
+       key.cmd = cmd;
+       return hashmap_get(hashmap, &key, NULL);
+}
+
+void subprocess_read_status(int fd, struct strbuf *status)
+{
+       struct strbuf **pair;
+       char *line;
+       for (;;) {
+               line = packet_read_line(fd, NULL);
+               if (!line)
+                       break;
+               pair = strbuf_split_str(line, '=', 2);
+               if (pair[0] && pair[0]->len && pair[1]) {
+                       /* the last "status=<foo>" line wins */
+                       if (!strcmp(pair[0]->buf, "status=")) {
+                               strbuf_reset(status);
+                               strbuf_addbuf(status, pair[1]);
+                       }
+               }
+               strbuf_list_free(pair);
+       }
+}
+
+void subprocess_stop(struct hashmap *hashmap, struct subprocess_entry *entry)
+{
+       if (!entry)
+               return;
+
+       entry->process.clean_on_exit = 0;
+       kill(entry->process.pid, SIGTERM);
+       finish_command(&entry->process);
+
+       hashmap_remove(hashmap, entry, NULL);
+}
+
+static void subprocess_exit_handler(struct child_process *process)
+{
+       sigchain_push(SIGPIPE, SIG_IGN);
+       /* Closing the pipe signals the subprocess to initiate a shutdown. */
+       close(process->in);
+       close(process->out);
+       sigchain_pop(SIGPIPE);
+       /* Finish command will wait until the shutdown is complete. */
+       finish_command(process);
+}
+
+int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, 
const char *cmd,
+       subprocess_start_fn startfn)
+{
+       int err;
+       struct child_process *process;
+       const char *argv[] = { cmd, NULL };
+
+       entry->cmd = cmd;
+       process = &entry->process;
+
+       child_process_init(process);
+       process->argv = argv;
+       process->use_shell = 1;
+       process->in = -1;
+       process->out = -1;
+       process->clean_on_exit = 1;
+       process->clean_on_exit_handler = subprocess_exit_handler;
+
+       err = start_command(process);
+       if (err) {
+               error("cannot fork to run subprocess '%s'", cmd);
+               return err;
+       }
+
+       hashmap_entry_init(entry, strhash(cmd));
+
+       err = startfn(entry);
+       if (err) {
+               error("initialization for subprocess '%s' failed", cmd);
+               subprocess_stop(hashmap, entry);
+               return err;
+       }
+
+       hashmap_add(hashmap, entry);
+       return 0;
+}
diff --git a/sub-process.h b/sub-process.h
new file mode 100644
index 0000000000..a88e782bfc
--- /dev/null
+++ b/sub-process.h
@@ -0,0 +1,49 @@
+#ifndef SUBPROCESS_H
+#define SUBPROCESS_H
+
+#include "git-compat-util.h"
+#include "hashmap.h"
+#include "run-command.h"
+
+/*
+ * Generic implementation of background process infrastructure.
+ * See Documentation/technical/api-background-process.txt.
+ */
+
+ /* data structures */
+
+struct subprocess_entry {
+       struct hashmap_entry ent; /* must be the first member! */
+       const char *cmd;
+       struct child_process process;
+};
+
+/* subprocess functions */
+
+int cmd2process_cmp(const struct subprocess_entry *e1,
+       const struct subprocess_entry *e2, const void *unused);
+
+typedef int(*subprocess_start_fn)(struct subprocess_entry *entry);
+int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, 
const char *cmd,
+               subprocess_start_fn startfn);
+
+void subprocess_stop(struct hashmap *hashmap, struct subprocess_entry *entry);
+
+struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const 
char *cmd);
+
+/* subprocess helper functions */
+
+static inline struct child_process *subprocess_get_child_process(
+               struct subprocess_entry *entry)
+{
+       return &entry->process;
+}
+
+/*
+ * Helper function that will read packets looking for "status=<foo>"
+ * key/value pairs and return the value from the last "status" packet
+ */
+
+void subprocess_read_status(int fd, struct strbuf *status);
+
+#endif
-- 
2.12.0.windows.1.33.g243d9b384c

Reply via email to