[git commit] ash: jobs: Only clear gotsigchld when waiting for everything

Denys Vlasenko vda.linux at googlemail.com
Tue Feb 18 14:37:43 UTC 2020


commit: https://git.busybox.net/busybox/commit/?id=47eb979404735b9528538968cb5eaac7355a0c5a
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master

Upstream commit:

    Date: Sat, 19 May 2018 02:39:41 +0800
    jobs: Only clear gotsigchld when waiting for everything

    The gotsigchld flag is always cleared in dowait but not all callers
    of dowait will wait for everything.  In particular, when jp is set
    we only wait until the set job isn't running anymore.

    This patch fixes this by only clearing gotsigchld if jp is unset.
    It also changes the waitcmd to actually set jp which corresponds
    to the behaviour of bash/ksh93/mksh.

    The only other caller of dowait that doesn't wait for everything
    is the jobless reaper.  This is in fact redundant now that we wait
    after every simple command.  This patch removes it.

    Finally as every caller of dowait needs to wait until either the
    given job is not running, or until all terminated jobs have been
    processed, this patch moves the loop into dowait itself.

    Fixes: 03876c0743a5 ("eval: Reap zombies after built-in...")
    Signed-off-by: Herbert Xu <herbert at gondor.apana.org.au>

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 shell/ash.c | 122 ++++++++++++++++++++++++++++--------------------------------
 1 file changed, 56 insertions(+), 66 deletions(-)

diff --git a/shell/ash.c b/shell/ash.c
index 2b1378694..ff594050c 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -3810,8 +3810,6 @@ static struct job *jobtab; //5
 static unsigned njobs; //4
 /* current job */
 static struct job *curjob; //lots
-/* number of presumed living untracked jobs */
-static int jobless; //4
 
 #if 0
 /* Bash has a feature: it restores termios after a successful wait for
@@ -4331,7 +4329,7 @@ wait_block_or_sig(int *status)
 #endif
 
 static int
-dowait(int block, struct job *job)
+waitone(int block, struct job *job)
 {
 	int pid;
 	int status;
@@ -4432,10 +4430,6 @@ dowait(int block, struct job *job)
 		goto out;
 	}
 	/* The process wasn't found in job list */
-#if JOBS
-	if (!WIFSTOPPED(status))
-		jobless--;
-#endif
  out:
 	INT_ON;
 
@@ -4460,6 +4454,20 @@ dowait(int block, struct job *job)
 	return pid;
 }
 
+static int
+dowait(int block, struct job *jp)
+{
+	int pid = block == DOWAIT_NONBLOCK ? got_sigchld : 1;
+
+	while (jp ? jp->state == JOBRUNNING : pid > 0) {
+		if (!jp)
+			got_sigchld = 0;
+		pid = waitone(block, jp);
+	}
+
+	return pid;
+}
+
 #if JOBS
 static void
 showjob(struct job *jp, int mode)
@@ -4548,8 +4556,7 @@ showjobs(int mode)
 	TRACE(("showjobs(0x%x) called\n", mode));
 
 	/* Handle all finished jobs */
-	while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
-		continue;
+	dowait(DOWAIT_NONBLOCK, NULL);
 
 	for (jp = curjob; jp; jp = jp->prev_job) {
 		if (!(mode & SHOW_CHANGED) || jp->changed) {
@@ -4666,10 +4673,10 @@ waitcmd(int argc UNUSED_PARAM, char **argv)
 #else
 			dowait(DOWAIT_BLOCK_OR_SIG, NULL);
 #endif
-	/* if child sends us a signal *and immediately exits*,
-	 * dowait() returns pid > 0. Check this case,
-	 * not "if (dowait() < 0)"!
-	 */
+			/* if child sends us a signal *and immediately exits*,
+			 * dowait() returns pid > 0. Check this case,
+			 * not "if (dowait() < 0)"!
+			 */
 			if (pending_sig)
 				goto sigout;
 #if BASH_WAIT_N
@@ -4705,11 +4712,9 @@ waitcmd(int argc UNUSED_PARAM, char **argv)
 			job = getjob(*argv, 0);
 		}
 		/* loop until process terminated or stopped */
-		while (job->state == JOBRUNNING) {
-			dowait(DOWAIT_BLOCK_OR_SIG, NULL);
-			if (pending_sig)
-				goto sigout;
-		}
+		dowait(DOWAIT_BLOCK_OR_SIG, NULL);
+		if (pending_sig)
+			goto sigout;
 		job->waited = 1;
 		retval = getstatus(job);
  repeat: ;
@@ -5261,7 +5266,6 @@ forkchild(struct job *jp, union node *n, int mode)
 #endif
 	for (jp = curjob; jp; jp = jp->prev_job)
 		freejob(jp);
-	jobless = 0;
 }
 
 /* Called after fork(), in parent */
@@ -5272,13 +5276,8 @@ static void
 forkparent(struct job *jp, union node *n, int mode, pid_t pid)
 {
 	TRACE(("In parent shell: child = %d\n", pid));
-	if (!jp) {
-		/* jp is NULL when called by openhere() for heredoc support */
-		while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
-			continue;
-		jobless++;
+	if (!jp) /* jp is NULL when called by openhere() for heredoc support */
 		return;
-	}
 #if JOBS
 	if (mode != FORK_NOJOB && jp->jobctl) {
 		int pgrp;
@@ -5357,48 +5356,39 @@ waitforjob(struct job *jp)
 
 	TRACE(("waitforjob(%%%d) called\n", jp ? jobno(jp) : 0));
 
-	if (!jp) {
-		int pid = got_sigchld;
-
-		while (pid > 0)
-			pid = dowait(DOWAIT_NONBLOCK, NULL);
-
+	/* In non-interactive shells, we _can_ get
+	 * a keyboard signal here and be EINTRed, but we just loop
+	 * inside dowait(), waiting for command to complete.
+	 *
+	 * man bash:
+	 * "If bash is waiting for a command to complete and receives
+	 * a signal for which a trap has been set, the trap
+	 * will not be executed until the command completes."
+	 *
+	 * Reality is that even if trap is not set, bash
+	 * will not act on the signal until command completes.
+	 * Try this. sleep5intoff.c:
+	 * #include <signal.h>
+	 * #include <unistd.h>
+	 * int main() {
+	 *         sigset_t set;
+	 *         sigemptyset(&set);
+	 *         sigaddset(&set, SIGINT);
+	 *         sigaddset(&set, SIGQUIT);
+	 *         sigprocmask(SIG_BLOCK, &set, NULL);
+	 *         sleep(5);
+	 *         return 0;
+	 * }
+	 * $ bash -c './sleep5intoff; echo hi'
+	 * ^C^C^C^C <--- pressing ^C once a second
+	 * $ _
+	 * $ bash -c './sleep5intoff; echo hi'
+	 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
+	 * $ _
+	 */
+	dowait(jp ? DOWAIT_BLOCK : DOWAIT_NONBLOCK, jp);
+	if (!jp)
 		return exitstatus;
-	}
-
-	while (jp->state == JOBRUNNING) {
-		/* In non-interactive shells, we _can_ get
-		 * a keyboard signal here and be EINTRed,
-		 * but we just loop back, waiting for command to complete.
-		 *
-		 * man bash:
-		 * "If bash is waiting for a command to complete and receives
-		 * a signal for which a trap has been set, the trap
-		 * will not be executed until the command completes."
-		 *
-		 * Reality is that even if trap is not set, bash
-		 * will not act on the signal until command completes.
-		 * Try this. sleep5intoff.c:
-		 * #include <signal.h>
-		 * #include <unistd.h>
-		 * int main() {
-		 *         sigset_t set;
-		 *         sigemptyset(&set);
-		 *         sigaddset(&set, SIGINT);
-		 *         sigaddset(&set, SIGQUIT);
-		 *         sigprocmask(SIG_BLOCK, &set, NULL);
-		 *         sleep(5);
-		 *         return 0;
-		 * }
-		 * $ bash -c './sleep5intoff; echo hi'
-		 * ^C^C^C^C <--- pressing ^C once a second
-		 * $ _
-		 * $ bash -c './sleep5intoff; echo hi'
-		 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
-		 * $ _
-		 */
-		dowait(DOWAIT_BLOCK, jp);
-	}
 
 	st = getstatus(jp);
 #if JOBS


More information about the busybox-cvs mailing list