Commit 3e5fdfe5 authored by Gregory P. Smith's avatar Gregory P. Smith

Fixes issue1336 - a race condition could occur when forking if the gc

kicked in during the critical section.  solution: disable gc during
that section.  Patch contributed by jpa and updated by me to cover the
race condition still existing what therve from twistedmatrix pointed
out (already seen and fixed in twisted's own subprocess code).
parent f36c37c0
...@@ -356,6 +356,7 @@ mswindows = (sys.platform == "win32") ...@@ -356,6 +356,7 @@ mswindows = (sys.platform == "win32")
import os import os
import types import types
import traceback import traceback
import gc
# Exception classes used by this module. # Exception classes used by this module.
class CalledProcessError(Exception): class CalledProcessError(Exception):
...@@ -994,66 +995,78 @@ class Popen(object): ...@@ -994,66 +995,78 @@ class Popen(object):
errpipe_read, errpipe_write = os.pipe() errpipe_read, errpipe_write = os.pipe()
self._set_cloexec_flag(errpipe_write) self._set_cloexec_flag(errpipe_write)
self.pid = os.fork() gc_was_enabled = gc.isenabled()
self._child_created = True # Disable gc to avoid bug where gc -> file_dealloc ->
if self.pid == 0: # write to stderr -> hang. http://bugs.python.org/issue1336
# Child gc.disable()
try: try:
# Close parent's pipe ends self.pid = os.fork()
if p2cwrite is not None: except:
os.close(p2cwrite) if gc_was_enabled:
if c2pread is not None: gc.enable()
os.close(c2pread) raise
if errread is not None: else:
os.close(errread) self._child_created = True
os.close(errpipe_read) if self.pid == 0:
# Child
# Dup fds for child try:
if p2cread is not None: # Close parent's pipe ends
os.dup2(p2cread, 0) if p2cwrite is not None:
if c2pwrite is not None: os.close(p2cwrite)
os.dup2(c2pwrite, 1) if c2pread is not None:
if errwrite is not None: os.close(c2pread)
os.dup2(errwrite, 2) if errread is not None:
os.close(errread)
# Close pipe fds. Make sure we don't close the same os.close(errpipe_read)
# fd more than once, or standard fds.
if p2cread is not None and p2cread not in (0,): # Dup fds for child
os.close(p2cread) if p2cread is not None:
if c2pwrite is not None and c2pwrite not in (p2cread, 1): os.dup2(p2cread, 0)
os.close(c2pwrite) if c2pwrite is not None:
if errwrite is not None and errwrite not in (p2cread, c2pwrite, 2): os.dup2(c2pwrite, 1)
os.close(errwrite) if errwrite is not None:
os.dup2(errwrite, 2)
# Close all other fds, if asked for
if close_fds: # Close pipe fds. Make sure we don't close the same
self._close_fds(but=errpipe_write) # fd more than once, or standard fds.
if p2cread is not None and p2cread not in (0,):
if cwd is not None: os.close(p2cread)
os.chdir(cwd) if c2pwrite is not None and c2pwrite not in (p2cread, 1):
os.close(c2pwrite)
if preexec_fn: if errwrite is not None and errwrite not in (p2cread, c2pwrite, 2):
apply(preexec_fn) os.close(errwrite)
if env is None: # Close all other fds, if asked for
os.execvp(executable, args) if close_fds:
else: self._close_fds(but=errpipe_write)
os.execvpe(executable, args, env)
if cwd is not None:
except: os.chdir(cwd)
exc_type, exc_value, tb = sys.exc_info()
# Save the traceback and attach it to the exception object if preexec_fn:
exc_lines = traceback.format_exception(exc_type, apply(preexec_fn)
exc_value,
tb) if env is None:
exc_value.child_traceback = ''.join(exc_lines) os.execvp(executable, args)
os.write(errpipe_write, pickle.dumps(exc_value)) else:
os.execvpe(executable, args, env)
# This exitcode won't be reported to applications, so it
# really doesn't matter what we return. except:
os._exit(255) exc_type, exc_value, tb = sys.exc_info()
# Save the traceback and attach it to the exception object
exc_lines = traceback.format_exception(exc_type,
exc_value,
tb)
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)
# Parent # Parent
if gc_was_enabled:
gc.enable()
os.close(errpipe_write) os.close(errpipe_write)
if p2cread is not None and p2cwrite is not None: if p2cread is not None and p2cwrite is not None:
os.close(p2cread) os.close(p2cread)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment