diff mbox series

[v2,2/5] run-command: allow stdin for run_processes_parallel

Message ID patch-v2-2.5-9a178577dcc-20230208T191924Z-avarab@gmail.com (mailing list archive)
State Accepted
Commit 540267304d37d6257edb3144e770693071d8fbb7
Headers show
Series [v2,1/5] run-command.c: remove dead assignment in while-loop | expand

Commit Message

Ævar Arnfjörð Bjarmason Feb. 8, 2023, 7:21 p.m. UTC
From: Emily Shaffer <emilyshaffer@google.com>

While it makes sense not to inherit stdin from the parent process to
avoid deadlocking, it's not necessary to completely ban stdin to
children. An informed user should be able to configure stdin safely. By
setting `some_child.process.no_stdin=1` before calling `get_next_task()`
we provide a reasonable default behavior but enable users to set up
stdin streaming for themselves during the callback.

`some_child.process.stdout_to_stderr`, however, remains unmodifiable by
`get_next_task()` - the rest of the run_processes_parallel() API depends
on child output in stderr.

Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 run-command.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

Comments

Junio C Hamano Feb. 8, 2023, 9:09 p.m. UTC | #1
Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> From: Emily Shaffer <emilyshaffer@google.com>
>
> While it makes sense not to inherit stdin from the parent process to
> avoid deadlocking, it's not necessary to completely ban stdin to
> children.

I do not think deadlock avoidance is an issue.  Unpredictable
feeding of pieces of input into multiple children is.  One possible
semantics is to grab the input and dup/tee into all the children, so 
that each child gets its own copy to process.  A hook like "pre-receive",
when it has more than one scripts listening to the event, may want
such a semantics.  Another possible semantics is to give priority
among the children running simultaneously and feed only one child,
while starving others.

> An informed user should be able to configure stdin safely. By
> setting `some_child.process.no_stdin=1` before calling `get_next_task()`
> we provide a reasonable default behavior but enable users to set up
> stdin streaming for themselves during the callback.

I _think_ this alludes to the latter, e.g. "only one child is
allowed, and the one that controls what children are spawned sets
no_stdin for everybody but the chosen one".  We may want to be a bit
more explicit in the proposed log message and definitely in the
documentation.

The implementation is "nice".

> +	/*
> +	 * By default, do not inherit stdin from the parent process - otherwise,
> +	 * all children would share stdin! Users may overwrite this to provide
> +	 * something to the child's stdin by having their 'get_next_task'
> +	 * callback assign 0 to .no_stdin and an appropriate integer to .in.
> +	 */
> +	pp->children[i].process.no_stdin = 1;
> +
>  	code = opts->get_next_task(&pp->children[i].process,
>  				   opts->ungroup ? NULL : &pp->children[i].err,
>  				   opts->data,
> @@ -1601,7 +1609,6 @@ static int pp_start_one(struct parallel_processes *pp,
>  		pp->children[i].process.err = -1;
>  		pp->children[i].process.stdout_to_stderr = 1;
>  	}
> -	pp->children[i].process.no_stdin = 1;
>  
>  	if (start_command(&pp->children[i].process)) {
>  		if (opts->start_failure)
diff mbox series

Patch

diff --git a/run-command.c b/run-command.c
index b439c7974ca..6bd16acb060 100644
--- a/run-command.c
+++ b/run-command.c
@@ -1586,6 +1586,14 @@  static int pp_start_one(struct parallel_processes *pp,
 	if (i == opts->processes)
 		BUG("bookkeeping is hard");
 
+	/*
+	 * By default, do not inherit stdin from the parent process - otherwise,
+	 * all children would share stdin! Users may overwrite this to provide
+	 * something to the child's stdin by having their 'get_next_task'
+	 * callback assign 0 to .no_stdin and an appropriate integer to .in.
+	 */
+	pp->children[i].process.no_stdin = 1;
+
 	code = opts->get_next_task(&pp->children[i].process,
 				   opts->ungroup ? NULL : &pp->children[i].err,
 				   opts->data,
@@ -1601,7 +1609,6 @@  static int pp_start_one(struct parallel_processes *pp,
 		pp->children[i].process.err = -1;
 		pp->children[i].process.stdout_to_stderr = 1;
 	}
-	pp->children[i].process.no_stdin = 1;
 
 	if (start_command(&pp->children[i].process)) {
 		if (opts->start_failure)