Home | History | Annotate | Download | only in multiprocessing
      1 import os
      2 import msvcrt
      3 import signal
      4 import sys
      5 import _winapi
      6 
      7 from .context import reduction, get_spawning_popen, set_spawning_popen
      8 from . import spawn
      9 from . import util
     10 
     11 __all__ = ['Popen']
     12 
     13 #
     14 #
     15 #
     16 
     17 TERMINATE = 0x10000
     18 WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False))
     19 WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
     20 
     21 
     22 def _path_eq(p1, p2):
     23     return p1 == p2 or os.path.normcase(p1) == os.path.normcase(p2)
     24 
     25 WINENV = (hasattr(sys, '_base_executable') and
     26           not _path_eq(sys.executable, sys._base_executable))
     27 
     28 
     29 def _close_handles(*handles):
     30     for handle in handles:
     31         _winapi.CloseHandle(handle)
     32 
     33 
     34 #
     35 # We define a Popen class similar to the one from subprocess, but
     36 # whose constructor takes a process object as its argument.
     37 #
     38 
     39 class Popen(object):
     40     '''
     41     Start a subprocess to run the code of a process object
     42     '''
     43     method = 'spawn'
     44 
     45     def __init__(self, process_obj):
     46         prep_data = spawn.get_preparation_data(process_obj._name)
     47 
     48         # read end of pipe will be "stolen" by the child process
     49         # -- see spawn_main() in spawn.py.
     50         rhandle, whandle = _winapi.CreatePipe(None, 0)
     51         wfd = msvcrt.open_osfhandle(whandle, 0)
     52         cmd = spawn.get_command_line(parent_pid=os.getpid(),
     53                                      pipe_handle=rhandle)
     54         cmd = ' '.join('"%s"' % x for x in cmd)
     55 
     56         python_exe = spawn.get_executable()
     57 
     58         # bpo-35797: When running in a venv, we bypass the redirect
     59         # executor and launch our base Python.
     60         if WINENV and _path_eq(python_exe, sys.executable):
     61             python_exe = sys._base_executable
     62             env = os.environ.copy()
     63             env["__PYVENV_LAUNCHER__"] = sys.executable
     64         else:
     65             env = None
     66 
     67         with open(wfd, 'wb', closefd=True) as to_child:
     68             # start process
     69             try:
     70                 hp, ht, pid, tid = _winapi.CreateProcess(
     71                     python_exe, cmd,
     72                     env, None, False, 0, None, None, None)
     73                 _winapi.CloseHandle(ht)
     74             except:
     75                 _winapi.CloseHandle(rhandle)
     76                 raise
     77 
     78             # set attributes of self
     79             self.pid = pid
     80             self.returncode = None
     81             self._handle = hp
     82             self.sentinel = int(hp)
     83             self.finalizer = util.Finalize(self, _winapi.CloseHandle, (self.sentinel,))
     84 
     85             # send information to child
     86             set_spawning_popen(self)
     87             try:
     88                 reduction.dump(prep_data, to_child)
     89                 reduction.dump(process_obj, to_child)
     90             finally:
     91                 set_spawning_popen(None)
     92 
     93     def duplicate_for_child(self, handle):
     94         assert self is get_spawning_popen()
     95         return reduction.duplicate(handle, self.sentinel)
     96 
     97     def wait(self, timeout=None):
     98         if self.returncode is None:
     99             if timeout is None:
    100                 msecs = _winapi.INFINITE
    101             else:
    102                 msecs = max(0, int(timeout * 1000 + 0.5))
    103 
    104             res = _winapi.WaitForSingleObject(int(self._handle), msecs)
    105             if res == _winapi.WAIT_OBJECT_0:
    106                 code = _winapi.GetExitCodeProcess(self._handle)
    107                 if code == TERMINATE:
    108                     code = -signal.SIGTERM
    109                 self.returncode = code
    110 
    111         return self.returncode
    112 
    113     def poll(self):
    114         return self.wait(timeout=0)
    115 
    116     def terminate(self):
    117         if self.returncode is None:
    118             try:
    119                 _winapi.TerminateProcess(int(self._handle), TERMINATE)
    120             except OSError:
    121                 if self.wait(timeout=1.0) is None:
    122                     raise
    123 
    124     kill = terminate
    125 
    126     def close(self):
    127         self.finalizer()
    128