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