1 import io
2 import os
3 
4 from .context import reduction, set_spawning_popen
5 from . import popen_fork
6 from . import spawn
7 from . import util
8 
9 __all__ = ['Popen']
10 
11 
12 #
13 # Wrapper for an fd used while launching a process
14 #
15 
16 class _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 
26 class 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