diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/jobs.c | 94 |
1 files changed, 32 insertions, 62 deletions
diff --git a/src/jobs.c b/src/jobs.c index 606d603..79a087e 100644 --- a/src/jobs.c +++ b/src/jobs.c @@ -96,8 +96,6 @@ static int ttyfd = -1; /* current job */ static struct job *curjob; -/* number of presumed living untracked jobs */ -static int jobless; STATIC void set_curjob(struct job *, unsigned); STATIC int jobno(const struct job *); @@ -556,8 +554,7 @@ showjobs(struct output *out, int mode) TRACE(("showjobs(%x) called\n", mode)); /* If not even one one job changed, there is nothing to do */ - while (dowait(DOWAIT_NORMAL, NULL) > 0) - continue; + dowait(DOWAIT_NORMAL, NULL); for (jp = curjob; jp; jp = jp->prev_job) { if (!(mode & SHOW_CHANGED) || jp->changed) @@ -614,7 +611,7 @@ waitcmd(int argc, char **argv) jp->waited = 1; jp = jp->prev_job; } - if (dowait(DOWAIT_WAITCMD, 0) <= 0) + if (!dowait(DOWAIT_WAITCMD, 0)) goto sigout; } } @@ -636,9 +633,8 @@ start: } else job = getjob(*argv, 0); /* loop until process terminated or stopped */ - while (job->state == JOBRUNNING) - if (dowait(DOWAIT_WAITCMD, 0) <= 0) - goto sigout; + if (!dowait(DOWAIT_WAITCMD, job)) + goto sigout; job->waited = 1; retval = getstatus(job); repeat: @@ -890,18 +886,14 @@ forkchild(struct job *jp, union node *n, int mode) } for (jp = curjob; jp; jp = jp->prev_job) freejob(jp); - jobless = 0; } STATIC inline void forkparent(struct job *jp, union node *n, int mode, pid_t pid) { TRACE(("In parent shell: child = %d\n", pid)); - if (!jp) { - while (jobless && dowait(DOWAIT_NORMAL, 0) > 0); - jobless++; + if (!jp) return; - } #if JOBS if (mode != FORK_NOJOB && jp->jobctl) { int pgrp; @@ -975,17 +967,10 @@ waitforjob(struct job *jp) int st; TRACE(("waitforjob(%%%d) called\n", jp ? jobno(jp) : 0)); - if (!jp) { - int pid = gotsigchld; - - while (pid > 0) - pid = dowait(DOWAIT_NORMAL, NULL); - + dowait(jp ? DOWAIT_BLOCK : DOWAIT_NORMAL, jp); + if (!jp) return exitstatus; - } - while (jp->state == JOBRUNNING) - dowait(DOWAIT_BLOCK, jp); st = getstatus(jp); #if JOBS if (jp->jobctl) { @@ -1013,8 +998,7 @@ waitforjob(struct job *jp) * Wait for a process to terminate. */ -STATIC int -dowait(int block, struct job *job) +static int waitone(int block, struct job *job) { int pid; int status; @@ -1057,8 +1041,6 @@ dowait(int block, struct job *job) if (thisjob) goto gotjob; } - if (!JOBS || !WIFSTOPPED(status)) - jobless--; goto out; gotjob: @@ -1093,45 +1075,34 @@ out: return pid; } +static int dowait(int block, struct job *jp) +{ + int pid = block == DOWAIT_NORMAL ? gotsigchld : 1; + + while (jp ? jp->state == JOBRUNNING : pid > 0) { + if (!jp) + gotsigchld = 0; + pid = waitone(block, jp); + } + return pid; +} /* - * Do a wait system call. If job control is compiled in, we accept - * stopped processes. If block is zero, we return a value of zero - * rather than blocking. + * Do a wait system call. If block is zero, we return -1 rather than + * blocking. If block is DOWAIT_WAITCMD, we return 0 when a signal + * other than SIGCHLD interrupted the wait. * - * System V doesn't have a non-blocking wait system call. It does - * have a SIGCLD signal that is sent to a process when one of it's - * children dies. The obvious way to use SIGCLD would be to install - * a handler for SIGCLD which simply bumped a counter when a SIGCLD - * was received, and have waitproc bump another counter when it got - * the status of a process. Waitproc would then know that a wait - * system call would not block if the two counters were different. - * This approach doesn't work because if a process has children that - * have not been waited for, System V will send it a SIGCLD when it - * installs a signal handler for SIGCLD. What this means is that when - * a child exits, the shell will be sent SIGCLD signals continuously - * until is runs out of stack space, unless it does a wait call before - * restoring the signal handler. The code below takes advantage of - * this (mis)feature by installing a signal handler for SIGCLD and - * then checking to see whether it was called. If there are any - * children to be waited for, it will be. + * We use sigsuspend in conjunction with a non-blocking wait3 in + * order to ensure that waitcmd exits promptly upon the reception + * of a signal. * - * If neither SYSV nor BSD is defined, we don't implement nonblocking - * waits at all. In this case, the user will not be informed when - * a background process until the next time she runs a real program - * (as opposed to running a builtin command or just typing return), - * and the jobs command may give out of date information. + * For code paths other than waitcmd we either use a blocking wait3 + * or a non-blocking wait3. For the latter case the caller of dowait + * must ensure that it is called over and over again until all dead + * children have been reaped. Otherwise zombies may linger. */ -#ifdef SYSV -STATIC int gotsigchild; - -STATIC int onsigchild() { - gotsigchild = 1; -} -#endif - STATIC int waitproc(int block, int *status) @@ -1146,13 +1117,10 @@ waitproc(int block, int *status) #endif do { - gotsigchld = 0; err = wait3(status, flags, NULL); - if (err || !block) + if (err || (err = -!block)) break; - block = 0; - sigfillset(&mask); sigprocmask(SIG_SETMASK, &mask, &oldmask); @@ -1160,6 +1128,8 @@ waitproc(int block, int *status) sigsuspend(&oldmask); sigclearmask(); + + err = 0; } while (gotsigchld); return err; |