17db96d56Sopenharmony_ciimport io
27db96d56Sopenharmony_ciimport os
37db96d56Sopenharmony_ci
47db96d56Sopenharmony_cifrom .context import reduction, set_spawning_popen
57db96d56Sopenharmony_ciif not reduction.HAVE_SEND_HANDLE:
67db96d56Sopenharmony_ci    raise ImportError('No support for sending fds between processes')
77db96d56Sopenharmony_cifrom . import forkserver
87db96d56Sopenharmony_cifrom . import popen_fork
97db96d56Sopenharmony_cifrom . import spawn
107db96d56Sopenharmony_cifrom . import util
117db96d56Sopenharmony_ci
127db96d56Sopenharmony_ci
137db96d56Sopenharmony_ci__all__ = ['Popen']
147db96d56Sopenharmony_ci
157db96d56Sopenharmony_ci#
167db96d56Sopenharmony_ci# Wrapper for an fd used while launching a process
177db96d56Sopenharmony_ci#
187db96d56Sopenharmony_ci
197db96d56Sopenharmony_ciclass _DupFd(object):
207db96d56Sopenharmony_ci    def __init__(self, ind):
217db96d56Sopenharmony_ci        self.ind = ind
227db96d56Sopenharmony_ci    def detach(self):
237db96d56Sopenharmony_ci        return forkserver.get_inherited_fds()[self.ind]
247db96d56Sopenharmony_ci
257db96d56Sopenharmony_ci#
267db96d56Sopenharmony_ci# Start child process using a server process
277db96d56Sopenharmony_ci#
287db96d56Sopenharmony_ci
297db96d56Sopenharmony_ciclass Popen(popen_fork.Popen):
307db96d56Sopenharmony_ci    method = 'forkserver'
317db96d56Sopenharmony_ci    DupFd = _DupFd
327db96d56Sopenharmony_ci
337db96d56Sopenharmony_ci    def __init__(self, process_obj):
347db96d56Sopenharmony_ci        self._fds = []
357db96d56Sopenharmony_ci        super().__init__(process_obj)
367db96d56Sopenharmony_ci
377db96d56Sopenharmony_ci    def duplicate_for_child(self, fd):
387db96d56Sopenharmony_ci        self._fds.append(fd)
397db96d56Sopenharmony_ci        return len(self._fds) - 1
407db96d56Sopenharmony_ci
417db96d56Sopenharmony_ci    def _launch(self, process_obj):
427db96d56Sopenharmony_ci        prep_data = spawn.get_preparation_data(process_obj._name)
437db96d56Sopenharmony_ci        buf = io.BytesIO()
447db96d56Sopenharmony_ci        set_spawning_popen(self)
457db96d56Sopenharmony_ci        try:
467db96d56Sopenharmony_ci            reduction.dump(prep_data, buf)
477db96d56Sopenharmony_ci            reduction.dump(process_obj, buf)
487db96d56Sopenharmony_ci        finally:
497db96d56Sopenharmony_ci            set_spawning_popen(None)
507db96d56Sopenharmony_ci
517db96d56Sopenharmony_ci        self.sentinel, w = forkserver.connect_to_new_process(self._fds)
527db96d56Sopenharmony_ci        # Keep a duplicate of the data pipe's write end as a sentinel of the
537db96d56Sopenharmony_ci        # parent process used by the child process.
547db96d56Sopenharmony_ci        _parent_w = os.dup(w)
557db96d56Sopenharmony_ci        self.finalizer = util.Finalize(self, util.close_fds,
567db96d56Sopenharmony_ci                                       (_parent_w, self.sentinel))
577db96d56Sopenharmony_ci        with open(w, 'wb', closefd=True) as f:
587db96d56Sopenharmony_ci            f.write(buf.getbuffer())
597db96d56Sopenharmony_ci        self.pid = forkserver.read_signed(self.sentinel)
607db96d56Sopenharmony_ci
617db96d56Sopenharmony_ci    def poll(self, flag=os.WNOHANG):
627db96d56Sopenharmony_ci        if self.returncode is None:
637db96d56Sopenharmony_ci            from multiprocessing.connection import wait
647db96d56Sopenharmony_ci            timeout = 0 if flag == os.WNOHANG else None
657db96d56Sopenharmony_ci            if not wait([self.sentinel], timeout):
667db96d56Sopenharmony_ci                return None
677db96d56Sopenharmony_ci            try:
687db96d56Sopenharmony_ci                self.returncode = forkserver.read_signed(self.sentinel)
697db96d56Sopenharmony_ci            except (OSError, EOFError):
707db96d56Sopenharmony_ci                # This should not happen usually, but perhaps the forkserver
717db96d56Sopenharmony_ci                # process itself got killed
727db96d56Sopenharmony_ci                self.returncode = 255
737db96d56Sopenharmony_ci
747db96d56Sopenharmony_ci        return self.returncode
75