~emersion/mrsh

54d8edcc8f4705160409aa67dfc623479588dfb7 — Simon Ser 8 months ago 507244c
Refresh jobs status after executing command

This allows us to notice that a process has finished without needing to
explicitely wait for it.
3 files changed, 50 insertions(+), 18 deletions(-)

M include/shell/job.h
M shell/job.c
M shell/task/task.c
M include/shell/job.h => include/shell/job.h +4 -0
@@ 84,6 84,10 @@ bool init_job_child_process(struct mrsh_state *state);
 */
void update_job(struct mrsh_state *state, pid_t pid, int stat);
/**
 * Refreshes status for all jobs.
 */
bool refresh_jobs_status(struct mrsh_state *state);
/**
 * Look up a job by its XBD Job Control Job ID.
 *
 * When using this to look up jobs internally, set interactive to false. This

M shell/job.c => shell/job.c +44 -18
@@ 232,34 232,50 @@ int job_poll(struct mrsh_job *job) {
	return proc_status;
}

static bool _job_wait(struct mrsh_state *state, pid_t pid) {
static bool _job_wait(struct mrsh_state *state, pid_t pid, int options) {
	struct mrsh_state_priv *priv = state_get_priv(state);

	assert(pid > 0 && pid != getpid());

	// We only want to be notified about stopped processes in the main
	// shell. Child processes want to block until their own children have
	// terminated.
	if (!priv->child) {
		options |= WUNTRACED;
	}

	while (true) {
		// We only want to be notified about stopped processes in the main
		// shell. Child processes want to block until their own children have
		// terminated.
		//
		// Here it's important to wait for a specific process: we don't want to
		// steal one of our grandchildren's status for one of our children.
		int stat;
		pid_t ret = waitpid(pid, &stat, priv->child ? 0 : WUNTRACED);
		if (ret < 0) {
		pid_t ret = waitpid(pid, &stat, options);
		if (ret == 0) { // no status available
			assert(options & WNOHANG);
			return true;
		} else if (ret < 0) {
			if (errno == EINTR) {
				continue;
			}
			perror("waitpid");
			return false;
		}
		assert(ret > 0);
		assert(ret == pid);

		update_job(state, ret, stat);
		return true;
	}
}

static struct mrsh_process *job_get_running_process(struct mrsh_job *job) {
	for (size_t j = 0; j < job->processes.len; ++j) {
		struct mrsh_process *proc = job->processes.data[j];
		if (process_poll(proc) == TASK_STATUS_WAIT) {
			return proc;
		}
	}
	return NULL;
}

int job_wait(struct mrsh_job *job) {
	while (true) {
		int status = job_poll(job);


@@ 267,16 283,9 @@ int job_wait(struct mrsh_job *job) {
			return status;
		}

		struct mrsh_process *wait_proc = NULL;
		for (size_t j = 0; j < job->processes.len; ++j) {
			struct mrsh_process *proc = job->processes.data[j];
			if (process_poll(proc) == TASK_STATUS_WAIT) {
				wait_proc = proc;
				break;
			}
		}
		struct mrsh_process *wait_proc = job_get_running_process(job);
		assert(wait_proc != NULL);
		if (!_job_wait(job->state, wait_proc->pid)) {
		if (!_job_wait(job->state, wait_proc->pid, 0)) {
			return TASK_STATUS_ERROR;
		}
	}


@@ 289,12 298,29 @@ int job_wait_process(struct mrsh_process *proc) {
			return status;
		}

		if (!_job_wait(proc->state, proc->pid)) {
		if (!_job_wait(proc->state, proc->pid, 0)) {
			return TASK_STATUS_ERROR;
		}
	}
}

bool refresh_jobs_status(struct mrsh_state *state) {
	struct mrsh_state_priv *priv = state_get_priv(state);

	for (size_t i = 0; i < priv->jobs.len; ++i) {
		struct mrsh_job *job = priv->jobs.data[i];
		struct mrsh_process *proc = job_get_running_process(job);
		if (proc == NULL) {
			continue;
		}
		if (!_job_wait(job->state, proc->pid, WNOHANG)) {
			return false;
		}
	}

	return true;
}

bool init_job_child_process(struct mrsh_state *state) {
	struct mrsh_state_priv *priv = state_get_priv(state);


M shell/task/task.c => shell/task/task.c +2 -0
@@ 403,6 403,8 @@ static void destroy_terminated_jobs(struct mrsh_state *state) {
		*previous = job_by_id(state, "%-", false);
	bool r = rand() % 2 == 0;

	refresh_jobs_status(state);

	for (size_t i = 0; i < priv->jobs.len; ++i) {
		struct mrsh_job *job = priv->jobs.data[i];