From: Gregory P. Smith Date: Sun, 11 Nov 2012 05:06:18 +0000 (-0800) Subject: Fixes issue #14396: Handle the odd rare case of waitpid returning 0 X-Git-Tag: v3.3.1rc1~652 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f328d792235e16397c109d7b85a34ab5efce9498;p=python Fixes issue #14396: Handle the odd rare case of waitpid returning 0 when not expected in subprocess.Popen.wait(). --- f328d792235e16397c109d7b85a34ab5efce9498 diff --cc Lib/subprocess.py index 57cc1a4a7d,35a98c8969..296613ac0b --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@@ -1466,52 -1425,24 +1466,56 @@@ class Popen(object) return self.returncode - def wait(self): + def _try_wait(self, wait_flags): + try: + (pid, sts) = _eintr_retry_call(os.waitpid, self.pid, wait_flags) + except OSError as e: + if e.errno != errno.ECHILD: + raise + # This happens if SIGCLD is set to be ignored or waiting + # for child processes has otherwise been disabled for our + # process. This child is dead, we can't get the status. + pid = self.pid + sts = 0 + return (pid, sts) + + + def wait(self, timeout=None, endtime=None): """Wait for child process to terminate. Returns returncode attribute.""" - while self.returncode is None: - try: - pid, sts = _eintr_retry_call(os.waitpid, self.pid, 0) - except OSError as e: - if e.errno != errno.ECHILD: - raise - # This happens if SIGCLD is set to be ignored or waiting - # for child processes has otherwise been disabled for our - # process. This child is dead, we can't get the status. - pid = self.pid - sts = 0 - # Check the pid and loop as waitpid has been known to return - # 0 even without WNOHANG in odd situations. issue14396. - if pid == self.pid: - self._handle_exitstatus(sts) + if self.returncode is not None: + return self.returncode + + # endtime is preferred to timeout. timeout is only used for + # printing. + if endtime is not None or timeout is not None: + if endtime is None: + endtime = _time() + timeout + elif timeout is None: + timeout = self._remaining_time(endtime) + + if endtime is not None: + # Enter a busy loop if we have a timeout. This busy loop was + # cribbed from Lib/threading.py in Thread.wait() at r71065. + delay = 0.0005 # 500 us -> initial delay of 1 ms + while True: + (pid, sts) = self._try_wait(os.WNOHANG) + assert pid == self.pid or pid == 0 + if pid == self.pid: + self._handle_exitstatus(sts) + break + remaining = self._remaining_time(endtime) + if remaining <= 0: + raise TimeoutExpired(self.args, timeout) + delay = min(delay * 2, remaining, .05) + time.sleep(delay) - elif self.returncode is None: - (pid, sts) = self._try_wait(0) - self._handle_exitstatus(sts) ++ else: ++ while self.returncode is None: ++ (pid, sts) = self._try_wait(0) ++ # Check the pid and loop as waitpid has been known to return ++ # 0 even without WNOHANG in odd situations. issue14396. ++ if pid == self.pid: ++ self._handle_exitstatus(sts) return self.returncode diff --cc Misc/NEWS index 44e0da20cf,a4f765a689..dea9c8ab67 --- a/Misc/NEWS +++ b/Misc/NEWS @@@ -83,6 -165,6 +83,9 @@@ Core and Builtin Library ------- ++- Issue #14396: Handle the odd rare case of waitpid returning 0 when not ++ expected in subprocess.Popen.wait(). ++ - Issue #16411: Fix a bug where zlib.decompressobj().flush() might try to access previously-freed memory. Patch by Serhiy Storchaka.