1import io 2import os 3 4from .context import reduction, set_spawning_popen 5from . import popen_fork 6from . import spawn 7from . import util 8 9__all__ = ['Popen'] 10 11 12# 13# Wrapper for an fd used while launching a process 14# 15 16class _DupFd(object): 17 def __init__(self, fd): 18 self.fd = fd 19 def detach(self): 20 return self.fd 21 22# 23# Start child process using a fresh interpreter 24# 25 26class Popen(popen_fork.Popen): 27 method = 'spawn' 28 DupFd = _DupFd 29 30 def __init__(self, process_obj): 31 self._fds = [] 32 super().__init__(process_obj) 33 34 def duplicate_for_child(self, fd): 35 self._fds.append(fd) 36 return fd 37 38 def _launch(self, process_obj): 39 from . import resource_tracker 40 tracker_fd = resource_tracker.getfd() 41 self._fds.append(tracker_fd) 42 prep_data = spawn.get_preparation_data(process_obj._name) 43 fp = io.BytesIO() 44 set_spawning_popen(self) 45 try: 46 reduction.dump(prep_data, fp) 47 reduction.dump(process_obj, fp) 48 finally: 49 set_spawning_popen(None) 50 51 parent_r = child_w = child_r = parent_w = None 52 try: 53 parent_r, child_w = os.pipe() 54 child_r, parent_w = os.pipe() 55 cmd = spawn.get_command_line(tracker_fd=tracker_fd, 56 pipe_handle=child_r) 57 self._fds.extend([child_r, child_w]) 58 self.pid = util.spawnv_passfds(spawn.get_executable(), 59 cmd, self._fds) 60 self.sentinel = parent_r 61 with open(parent_w, 'wb', closefd=False) as f: 62 f.write(fp.getbuffer()) 63 finally: 64 fds_to_close = [] 65 for fd in (parent_r, parent_w): 66 if fd is not None: 67 fds_to_close.append(fd) 68 self.finalizer = util.Finalize(self, util.close_fds, fds_to_close) 69 70 for fd in (child_r, child_w): 71 if fd is not None: 72 os.close(fd) 73