mirror of
https://github.com/python/cpython.git
synced 2025-11-01 14:11:41 +00:00
fix potential hang. It can happen that the child crashes right in the beginning for whatever reason. In this case, the parent will hang when writing into the pipe, because the child fd is not closed yet. The normal pattern is to close the child fds right after the child proc is forked/executed/spawned, so when the child dies, then also the pipes will be closed, and there will be no hang (the parent gets SIGPIPE instead).
76 lines
2.1 KiB
Python
76 lines
2.1 KiB
Python
import io
|
|
import os
|
|
|
|
from .context import reduction, set_spawning_popen
|
|
from . import popen_fork
|
|
from . import spawn
|
|
from . import util
|
|
|
|
__all__ = ['Popen']
|
|
|
|
|
|
#
|
|
# Wrapper for an fd used while launching a process
|
|
#
|
|
|
|
class _DupFd(object):
|
|
def __init__(self, fd):
|
|
self.fd = fd
|
|
def detach(self):
|
|
return self.fd
|
|
|
|
#
|
|
# Start child process using a fresh interpreter
|
|
#
|
|
|
|
class Popen(popen_fork.Popen):
|
|
method = 'spawn'
|
|
DupFd = _DupFd
|
|
|
|
def __init__(self, process_obj):
|
|
self._fds = []
|
|
super().__init__(process_obj)
|
|
|
|
def duplicate_for_child(self, fd):
|
|
self._fds.append(fd)
|
|
return fd
|
|
|
|
def _launch(self, process_obj):
|
|
from . import resource_tracker
|
|
tracker_fd = resource_tracker.getfd()
|
|
self._fds.append(tracker_fd)
|
|
prep_data = spawn.get_preparation_data(process_obj._name)
|
|
fp = io.BytesIO()
|
|
set_spawning_popen(self)
|
|
try:
|
|
reduction.dump(prep_data, fp)
|
|
reduction.dump(process_obj, fp)
|
|
finally:
|
|
set_spawning_popen(None)
|
|
|
|
parent_r = child_w = child_r = parent_w = None
|
|
try:
|
|
parent_r, child_w = os.pipe()
|
|
child_r, parent_w = os.pipe()
|
|
cmd = spawn.get_command_line(tracker_fd=tracker_fd,
|
|
pipe_handle=child_r)
|
|
self._fds.extend([child_r, child_w])
|
|
self.pid = util.spawnv_passfds(spawn.get_executable(),
|
|
cmd, self._fds)
|
|
os.close(child_r)
|
|
child_r = None
|
|
os.close(child_w)
|
|
child_w = None
|
|
self.sentinel = parent_r
|
|
with open(parent_w, 'wb', closefd=False) as f:
|
|
f.write(fp.getbuffer())
|
|
finally:
|
|
fds_to_close = []
|
|
for fd in (parent_r, parent_w):
|
|
if fd is not None:
|
|
fds_to_close.append(fd)
|
|
self.finalizer = util.Finalize(self, util.close_fds, fds_to_close)
|
|
|
|
for fd in (child_r, child_w):
|
|
if fd is not None:
|
|
os.close(fd)
|