diff --git a/subprocess2.py b/subprocess2.py index 21e348785..a00592f47 100644 --- a/subprocess2.py +++ b/subprocess2.py @@ -181,6 +181,14 @@ class Popen(subprocess.Popen): Note: Popen() can throw OSError when cwd or args[0] doesn't exist. Translate exceptions generated by cygwin when it fails trying to emulate fork(). """ + # subprocess.Popen.__init__() is not threadsafe; there is a race between + # creating the exec-error pipe for the child and setting it to CLOEXEC during + # which another thread can fork and cause the pipe to be inherited by its + # descendents, which will cause the current Popen to hang until all those + # descendents exit. Protect this with a lock so that only one fork/exec can + # happen at a time. + popen_lock = threading.Lock() + def __init__(self, args, **kwargs): # Make sure we hack subprocess if necessary. hack_subprocess() @@ -234,7 +242,8 @@ class Popen(subprocess.Popen): self.returncode = None try: - super(Popen, self).__init__(args, **kwargs) + with self.popen_lock: + super(Popen, self).__init__(args, **kwargs) except OSError, e: if e.errno == errno.EAGAIN and sys.platform == 'cygwin': # Convert fork() emulation failure into a CygwinRebaseError().