sub-process: move sub-process functions into separate files

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 <benpeart@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Ben Peart committed May 5, 2017 at 11:28 UTC 99605d62e8e7e568035dc953b24b79b3d52f0522
5 files changed +212 -103
Documentation/technical/api-sub-process.txt new
+59
@@ -0,0 +1,59 @@
1 +sub-process API
2 +===============
3 +
4 +The sub-process API makes it possible to run background sub-processes
5 +for the entire lifetime of a Git invocation. If Git needs to communicate
6 +with an external process multiple times, then this can reduces the process
7 +invocation overhead. Git and the sub-process communicate through stdin and
8 +stdout.
9 +
10 +The sub-processes are kept in a hashmap by command name and looked up
11 +via the subprocess_find_entry function. If an existing instance can not
12 +be found then a new process should be created and started. When the
13 +parent git command terminates, all sub-processes are also terminated.
14 +
15 +This API is based on the run-command API.
16 +
17 +Data structures
18 +---------------
19 +
20 +* `struct subprocess_entry`
21 +
22 +The sub-process structure. Members should not be accessed directly.
23 +
24 +Types
25 +-----
26 +
27 +'int(*subprocess_start_fn)(struct subprocess_entry *entry)'::
28 +
29 + User-supplied function to initialize the sub-process. This is
30 + typically used to negotiate the interface version and capabilities.
31 +
32 +
33 +Functions
34 +---------
35 +
36 +`cmd2process_cmp`::
37 +
38 + Function to test two subprocess hashmap entries for equality.
39 +
40 +`subprocess_start`::
41 +
42 + Start a subprocess and add it to the subprocess hashmap.
43 +
44 +`subprocess_stop`::
45 +
46 + Kill a subprocess and remove it from the subprocess hashmap.
47 +
48 +`subprocess_find_entry`::
49 +
50 + Find a subprocess in the subprocess hashmap.
51 +
52 +`subprocess_get_child_process`::
53 +
54 + Get the underlying `struct child_process` from a subprocess.
55 +
56 +`subprocess_read_status`::
57 +
58 + Helper function to read packets looking for the last "status=<foo>"
59 + key/value pair.
Makefile
+1
@@ -838,6 +838,7 @@ LIB_OBJS += streaming.o
838 LIB_OBJS += string-list.o
839 LIB_OBJS += submodule.o
840 LIB_OBJS += submodule-config.o
841 +LIB_OBJS += sub-process.o
842 LIB_OBJS += symlinks.o
843 LIB_OBJS += tag.o
844 LIB_OBJS += tempfile.o
convert.c
+1 -103
@@ -4,6 +4,7 @@
4 #include "quote.h"
5 #include "sigchain.h"
6 #include "pkt-line.h"
7 +#include "sub-process.h"
8
9 /*
10 * 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
497 #define CAP_CLEAN (1u<<0)
498 #define CAP_SMUDGE (1u<<1)
499
499 -struct subprocess_entry {
500 - struct hashmap_entry ent; /* must be the first member! */
501 - const char *cmd;
502 - struct child_process process;
503 -};
504 -
500 struct cmd2process {
501 struct subprocess_entry subprocess; /* must be the first member! */
502 unsigned int supported_capabilities;
@@ -510,65 +505,6 @@ struct cmd2process {
505 static int subprocess_map_initialized;
506 static struct hashmap subprocess_map;
507
513 -static int cmd2process_cmp(const struct subprocess_entry *e1,
514 - const struct subprocess_entry *e2,
515 - const void *unused)
516 -{
517 - return strcmp(e1->cmd, e2->cmd);
518 -}
519 -
520 -static struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const char *cmd)
521 -{
522 - struct subprocess_entry key;
523 -
524 - hashmap_entry_init(&key, strhash(cmd));
525 - key.cmd = cmd;
526 - return hashmap_get(hashmap, &key, NULL);
527 -}
528 -
529 -static void subprocess_read_status(int fd, struct strbuf *status)
530 -{
531 - struct strbuf **pair;
532 - char *line;
533 - for (;;) {
534 - line = packet_read_line(fd, NULL);
535 - if (!line)
536 - break;
537 - pair = strbuf_split_str(line, '=', 2);
538 - if (pair[0] && pair[0]->len && pair[1]) {
539 - /* the last "status=<foo>" line wins */
540 - if (!strcmp(pair[0]->buf, "status=")) {
541 - strbuf_reset(status);
542 - strbuf_addbuf(status, pair[1]);
543 - }
544 - }
545 - strbuf_list_free(pair);
546 - }
547 -}
548 -
549 -static void subprocess_stop(struct hashmap *hashmap, struct subprocess_entry *entry)
550 -{
551 - if (!entry)
552 - return;
553 -
554 - entry->process.clean_on_exit = 0;
555 - kill(entry->process.pid, SIGTERM);
556 - finish_command(&entry->process);
557 -
558 - hashmap_remove(hashmap, entry, NULL);
559 -}
560 -
561 -static void subprocess_exit_handler(struct child_process *process)
562 -{
563 - sigchain_push(SIGPIPE, SIG_IGN);
564 - /* Closing the pipe signals the subprocess to initiate a shutdown. */
565 - close(process->in);
566 - close(process->out);
567 - sigchain_pop(SIGPIPE);
568 - /* Finish command will wait until the shutdown is complete. */
569 - finish_command(process);
570 -}
571 -
508 static int start_multi_file_filter_fn(struct subprocess_entry *subprocess)
509 {
510 int err;
@@ -629,44 +565,6 @@ done:
565 return err;
566 }
567
632 -typedef int(*subprocess_start_fn)(struct subprocess_entry *entry);
633 -int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, const char *cmd,
634 - subprocess_start_fn startfn)
635 -{
636 - int err;
637 - struct child_process *process;
638 - const char *argv[] = { cmd, NULL };
639 -
640 - entry->cmd = cmd;
641 - process = &entry->process;
642 -
643 - child_process_init(process);
644 - process->argv = argv;
645 - process->use_shell = 1;
646 - process->in = -1;
647 - process->out = -1;
648 - process->clean_on_exit = 1;
649 - process->clean_on_exit_handler = subprocess_exit_handler;
650 -
651 - err = start_command(process);
652 - if (err) {
653 - error("cannot fork to run subprocess '%s'", cmd);
654 - return err;
655 - }
656 -
657 - hashmap_entry_init(entry, strhash(cmd));
658 -
659 - err = startfn(entry);
660 - if (err) {
661 - error("initialization for subprocess '%s' failed", cmd);
662 - subprocess_stop(hashmap, entry);
663 - return err;
664 - }
665 -
666 - hashmap_add(hashmap, entry);
667 - return 0;
668 -}
669 -
568 static int apply_multi_file_filter(const char *path, const char *src, size_t len,
569 int fd, struct strbuf *dst, const char *cmd,
570 const unsigned int wanted_capability)
sub-process.c new
+102
@@ -0,0 +1,102 @@
1 +/*
2 + * Generic implementation of background process infrastructure.
3 + */
4 +#include "sub-process.h"
5 +#include "sigchain.h"
6 +#include "pkt-line.h"
7 +
8 +int cmd2process_cmp(const struct subprocess_entry *e1,
9 + const struct subprocess_entry *e2,
10 + const void *unused)
11 +{
12 + return strcmp(e1->cmd, e2->cmd);
13 +}
14 +
15 +struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const char *cmd)
16 +{
17 + struct subprocess_entry key;
18 +
19 + hashmap_entry_init(&key, strhash(cmd));
20 + key.cmd = cmd;
21 + return hashmap_get(hashmap, &key, NULL);
22 +}
23 +
24 +void subprocess_read_status(int fd, struct strbuf *status)
25 +{
26 + struct strbuf **pair;
27 + char *line;
28 + for (;;) {
29 + line = packet_read_line(fd, NULL);
30 + if (!line)
31 + break;
32 + pair = strbuf_split_str(line, '=', 2);
33 + if (pair[0] && pair[0]->len && pair[1]) {
34 + /* the last "status=<foo>" line wins */
35 + if (!strcmp(pair[0]->buf, "status=")) {
36 + strbuf_reset(status);
37 + strbuf_addbuf(status, pair[1]);
38 + }
39 + }
40 + strbuf_list_free(pair);
41 + }
42 +}
43 +
44 +void subprocess_stop(struct hashmap *hashmap, struct subprocess_entry *entry)
45 +{
46 + if (!entry)
47 + return;
48 +
49 + entry->process.clean_on_exit = 0;
50 + kill(entry->process.pid, SIGTERM);
51 + finish_command(&entry->process);
52 +
53 + hashmap_remove(hashmap, entry, NULL);
54 +}
55 +
56 +static void subprocess_exit_handler(struct child_process *process)
57 +{
58 + sigchain_push(SIGPIPE, SIG_IGN);
59 + /* Closing the pipe signals the subprocess to initiate a shutdown. */
60 + close(process->in);
61 + close(process->out);
62 + sigchain_pop(SIGPIPE);
63 + /* Finish command will wait until the shutdown is complete. */
64 + finish_command(process);
65 +}
66 +
67 +int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, const char *cmd,
68 + subprocess_start_fn startfn)
69 +{
70 + int err;
71 + struct child_process *process;
72 + const char *argv[] = { cmd, NULL };
73 +
74 + entry->cmd = cmd;
75 + process = &entry->process;
76 +
77 + child_process_init(process);
78 + process->argv = argv;
79 + process->use_shell = 1;
80 + process->in = -1;
81 + process->out = -1;
82 + process->clean_on_exit = 1;
83 + process->clean_on_exit_handler = subprocess_exit_handler;
84 +
85 + err = start_command(process);
86 + if (err) {
87 + error("cannot fork to run subprocess '%s'", cmd);
88 + return err;
89 + }
90 +
91 + hashmap_entry_init(entry, strhash(cmd));
92 +
93 + err = startfn(entry);
94 + if (err) {
95 + error("initialization for subprocess '%s' failed", cmd);
96 + subprocess_stop(hashmap, entry);
97 + return err;
98 + }
99 +
100 + hashmap_add(hashmap, entry);
101 + return 0;
102 +}
sub-process.h new
+49
@@ -0,0 +1,49 @@
1 +#ifndef SUBPROCESS_H
2 +#define SUBPROCESS_H
3 +
4 +#include "git-compat-util.h"
5 +#include "hashmap.h"
6 +#include "run-command.h"
7 +
8 +/*
9 + * Generic implementation of background process infrastructure.
10 + * See Documentation/technical/api-background-process.txt.
11 + */
12 +
13 + /* data structures */
14 +
15 +struct subprocess_entry {
16 + struct hashmap_entry ent; /* must be the first member! */
17 + const char *cmd;
18 + struct child_process process;
19 +};
20 +
21 +/* subprocess functions */
22 +
23 +int cmd2process_cmp(const struct subprocess_entry *e1,
24 + const struct subprocess_entry *e2, const void *unused);
25 +
26 +typedef int(*subprocess_start_fn)(struct subprocess_entry *entry);
27 +int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, const char *cmd,
28 + subprocess_start_fn startfn);
29 +
30 +void subprocess_stop(struct hashmap *hashmap, struct subprocess_entry *entry);
31 +
32 +struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const char *cmd);
33 +
34 +/* subprocess helper functions */
35 +
36 +static inline struct child_process *subprocess_get_child_process(
37 + struct subprocess_entry *entry)
38 +{
39 + return &entry->process;
40 +}
41 +
42 +/*
43 + * Helper function that will read packets looking for "status=<foo>"
44 + * key/value pairs and return the value from the last "status" packet
45 + */
46 +
47 +void subprocess_read_status(int fd, struct strbuf *status);
48 +
49 +#endif