summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorGregory P. Smith <greg@krypto.org>2017-09-05 18:20:02 (GMT)
committerGitHub <noreply@github.com>2017-09-05 18:20:02 (GMT)
commit5e8e371364ee58dadb9a4e4e51c7e9cf6bedbfae (patch)
treead244d74a8a78950a508a59cf3afd2b7952a549e /Lib
parent9721e51daf59e9a2f56dbf7c2428ea79c0b13be0 (diff)
downloadcpython-5e8e371364ee58dadb9a4e4e51c7e9cf6bedbfae.zip
cpython-5e8e371364ee58dadb9a4e4e51c7e9cf6bedbfae.tar.gz
cpython-5e8e371364ee58dadb9a4e4e51c7e9cf6bedbfae.tar.bz2
bpo-27448: Work around a gc.disable race condition in subprocess. (#1932)
* bpo-27448: Work around a gc.disable race condition in subprocess. This works around a gc.isenabled/gc.disable race condition in the 2.7 subprocess module by using a lock for the critical section. It'll prevent multiple simultaneous subprocess launches from winding up with gc remaining disabled but it can't fix the ultimate problem: gc enable and disable is a global setting and a hack. Users are *strongly encouraged* to use subprocess32 from PyPI instead of the 2.7 standard library subprocess module. Mixing threads with subprocess is a recipie for disaster otherwise even with "fixes" to ameliorate common issues like this. * Add a blurb!
Diffstat (limited to 'Lib')
-rw-r--r--Lib/subprocess.py36
1 files changed, 29 insertions, 7 deletions
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index 4b41f5e..1f2da0f 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -71,6 +71,10 @@ if mswindows:
else:
import select
_has_poll = hasattr(select, 'poll')
+ try:
+ import threading
+ except ImportError:
+ threading = None
import fcntl
import pickle
@@ -878,6 +882,21 @@ class Popen(object):
pass
+ # Used as a bandaid workaround for https://bugs.python.org/issue27448
+ # to prevent multiple simultaneous subprocess launches from interfering
+ # with one another and leaving gc disabled.
+ if threading:
+ _disabling_gc_lock = threading.Lock()
+ else:
+ class _noop_context_manager(object):
+ # A dummy context manager that does nothing for the rare
+ # user of a --without-threads build.
+ def __enter__(self): pass
+ def __exit__(self, *args): pass
+
+ _disabling_gc_lock = _noop_context_manager()
+
+
def _execute_child(self, args, executable, preexec_fn, close_fds,
cwd, env, universal_newlines,
startupinfo, creationflags, shell, to_close,
@@ -909,10 +928,12 @@ class Popen(object):
errpipe_read, errpipe_write = self.pipe_cloexec()
try:
try:
- gc_was_enabled = gc.isenabled()
- # Disable gc to avoid bug where gc -> file_dealloc ->
- # write to stderr -> hang. http://bugs.python.org/issue1336
- gc.disable()
+ with self._disabling_gc_lock:
+ gc_was_enabled = gc.isenabled()
+ # Disable gc to avoid bug where gc -> file_dealloc ->
+ # write to stderr -> hang.
+ # https://bugs.python.org/issue1336
+ gc.disable()
try:
self.pid = os.fork()
except:
@@ -986,9 +1007,10 @@ class Popen(object):
exc_value.child_traceback = ''.join(exc_lines)
os.write(errpipe_write, pickle.dumps(exc_value))
- # This exitcode won't be reported to applications, so it
- # really doesn't matter what we return.
- os._exit(255)
+ finally:
+ # This exitcode won't be reported to applications, so it
+ # really doesn't matter what we return.
+ os._exit(255)
# Parent
if gc_was_enabled: