git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2 6/7] run-command: create start_bg_command

From
JGJeff Hostetler via GitGitGadget <gitgitgadget@gmail.com>
Date
Sep 20, 2021, 15:36 UTC
Message-ID
<0822118c4b50ddd3668ded97a89cab2c47895078.1632152178.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.1040.v2.git.1632152178.gitgitgadget@gmail.com>
From: Jeff Hostetler <jeffhost@microsoft.com>

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>
---
 run-command.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++
 run-command.h |  57 ++++++++++++++++++++++
 2 files changed, 186 insertions(+)
diff --git a/run-command.c b/run-command.c
index 3e4e082e94d..76bbef9d96d 100644
--- a/run-command.c
+++ b/run-command.c
@@ -1901,3 +1901,132 @@ void prepare_other_repo_env(struct strvec *env_array, const char *new_git_dir)
 	}
 	strvec_pushf(env_array, "%s=%s", GIT_DIR_ENVIRONMENT, new_git_dir);
 }
+
+enum start_bg_result start_bg_command(struct child_process *cmd,
+				      start_bg_wait_cb *wait_cb,
+				      void *cb_data,
+				      unsigned int timeout_sec)
+{
+	enum start_bg_result sbgr = SBGR_ERROR;
+	int ret;
+	int wait_status;
+	pid_t pid_seen;
+	time_t time_limit;
+
+	/*
+	 * We do not allow clean-on-exit because the child process
+	 * should persist in the background and possibly/probably
+	 * after this process exits.  So we don't want to kill the
+	 * child during our atexit routine.
+	 */
+	if (cmd->clean_on_exit)
+		BUG("start_bg_command() does not allow non-zero clean_on_exit");
+
+	if (!cmd->trace2_child_class)
+		cmd->trace2_child_class = "background";
+
+	ret = start_command(cmd);
+	if (ret) {
+		/*
+		 * We assume that if `start_command()` fails, we
+		 * either get a complete `trace2_child_start() /
+		 * trace2_child_exit()` pair or it fails before the
+		 * `trace2_child_start()` is emitted, so we do not
+		 * need to worry about it here.
+		 *
+		 * We also assume that `start_command()` does not add
+		 * us to the cleanup list.  And that it calls
+		 * calls `child_process_clear()`.
+		 */
+		sbgr = SBGR_ERROR;
+		goto done;
+	}
+
+	time(&time_limit);
+	time_limit += timeout_sec;
+
+wait:
+	pid_seen = waitpid(cmd->pid, &wait_status, WNOHANG);
+
+	if (!pid_seen) {
+		/*
+		 * The child is currently running.  Ask the callback
+		 * if the child is ready to do work or whether we
+		 * should keep waiting for it to boot up.
+		 */
+		ret = (*wait_cb)(cmd, cb_data);
+		if (!ret) {
+			/*
+			 * The child is running and "ready".
+			 */
+			trace2_child_ready(cmd, "ready");
+			sbgr = SBGR_READY;
+			goto done;
+		} else if (ret > 0) {
+			/*
+			 * The callback said to give it more time to boot up
+			 * (subject to our timeout limit).
+			 */
+			time_t now;
+
+			time(&now);
+			if (now < time_limit)
+				goto wait;
+
+			/*
+			 * Our timeout has expired.  We don't try to
+			 * kill the child, but rather let it continue
+			 * (hopefully) trying to startup.
+			 */
+			trace2_child_ready(cmd, "timeout");
+			sbgr = SBGR_TIMEOUT;
+			goto done;
+		} else {
+			/*
+			 * The cb gave up on this child.  It is still running,
+			 * but our cb got an error trying to probe it.
+			 */
+			trace2_child_ready(cmd, "error");
+			sbgr = SBGR_CB_ERROR;
+			goto done;
+		}
+	}
+
+	else if (pid_seen == cmd->pid) {
+		int child_code = -1;
+
+		/*
+		 * The child started, but exited or was terminated
+		 * before becoming "ready".
+		 *
+		 * We try to match the behavior of `wait_or_whine()`
+		 * WRT the handling of WIFSIGNALED() and WIFEXITED()
+		 * and convert the child's status to a return code for
+		 * tracing purposes and emit the `trace2_child_exit()`
+		 * event.
+		 *
+		 * We do not want the wait_or_whine() error message
+		 * because we will be called by client-side library
+		 * routines.
+		 */
+		if (WIFEXITED(wait_status))
+			child_code = WEXITSTATUS(wait_status);
+		else if (WIFSIGNALED(wait_status))
+			child_code = WTERMSIG(wait_status) + 128;
+		trace2_child_exit(cmd, child_code);
+
+		sbgr = SBGR_DIED;
+		goto done;
+	}
+
+	else if (pid_seen < 0 && errno == EINTR)
+		goto wait;
+
+	trace2_child_exit(cmd, -1);
+	sbgr = SBGR_ERROR;
+
+done:
+	child_process_clear(cmd);
+	invalidate_lstat_cache();
+	return sbgr;
+}
diff --git a/run-command.h b/run-command.h
index af1296769f9..17b1b80c3d7 100644
--- a/run-command.h
+++ b/run-command.h
@@ -496,4 +496,61 @@ int run_processes_parallel_tr2(int n, get_next_task_fn, start_failure_fn,
  */
 void prepare_other_repo_env(struct strvec *env_array, const char *new_git_dir);
 
+/**
+ * Possible return values for start_bg_command().
+ */
+enum start_bg_result {
+	/* child process is "ready" */
+	SBGR_READY = 0,
+
+	/* child process could not be started */
+	SBGR_ERROR,
+
+	/* callback error when testing for "ready" */
+	SBGR_CB_ERROR,
+
+	/* timeout expired waiting for child to become "ready" */
+	SBGR_TIMEOUT,
+
+	/* child process exited or was signalled before becomming "ready" */
+	SBGR_DIED,
+};
+
+/**
+ * Callback used by start_bg_command() to ask whether the
+ * child process is ready or needs more time to become "ready".
+ *
+ * The callback will receive the cmd and cb_data arguments given to
+ * start_bg_command().
+ *
+ * Returns 1 is child needs more time (subject to the requested timeout).
+ * Returns 0 if child is "ready".
+ * Returns -1 on any error and cause start_bg_command() to also error out.
+ */
+typedef int(start_bg_wait_cb)(const struct child_process *cmd, void *cb_data);
+
+/**
+ * Start a command in the background.  Wait long enough for the child
+ * to become "ready" (as defined by the provided callback).  Capture
+ * immediate errors (like failure to start) and any immediate exit
+ * status (such as a shutdown/signal before the child became "ready")
+ * and return this like start_command().
+ *
+ * We run a custom wait loop using the provided callback to wait for
+ * the child to start and become "ready".  This is limited by the given
+ * timeout value.
+ *
+ * If the child does successfully start and become "ready", we orphan
+ * it into the background.
+ *
+ * The caller must not call finish_command().
+ *
+ * The opaque cb_data argument will be forwarded to the callback for
+ * any instance data that it might require.  This may be NULL.
+ */
+enum start_bg_result start_bg_command(struct child_process *cmd,
+				      start_bg_wait_cb *wait_cb,
+				      void *cb_data,
+				      unsigned int timeout_sec);
+
 #endif
-- 
gitgitgadget
Previous: Jeff Hostetler via GitGitGadgetNext: Jeff Hostetler via GitGitGadget
Message 40 of 66 in “Builtin FSMonitor Part 1”
  1. 0/7 Builtin FSMonitor Part 1Jeff Hostetler via GitGitGadget, Sep 15, 2021
  2. 4/7 simple-ipc/ipc-win32: add trace2 debuggingJeff Hostetler via GitGitGadget, Sep 15, 2021
  3. Ævar Arnfjörð BjarmasonSep 16, 2021
  4. Jeff HostetlerSep 17, 2021
  5. 6/7 run-command: create start_bg_commandJeff Hostetler via GitGitGadget, Sep 15, 2021
  6. Taylor BlauSep 16, 2021
  7. Taylor BlauSep 16, 2021
  8. Ævar Arnfjörð BjarmasonSep 16, 2021
  9. 5/7 simple-ipc/ipc-win32: add Windows ACL to named pipeJeff Hostetler via GitGitGadget, Sep 15, 2021
  10. Ævar Arnfjörð BjarmasonSep 16, 2021
  11. Jeff HostetlerSep 17, 2021
  12. Ævar Arnfjörð BjarmasonSep 17, 2021
  13. 1/7 trace2: fix memory leak of thread nameJeff Hostetler via GitGitGadget, Sep 15, 2021
  14. Junio C HamanoSep 15, 2021
  15. Taylor BlauSep 16, 2021
  16. Ævar Arnfjörð BjarmasonSep 16, 2021
  17. Taylor BlauSep 16, 2021
  18. Ævar Arnfjörð BjarmasonSep 16, 2021
  19. Jeff HostetlerSep 16, 2021
  20. Ævar Arnfjörð BjarmasonSep 16, 2021
  21. Junio C HamanoSep 16, 2021
  22. 3/7 simple-ipc: move definition of ipc_active_state outside of ifdefJeff Hostetler via GitGitGadget, Sep 15, 2021
  23. Junio C HamanoSep 15, 2021
  24. Jeff HostetlerSep 17, 2021
  25. Carlo ArenasSep 18, 2021
  26. Junio C HamanoSep 20, 2021
  27. 2/7 simple-ipc: preparations for supporting binary messages.Jeff Hostetler via GitGitGadget, Sep 15, 2021
  28. Eric SunshineSep 15, 2021
  29. Jeff HostetlerSep 17, 2021
  30. 7/7 t/helper/simple-ipc: convert test-simple-ipc to use start_bg_commandJeff Hostetler via GitGitGadget, Sep 15, 2021
  31. Taylor BlauSep 16, 2021
  32. Jeff HostetlerSep 17, 2021
  33. Ævar Arnfjörð BjarmasonSep 18, 2021
  34. Ævar Arnfjörð BjarmasonSep 16, 2021
  35. 0/7 Builtin FSMonitor Part 1Jeff Hostetler via GitGitGadget, Sep 20, 2021
  36. 1/7 trace2: add trace2_child_ready() to report on background childrenJeff Hostetler via GitGitGadget, Sep 20, 2021
  37. 2/7 simple-ipc: preparations for supporting binary messages.Jeff Hostetler via GitGitGadget, Sep 20, 2021
  38. 3/7 simple-ipc: move definition of ipc_active_state outside of ifdefJeff Hostetler via GitGitGadget, Sep 20, 2021
  39. 4/7 simple-ipc/ipc-win32: add trace2 debuggingJeff Hostetler via GitGitGadget, Sep 20, 2021
  40. 6/7 run-command: create start_bg_commandJeff Hostetler via GitGitGadget, Sep 20, 2021
  41. 5/7 simple-ipc/ipc-win32: add Windows ACL to named pipeJeff Hostetler via GitGitGadget, Sep 20, 2021
  42. 7/7 t/helper/simple-ipc: convert test-simple-ipc to use start_bg_commandJeff Hostetler via GitGitGadget, Sep 20, 2021
  43. Ævar Arnfjörð BjarmasonSep 23, 2021
  44. Jeff HostetlerSep 23, 2021
  45. Junio C HamanoSep 23, 2021
  46. Adam DinwoodieNov 4, 2021
  47. Ramsay JonesNov 4, 2021
  48. Jeff HostetlerNov 8, 2021
  49. Johannes SchindelinNov 8, 2021
  50. Ramsay JonesNov 9, 2021
  51. Johannes SchindelinNov 9, 2021
  52. Junio C HamanoNov 9, 2021
  53. Johannes SchindelinNov 10, 2021
  54. Adam DinwoodieNov 12, 2021
  55. Junio C HamanoNov 12, 2021
  56. Adam DinwoodieNov 12, 2021
  57. Johannes SchindelinNov 16, 2021
  58. Johannes SchindelinNov 16, 2021
  59. Ramsay JonesNov 13, 2021
  60. Ramsay JonesNov 14, 2021
  61. Adam DinwoodieNov 14, 2021
  62. Ævar Arnfjörð BjarmasonSep 23, 2021
  63. Jeff HostetlerSep 23, 2021
  64. Ævar Arnfjörð BjarmasonSep 23, 2021
  65. Jeff HostetlerSep 27, 2021
  66. Taylor BlauSep 23, 2021

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.