1import os 2import signal 3 4from . import util 5 6__all__ = ['Popen'] 7 8# 9# Start child process using fork 10# 11 12class Popen(object): 13 method = 'fork' 14 15 def __init__(self, process_obj): 16 util._flush_std_streams() 17 self.returncode = None 18 self.finalizer = None 19 self._launch(process_obj) 20 21 def duplicate_for_child(self, fd): 22 return fd 23 24 def poll(self, flag=os.WNOHANG): 25 if self.returncode is None: 26 try: 27 pid, sts = os.waitpid(self.pid, flag) 28 except OSError as e: 29 # Child process not yet created. See #1731717 30 # e.errno == errno.ECHILD == 10 31 return None 32 if pid == self.pid: 33 if os.WIFSIGNALED(sts): 34 self.returncode = -os.WTERMSIG(sts) 35 else: 36 assert os.WIFEXITED(sts), "Status is {:n}".format(sts) 37 self.returncode = os.WEXITSTATUS(sts) 38 return self.returncode 39 40 def wait(self, timeout=None): 41 if self.returncode is None: 42 if timeout is not None: 43 from multiprocessing.connection import wait 44 if not wait([self.sentinel], timeout): 45 return None 46 # This shouldn't block if wait() returned successfully. 47 return self.poll(os.WNOHANG if timeout == 0.0 else 0) 48 return self.returncode 49 50 def _send_signal(self, sig): 51 if self.returncode is None: 52 try: 53 os.kill(self.pid, sig) 54 except ProcessLookupError: 55 pass 56 except OSError: 57 if self.wait(timeout=0.1) is None: 58 raise 59 60 def terminate(self): 61 self._send_signal(signal.SIGTERM) 62 63 def kill(self): 64 self._send_signal(signal.SIGKILL) 65 66 def _launch(self, process_obj): 67 code = 1 68 parent_r, child_w = os.pipe() 69 child_r, parent_w = os.pipe() 70 self.pid = os.fork() 71 if self.pid == 0: 72 try: 73 os.close(parent_r) 74 os.close(parent_w) 75 code = process_obj._bootstrap(parent_sentinel=child_r) 76 finally: 77 os._exit(code) 78 else: 79 os.close(child_w) 80 os.close(child_r) 81 self.finalizer = util.Finalize(self, util.close_fds, 82 (parent_r, parent_w,)) 83 self.sentinel = parent_r 84 85 def close(self): 86 if self.finalizer is not None: 87 self.finalizer() 88