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>
This commit is contained in:
parent
8750249053
commit
fdb1322651
129
run-command.c
129
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;
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user