ash: jobs: Fix waitcmd busy loop
Upstream commit: Date: Tue, 2 Jun 2020 23:46:48 +1000 jobs: Fix waitcmd busy loop We need to clear gotsigchld in waitproc because it is used as a loop conditional for the waitcmd case. Without it waitcmd may busy loop after a SIGCHLD. This patch also changes gotsigchld into a volatile sig_atomic_t to prevent compilers from optimising its accesses away. Fixes: 6c691b3e5099 ("jobs: Only clear gotsigchld when waiting...") Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> This change also incorporates other changes to bring us closer to upstream. function old new delta dowait 553 636 +83 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
8d5f465a20
commit
91e11eba6e
101
shell/ash.c
101
shell/ash.c
@ -4273,50 +4273,6 @@ sprint_status48(char *os, int status, int sigonly)
|
|||||||
return s - os;
|
return s - os;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
wait_block_or_sig(int *status)
|
|
||||||
{
|
|
||||||
int pid;
|
|
||||||
|
|
||||||
do {
|
|
||||||
sigset_t mask;
|
|
||||||
|
|
||||||
/* Poll all children for changes in their state */
|
|
||||||
got_sigchld = 0;
|
|
||||||
/* if job control is active, accept stopped processes too */
|
|
||||||
pid = waitpid(-1, status, doing_jobctl ? (WNOHANG|WUNTRACED) : WNOHANG);
|
|
||||||
if (pid != 0)
|
|
||||||
break; /* Error (e.g. EINTR, ECHILD) or pid */
|
|
||||||
|
|
||||||
/* Children exist, but none are ready. Sleep until interesting signal */
|
|
||||||
#if 1
|
|
||||||
sigfillset(&mask);
|
|
||||||
sigprocmask2(SIG_SETMASK, &mask); /* mask is updated */
|
|
||||||
while (!got_sigchld && !pending_sig) {
|
|
||||||
sigsuspend(&mask);
|
|
||||||
/* ^^^ add "sigdelset(&mask, SIGCHLD);" before sigsuspend
|
|
||||||
* to make sure SIGCHLD is not masked off?
|
|
||||||
* It was reported that this:
|
|
||||||
* fn() { : | return; }
|
|
||||||
* shopt -s lastpipe
|
|
||||||
* fn
|
|
||||||
* exec ash SCRIPT
|
|
||||||
* under bash 4.4.23 runs SCRIPT with SIGCHLD masked,
|
|
||||||
* making "wait" commands in SCRIPT block forever.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
sigprocmask(SIG_SETMASK, &mask, NULL);
|
|
||||||
#else /* unsafe: a signal can set pending_sig after check, but before pause() */
|
|
||||||
while (!got_sigchld && !pending_sig)
|
|
||||||
pause();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* If it was SIGCHLD, poll children again */
|
|
||||||
} while (got_sigchld);
|
|
||||||
|
|
||||||
return pid;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define DOWAIT_NONBLOCK 0
|
#define DOWAIT_NONBLOCK 0
|
||||||
#define DOWAIT_BLOCK 1
|
#define DOWAIT_BLOCK 1
|
||||||
#define DOWAIT_BLOCK_OR_SIG 2
|
#define DOWAIT_BLOCK_OR_SIG 2
|
||||||
@ -4324,13 +4280,48 @@ wait_block_or_sig(int *status)
|
|||||||
# define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */
|
# define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static int
|
||||||
|
waitproc(int block, int *status)
|
||||||
|
{
|
||||||
|
sigset_t oldmask;
|
||||||
|
int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
#if JOBS
|
||||||
|
if (doing_jobctl)
|
||||||
|
flags |= WUNTRACED;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
do {
|
||||||
|
got_sigchld = 0;
|
||||||
|
do
|
||||||
|
err = waitpid(-1, status, flags);
|
||||||
|
while (err < 0 && errno == EINTR);
|
||||||
|
|
||||||
|
if (err || (err = -!block))
|
||||||
|
break;
|
||||||
|
|
||||||
|
sigfillset(&oldmask);
|
||||||
|
sigprocmask2(SIG_SETMASK, &oldmask); /* mask is updated */
|
||||||
|
while (!got_sigchld && !pending_sig)
|
||||||
|
sigsuspend(&oldmask);
|
||||||
|
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||||
|
//simpler, but unsafe: a signal can set pending_sig after check, but before pause():
|
||||||
|
//while (!got_sigchld && !pending_sig)
|
||||||
|
// pause();
|
||||||
|
|
||||||
|
} while (got_sigchld);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
waitone(int block, struct job *job)
|
waitone(int block, struct job *job)
|
||||||
{
|
{
|
||||||
int pid;
|
int pid;
|
||||||
int status;
|
int status;
|
||||||
struct job *jp;
|
struct job *jp;
|
||||||
struct job *thisjob;
|
struct job *thisjob = NULL;
|
||||||
#if BASH_WAIT_N
|
#if BASH_WAIT_N
|
||||||
bool want_jobexitstatus = (block & DOWAIT_JOBSTATUS);
|
bool want_jobexitstatus = (block & DOWAIT_JOBSTATUS);
|
||||||
block = (block & ~DOWAIT_JOBSTATUS);
|
block = (block & ~DOWAIT_JOBSTATUS);
|
||||||
@ -4357,21 +4348,8 @@ waitone(int block, struct job *job)
|
|||||||
* SIG_DFL handler does not wake sigsuspend().
|
* SIG_DFL handler does not wake sigsuspend().
|
||||||
*/
|
*/
|
||||||
INT_OFF;
|
INT_OFF;
|
||||||
if (block == DOWAIT_BLOCK_OR_SIG) {
|
pid = waitproc(block, &status);
|
||||||
pid = wait_block_or_sig(&status);
|
TRACE(("wait returns pid %d, status=%d\n", pid, status));
|
||||||
} else {
|
|
||||||
int wait_flags = 0;
|
|
||||||
if (block == DOWAIT_NONBLOCK)
|
|
||||||
wait_flags = WNOHANG;
|
|
||||||
/* if job control is active, accept stopped processes too */
|
|
||||||
if (doing_jobctl)
|
|
||||||
wait_flags |= WUNTRACED;
|
|
||||||
/* NB: _not_ safe_waitpid, we need to detect EINTR */
|
|
||||||
pid = waitpid(-1, &status, wait_flags);
|
|
||||||
}
|
|
||||||
TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
|
|
||||||
pid, status, errno, strerror(errno)));
|
|
||||||
thisjob = NULL;
|
|
||||||
if (pid <= 0)
|
if (pid <= 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
@ -4466,7 +4444,6 @@ dowait(int block, struct job *jp)
|
|||||||
rpid = 1;
|
rpid = 1;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
got_sigchld = 0;
|
|
||||||
pid = waitone(block, jp);
|
pid = waitone(block, jp);
|
||||||
rpid &= !!pid;
|
rpid &= !!pid;
|
||||||
|
|
||||||
@ -4721,7 +4698,7 @@ waitcmd(int argc UNUSED_PARAM, char **argv)
|
|||||||
job = getjob(*argv, 0);
|
job = getjob(*argv, 0);
|
||||||
}
|
}
|
||||||
/* loop until process terminated or stopped */
|
/* loop until process terminated or stopped */
|
||||||
dowait(DOWAIT_BLOCK_OR_SIG, NULL);
|
dowait(DOWAIT_BLOCK_OR_SIG, job);
|
||||||
if (pending_sig)
|
if (pending_sig)
|
||||||
goto sigout;
|
goto sigout;
|
||||||
job->waited = 1;
|
job->waited = 1;
|
||||||
|
Loading…
Reference in New Issue
Block a user