aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2020-03-07 04:48:55 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2020-03-07 04:48:55 +0000
commit1a9db9fd1b619bda0a2da17b8fdc3139cccaa8da (patch)
treeee9dccb756df9e9e9af70abcb850c8d30ff4f47f
parent2b68127d6634c9b6e7da53bb6b7bec8ce4e3ca4a (diff)
parent0fd734073ab98c5d249a3c0de6a357a62654515f (diff)
downloadrepohooks-1a9db9fd1b619bda0a2da17b8fdc3139cccaa8da.tar.gz
Merge "utils: _Popen: workaround Python 3.4.1+ subprocess locking bug"
-rw-r--r--rh/utils.py37
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