diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2020-03-07 04:48:55 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2020-03-07 04:48:55 +0000 |
commit | 1a9db9fd1b619bda0a2da17b8fdc3139cccaa8da (patch) | |
tree | ee9dccb756df9e9e9af70abcb850c8d30ff4f47f | |
parent | 2b68127d6634c9b6e7da53bb6b7bec8ce4e3ca4a (diff) | |
parent | 0fd734073ab98c5d249a3c0de6a357a62654515f (diff) | |
download | repohooks-1a9db9fd1b619bda0a2da17b8fdc3139cccaa8da.tar.gz |
Merge "utils: _Popen: workaround Python 3.4.1+ subprocess locking bug"
-rw-r--r-- | rh/utils.py | 37 |
1 files changed, 33 insertions, 4 deletions
diff --git a/rh/utils.py b/rh/utils.py index c6c77e0..53c40b7 100644 --- a/rh/utils.py +++ b/rh/utils.py @@ -222,16 +222,16 @@ def _kill_child_process(proc, int_timeout, kill_timeout, cmd, original_handler, # where the Popen instance was created, but no process was generated. if proc.returncode is None and proc.pid is not None: try: - while proc.poll() is None and int_timeout >= 0: + while proc.poll_lock_breaker() is None and int_timeout >= 0: time.sleep(0.1) int_timeout -= 0.1 proc.terminate() - while proc.poll() is None and kill_timeout >= 0: + while proc.poll_lock_breaker() is None and kill_timeout >= 0: time.sleep(0.1) kill_timeout -= 0.1 - if proc.poll() is None: + if proc.poll_lock_breaker() is None: # Still doesn't want to die. Too bad, so sad, time to die. proc.kill() except EnvironmentError as e: @@ -239,7 +239,11 @@ def _kill_child_process(proc, int_timeout, kill_timeout, cmd, original_handler, file=sys.stderr) # Ensure our child process has been reaped. - proc.wait() + kwargs = {} + if sys.version_info.major >= 3: + # ... but don't wait forever. + kwargs['timeout'] = 60 + proc.wait_lock_breaker(**kwargs) if not rh.signals.relay_signal(original_handler, signum, frame): # Mock up our own, matching exit code for signaling. @@ -296,6 +300,31 @@ class _Popen(subprocess.Popen): else: raise + def _lock_breaker(self, func, *args, **kwargs): + """Helper to manage the waitpid lock. + + Workaround https://bugs.python.org/issue25960. + """ + # If the lock doesn't exist, or is not locked, call the func directly. + lock = getattr(self, '_waitpid_lock', None) + if lock is not None and lock.locked(): + try: + lock.release() + return func(*args, **kwargs) + finally: + if not lock.locked(): + lock.acquire() + else: + return func(*args, **kwargs) + + def poll_lock_breaker(self, *args, **kwargs): + """Wrapper around poll() to break locks if needed.""" + return self._lock_breaker(self.poll, *args, **kwargs) + + def wait_lock_breaker(self, *args, **kwargs): + """Wrapper around wait() to break locks if needed.""" + return self._lock_breaker(self.wait, *args, **kwargs) + # We use the keyword arg |input| which trips up pylint checks. # pylint: disable=redefined-builtin,input-builtin |