blob: b0fc01396eb24cfe285fc39dce162fb93020d55a [file] [log] [blame]
Richard Oudkerk84ed9a62013-08-14 15:35:41 +01001import os
2import sys
3import signal
Richard Oudkerk84ed9a62013-08-14 15:35:41 +01004
5from . import util
6
7__all__ = ['Popen']
8
9#
10# Start child process using fork
11#
12
13class Popen(object):
14 method = 'fork'
15
16 def __init__(self, process_obj):
Antoine Pitroudaeefd22017-10-22 11:40:31 +020017 try:
18 sys.stdout.flush()
19 except (AttributeError, ValueError):
20 pass
21 try:
22 sys.stderr.flush()
23 except (AttributeError, ValueError):
24 pass
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010025 self.returncode = None
Antoine Pitrou13e96cc2017-06-24 19:22:23 +020026 self.finalizer = None
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010027 self._launch(process_obj)
28
29 def duplicate_for_child(self, fd):
30 return fd
31
32 def poll(self, flag=os.WNOHANG):
33 if self.returncode is None:
Antoine Pitroudfd5f342017-06-12 15:28:19 +020034 try:
35 pid, sts = os.waitpid(self.pid, flag)
36 except OSError as e:
37 # Child process not yet created. See #1731717
38 # e.errno == errno.ECHILD == 10
39 return None
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010040 if pid == self.pid:
41 if os.WIFSIGNALED(sts):
42 self.returncode = -os.WTERMSIG(sts)
43 else:
Allen W. Smith, Ph.Dbd73e722017-08-29 17:52:18 -050044 assert os.WIFEXITED(sts), "Status is {:n}".format(sts)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010045 self.returncode = os.WEXITSTATUS(sts)
46 return self.returncode
47
48 def wait(self, timeout=None):
49 if self.returncode is None:
50 if timeout is not None:
Richard Oudkerkc3460602014-03-23 12:52:16 +000051 from multiprocessing.connection import wait
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010052 if not wait([self.sentinel], timeout):
53 return None
54 # This shouldn't block if wait() returned successfully.
55 return self.poll(os.WNOHANG if timeout == 0.0 else 0)
56 return self.returncode
57
Vitor Pereiraba75af72017-07-18 16:34:23 +010058 def _send_signal(self, sig):
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010059 if self.returncode is None:
60 try:
Vitor Pereiraba75af72017-07-18 16:34:23 +010061 os.kill(self.pid, sig)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010062 except ProcessLookupError:
63 pass
64 except OSError:
65 if self.wait(timeout=0.1) is None:
66 raise
67
Vitor Pereiraba75af72017-07-18 16:34:23 +010068 def terminate(self):
69 self._send_signal(signal.SIGTERM)
70
71 def kill(self):
72 self._send_signal(signal.SIGKILL)
73
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010074 def _launch(self, process_obj):
75 code = 1
Victor Stinnerdaf45552013-08-28 00:53:59 +020076 parent_r, child_w = os.pipe()
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010077 self.pid = os.fork()
78 if self.pid == 0:
79 try:
80 os.close(parent_r)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010081 code = process_obj._bootstrap()
82 finally:
83 os._exit(code)
84 else:
85 os.close(child_w)
Antoine Pitrou13e96cc2017-06-24 19:22:23 +020086 self.finalizer = util.Finalize(self, os.close, (parent_r,))
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010087 self.sentinel = parent_r
Antoine Pitrou13e96cc2017-06-24 19:22:23 +020088
89 def close(self):
90 if self.finalizer is not None:
91 self.finalizer()