54d8edcc8f4705160409aa67dfc623479588dfb7 — Simon Ser 16 days 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];