run-command: create start_bg_command
Create a variation of `run_command()` and `start_command()` to launch a command into the background and optionally wait for it to become "ready" before returning. Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Jeff Hostetler committed
Sep 20, 2021 at 15:36 UTC
fdb1322651a41a4e754bfac23513c0fa32dc1db8
2 files changed
+186
run-command.c
+129
@@ -1901,3 +1901,132 @@ void prepare_other_repo_env(struct strvec *env_array, const char *new_git_dir)
1901
}
1902
strvec_pushf(env_array, "%s=%s", GIT_DIR_ENVIRONMENT, new_git_dir);
1903
}
1904
+
1905
+enum start_bg_result start_bg_command(struct child_process *cmd,
1906
+ start_bg_wait_cb *wait_cb,
1907
+ void *cb_data,
1908
+ unsigned int timeout_sec)
1909
+{
1910
+ enum start_bg_result sbgr = SBGR_ERROR;
1911
+ int ret;
1912
+ int wait_status;
1913
+ pid_t pid_seen;
1914
+ time_t time_limit;
1915
+
1916
+ /*
1917
+ * We do not allow clean-on-exit because the child process
1918
+ * should persist in the background and possibly/probably
1919
+ * after this process exits. So we don't want to kill the
1920
+ * child during our atexit routine.
1921
+ */
1922
+ if (cmd->clean_on_exit)
1923
+ BUG("start_bg_command() does not allow non-zero clean_on_exit");
1924
+
1925
+ if (!cmd->trace2_child_class)
1926
+ cmd->trace2_child_class = "background";
1927
+
1928
+ ret = start_command(cmd);
1929
+ if (ret) {
1930
+ /*
1931
+ * We assume that if `start_command()` fails, we
1932
+ * either get a complete `trace2_child_start() /
1933
+ * trace2_child_exit()` pair or it fails before the
1934
+ * `trace2_child_start()` is emitted, so we do not
1935
+ * need to worry about it here.
1936
+ *
1937
+ * We also assume that `start_command()` does not add
1938
+ * us to the cleanup list. And that it calls
1939
+ * calls `child_process_clear()`.
1940
+ */
1941
+ sbgr = SBGR_ERROR;
1942
+ goto done;
1943
+ }
1944
+
1945
+ time(&time_limit);
1946
+ time_limit += timeout_sec;
1947
+
1948
+wait:
1949
+ pid_seen = waitpid(cmd->pid, &wait_status, WNOHANG);
1950
+
1951
+ if (!pid_seen) {
1952
+ /*
1953
+ * The child is currently running. Ask the callback
1954
+ * if the child is ready to do work or whether we
1955
+ * should keep waiting for it to boot up.
1956
+ */
1957
+ ret = (*wait_cb)(cmd, cb_data);
1958
+ if (!ret) {
1959
+ /*
1960
+ * The child is running and "ready".
1961
+ */
1962
+ trace2_child_ready(cmd, "ready");
1963
+ sbgr = SBGR_READY;
1964
+ goto done;
1965
+ } else if (ret > 0) {
1966
+ /*
1967
+ * The callback said to give it more time to boot up
1968
+ * (subject to our timeout limit).
1969
+ */
1970
+ time_t now;
1971
+
1972
+ time(&now);
1973
+ if (now < time_limit)
1974
+ goto wait;
1975
+
1976
+ /*
1977
+ * Our timeout has expired. We don't try to
1978
+ * kill the child, but rather let it continue
1979
+ * (hopefully) trying to startup.
1980
+ */
1981
+ trace2_child_ready(cmd, "timeout");
1982
+ sbgr = SBGR_TIMEOUT;
1983
+ goto done;
1984
+ } else {
1985
+ /*
1986
+ * The cb gave up on this child. It is still running,
1987
+ * but our cb got an error trying to probe it.
1988
+ */
1989
+ trace2_child_ready(cmd, "error");
1990
+ sbgr = SBGR_CB_ERROR;
1991
+ goto done;
1992
+ }
1993
+ }
1994
+
1995
+ else if (pid_seen == cmd->pid) {
1996
+ int child_code = -1;
1997
+
1998
+ /*
1999
+ * The child started, but exited or was terminated
2000
+ * before becoming "ready".
2001
+ *
2002
+ * We try to match the behavior of `wait_or_whine()`
2003
+ * WRT the handling of WIFSIGNALED() and WIFEXITED()
2004
+ * and convert the child's status to a return code for
2005
+ * tracing purposes and emit the `trace2_child_exit()`
2006
+ * event.
2007
+ *
2008
+ * We do not want the wait_or_whine() error message
2009
+ * because we will be called by client-side library
2010
+ * routines.
2011
+ */
2012
+ if (WIFEXITED(wait_status))
2013
+ child_code = WEXITSTATUS(wait_status);
2014
+ else if (WIFSIGNALED(wait_status))
2015
+ child_code = WTERMSIG(wait_status) + 128;
2016
+ trace2_child_exit(cmd, child_code);
2017
+
2018
+ sbgr = SBGR_DIED;
2019
+ goto done;
2020
+ }
2021
+
2022
+ else if (pid_seen < 0 && errno == EINTR)
2023
+ goto wait;
2024
+
2025
+ trace2_child_exit(cmd, -1);
2026
+ sbgr = SBGR_ERROR;
2027
+
2028
+done:
2029
+ child_process_clear(cmd);
2030
+ invalidate_lstat_cache();
2031
+ return sbgr;
2032
+}
run-command.h
+57
@@ -496,4 +496,61 @@ int run_processes_parallel_tr2(int n, get_next_task_fn, start_failure_fn,
496
*/
497
void prepare_other_repo_env(struct strvec *env_array, const char *new_git_dir);
498
499
+/**
500
+ * Possible return values for start_bg_command().
501
+ */
502
+enum start_bg_result {
503
+ /* child process is "ready" */
504
+ SBGR_READY = 0,
505
+
506
+ /* child process could not be started */
507
+ SBGR_ERROR,
508
+
509
+ /* callback error when testing for "ready" */
510
+ SBGR_CB_ERROR,
511
+
512
+ /* timeout expired waiting for child to become "ready" */
513
+ SBGR_TIMEOUT,
514
+
515
+ /* child process exited or was signalled before becomming "ready" */
516
+ SBGR_DIED,
517
+};
518
+
519
+/**
520
+ * Callback used by start_bg_command() to ask whether the
521
+ * child process is ready or needs more time to become "ready".
522
+ *
523
+ * The callback will receive the cmd and cb_data arguments given to
524
+ * start_bg_command().
525
+ *
526
+ * Returns 1 is child needs more time (subject to the requested timeout).
527
+ * Returns 0 if child is "ready".
528
+ * Returns -1 on any error and cause start_bg_command() to also error out.
529
+ */
530
+typedef int(start_bg_wait_cb)(const struct child_process *cmd, void *cb_data);
531
+
532
+/**
533
+ * Start a command in the background. Wait long enough for the child
534
+ * to become "ready" (as defined by the provided callback). Capture
535
+ * immediate errors (like failure to start) and any immediate exit
536
+ * status (such as a shutdown/signal before the child became "ready")
537
+ * and return this like start_command().
538
+ *
539
+ * We run a custom wait loop using the provided callback to wait for
540
+ * the child to start and become "ready". This is limited by the given
541
+ * timeout value.
542
+ *
543
+ * If the child does successfully start and become "ready", we orphan
544
+ * it into the background.
545
+ *
546
+ * The caller must not call finish_command().
547
+ *
548
+ * The opaque cb_data argument will be forwarded to the callback for
549
+ * any instance data that it might require. This may be NULL.
550
+ */
551
+enum start_bg_result start_bg_command(struct child_process *cmd,
552
+ start_bg_wait_cb *wait_cb,
553
+ void *cb_data,
554
+ unsigned int timeout_sec);
555
+
556
#endif