[PATCH v7 09/12] hook: add jobs option
- From
Adrian Ratiu <adrian.ratiu@collabora.com>
- Date
- Jan 21, 2026, 21:54 UTC
- Message-ID
- <20260121215436.1473800-10-adrian.ratiu@collabora.com>
- In-Reply-To
- <20260121215436.1473800-1-adrian.ratiu@collabora.com>
Allow the API callers to specify the number of jobs across which hook execution can be parallelized. It defaults to 1 and no hook currently changes it, so all hooks run sequentially as before.
This allows us to both pave the way for parallel hook execution (that will be a follow-up patch series building upon this) and to finish the API conversion of builtin/receive-pack.c, keeping the output muxer thread design as Peff suggested.
When .jobs == 1 nothing changes, the simple "copy_to_sideband" mux still outputs directly via sideband channel 2, keeping the current (mostly) real-time output characteristics, avoids unecessary poll delays or deadlock risks.
When .jobs > 1, a more complex muxer is needed, to buffer the hook output and avoid interleaving. After working on this mux I quickly realized I was re-implementing run-command with ungroup=0 so that ideas was dropped in favor of run-command which outputs to stderr. Then run-command's stderr just connects to the simple muxer input.
Maybe it's better to understand using ascii graphics:
[ Sequential (jobs = 1) ] [ Parallel (jobs > 1) ]
+--------------+ +--------+ +--------+
| Hook Process | | Hook 1 | | Hook 2 |
+--------------+ +--------+ +--------+
| | |
| stderr (inherited) | stderr pipe |
| | (captured) |
v v v
+-------------------------------------------------------------+
| Parent Process |
| |
| (direct write) [run-command (buffered)] |
| | | |
| | | writes |
| v v |
| +-------------------------------------------+ |
| | stderr (FD 2) | |
| +-------------------------------------------+ |
| | |
| | (dup2'd to pipe) |
| v |
| +-------------------------+ |
| | sideband "muxer" thread | |
| +-------------------------+ |
+-------------------------------------------------------------+When use_sideband == 0, the sideband muxer thread is missing, so the same architecture just outputs via the parent stderr stream.
See the following commits for the hook API conversions doing this, using pre-existing sideband thread logic from `copy_to_sideband`.
Suggested-by: Jeff King <peff@peff.net> Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com> --- hook.c | 7 +++++-- hook.h | 9 +++++++++ 2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/hook.c b/hook.c index fde1f88ce8..aafed134af 100644 --- a/hook.c +++ b/hook.c @@ -152,8 +152,8 @@ int run_hooks_opt(struct repository *r, const char *hook_name, .tr2_category = "hook", .tr2_label = hook_name, - .processes = 1, - .ungroup = 1, + .processes = options->jobs, + .ungroup = options->jobs <= 1, .get_next_task = pick_next_hook, .start_failure = notify_start_failure, @@ -169,6 +169,9 @@ int run_hooks_opt(struct repository *r, const char *hook_name, if (options->path_to_stdin && options->feed_pipe) BUG("options path_to_stdin and feed_pipe are mutually exclusive"); + if (!options->jobs) + BUG("run_hooks_opt must be called with options.jobs >= 1"); + if (options->invoked_hook) *options->invoked_hook = 0; diff --git a/hook.h b/hook.h index 7cbeef0a1e..3db860bf3c 100644 --- a/hook.h +++ b/hook.h @@ -16,6 +16,14 @@ struct run_hooks_opt /* Emit an error if the hook is missing */ unsigned int error_if_missing:1; + /** + * Number of processes to parallelize across. + * + * If > 1, output will be buffered and de-interleaved (ungroup=0). + * If == 1, output will be real-time (ungroup=1). + */ + unsigned int jobs; + /** * An optional initial working directory for the hook, * translates to "struct child_process"'s "dir" member. @@ -86,6 +94,7 @@ struct run_hooks_opt .env = STRVEC_INIT, \ .args = STRVEC_INIT, \ .stdout_to_stderr = 1, \ + .jobs = 1, \ } struct hook_cb_data {
-- 2.52.0.732.gb351b5166d.dirty