blob: bca8b7a0047070f054f3bd6ecfb256e709e20e5c [file] [log] [blame]
Benjamin Petersone711caf2008-06-11 16:44:04 +00001#
2# Module providing the `Process` class which emulates `threading.Thread`
3#
4# multiprocessing/process.py
5#
R. David Murray3fc969a2010-12-14 01:38:16 +00006# Copyright (c) 2006-2008, R Oudkerk
Richard Oudkerk3e268aa2012-04-30 12:13:55 +01007# Licensed to PSF under a Contributor Agreement.
Benjamin Petersone711caf2008-06-11 16:44:04 +00008#
9
Richard Oudkerkb1694cf2013-10-16 16:41:56 +010010__all__ = ['BaseProcess', 'current_process', 'active_children']
Benjamin Petersone711caf2008-06-11 16:44:04 +000011
12#
13# Imports
14#
15
16import os
17import sys
18import signal
19import itertools
Antoine Pitrouc081c0c2011-07-15 22:12:24 +020020from _weakrefset import WeakSet
Benjamin Petersone711caf2008-06-11 16:44:04 +000021
22#
23#
24#
25
26try:
27 ORIGINAL_DIR = os.path.abspath(os.getcwd())
28except OSError:
29 ORIGINAL_DIR = None
30
Benjamin Petersone711caf2008-06-11 16:44:04 +000031#
32# Public functions
33#
34
35def current_process():
36 '''
37 Return process object representing the current process
38 '''
39 return _current_process
40
41def active_children():
42 '''
43 Return list of process objects corresponding to live child processes
44 '''
45 _cleanup()
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010046 return list(_children)
Benjamin Petersone711caf2008-06-11 16:44:04 +000047
48#
49#
50#
51
52def _cleanup():
53 # check for processes which have finished
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010054 for p in list(_children):
Benjamin Petersone711caf2008-06-11 16:44:04 +000055 if p._popen.poll() is not None:
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010056 _children.discard(p)
Benjamin Petersone711caf2008-06-11 16:44:04 +000057
58#
59# The `Process` class
60#
61
Richard Oudkerkb1694cf2013-10-16 16:41:56 +010062class BaseProcess(object):
Benjamin Petersone711caf2008-06-11 16:44:04 +000063 '''
64 Process objects represent activity that is run in a separate process
65
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010066 The class is analogous to `threading.Thread`
Benjamin Petersone711caf2008-06-11 16:44:04 +000067 '''
Richard Oudkerkb1694cf2013-10-16 16:41:56 +010068 def _Popen(self):
69 raise NotImplementedError
Benjamin Petersone711caf2008-06-11 16:44:04 +000070
Antoine Pitrou0bd4deb2011-02-25 22:07:43 +000071 def __init__(self, group=None, target=None, name=None, args=(), kwargs={},
72 *, daemon=None):
Benjamin Petersone711caf2008-06-11 16:44:04 +000073 assert group is None, 'group argument must be None for now'
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010074 count = next(_process_counter)
Benjamin Petersone711caf2008-06-11 16:44:04 +000075 self._identity = _current_process._identity + (count,)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010076 self._config = _current_process._config.copy()
Benjamin Petersone711caf2008-06-11 16:44:04 +000077 self._parent_pid = os.getpid()
78 self._popen = None
79 self._target = target
80 self._args = tuple(args)
81 self._kwargs = dict(kwargs)
82 self._name = name or type(self).__name__ + '-' + \
83 ':'.join(str(i) for i in self._identity)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010084 if daemon is not None:
85 self.daemon = daemon
Antoine Pitrouc081c0c2011-07-15 22:12:24 +020086 _dangling.add(self)
Benjamin Petersone711caf2008-06-11 16:44:04 +000087
88 def run(self):
89 '''
90 Method to be run in sub-process; can be overridden in sub-class
91 '''
92 if self._target:
93 self._target(*self._args, **self._kwargs)
94
95 def start(self):
96 '''
97 Start child process
98 '''
99 assert self._popen is None, 'cannot start a process twice'
100 assert self._parent_pid == os.getpid(), \
101 'can only start a process object created by current process'
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100102 assert not _current_process._config.get('daemon'), \
Benjamin Petersone711caf2008-06-11 16:44:04 +0000103 'daemonic processes are not allowed to have children'
104 _cleanup()
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100105 self._popen = self._Popen(self)
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200106 self._sentinel = self._popen.sentinel
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100107 _children.add(self)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000108
109 def terminate(self):
110 '''
111 Terminate process; sends SIGTERM signal or uses TerminateProcess()
112 '''
113 self._popen.terminate()
114
115 def join(self, timeout=None):
116 '''
117 Wait until child process terminates
118 '''
119 assert self._parent_pid == os.getpid(), 'can only join a child process'
120 assert self._popen is not None, 'can only join a started process'
121 res = self._popen.wait(timeout)
122 if res is not None:
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100123 _children.discard(self)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000124
125 def is_alive(self):
126 '''
127 Return whether process is alive
128 '''
129 if self is _current_process:
130 return True
131 assert self._parent_pid == os.getpid(), 'can only test a child process'
132 if self._popen is None:
133 return False
134 self._popen.poll()
135 return self._popen.returncode is None
136
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000137 @property
138 def name(self):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000139 return self._name
140
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000141 @name.setter
142 def name(self, name):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000143 assert isinstance(name, str), 'name must be a string'
144 self._name = name
145
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000146 @property
147 def daemon(self):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000148 '''
149 Return whether process is a daemon
150 '''
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100151 return self._config.get('daemon', False)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000152
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000153 @daemon.setter
154 def daemon(self, daemonic):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000155 '''
156 Set whether process is a daemon
157 '''
158 assert self._popen is None, 'process has already started'
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100159 self._config['daemon'] = daemonic
Benjamin Petersone711caf2008-06-11 16:44:04 +0000160
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000161 @property
162 def authkey(self):
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100163 return self._config['authkey']
Benjamin Petersone711caf2008-06-11 16:44:04 +0000164
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000165 @authkey.setter
166 def authkey(self, authkey):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000167 '''
168 Set authorization key of process
169 '''
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100170 self._config['authkey'] = AuthenticationString(authkey)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000171
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000172 @property
173 def exitcode(self):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000174 '''
175 Return exit code of process or `None` if it has yet to stop
176 '''
177 if self._popen is None:
178 return self._popen
179 return self._popen.poll()
180
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000181 @property
182 def ident(self):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000183 '''
Florent Xiclunab519d232010-03-04 16:10:55 +0000184 Return identifier (PID) of process or `None` if it has yet to start
Benjamin Petersone711caf2008-06-11 16:44:04 +0000185 '''
186 if self is _current_process:
187 return os.getpid()
188 else:
189 return self._popen and self._popen.pid
190
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000191 pid = ident
Benjamin Petersone711caf2008-06-11 16:44:04 +0000192
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200193 @property
194 def sentinel(self):
195 '''
196 Return a file descriptor (Unix) or handle (Windows) suitable for
197 waiting for process termination.
198 '''
199 try:
200 return self._sentinel
201 except AttributeError:
202 raise ValueError("process not started")
203
Benjamin Petersone711caf2008-06-11 16:44:04 +0000204 def __repr__(self):
205 if self is _current_process:
206 status = 'started'
207 elif self._parent_pid != os.getpid():
208 status = 'unknown'
209 elif self._popen is None:
210 status = 'initial'
211 else:
212 if self._popen.poll() is not None:
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000213 status = self.exitcode
Benjamin Petersone711caf2008-06-11 16:44:04 +0000214 else:
215 status = 'started'
216
217 if type(status) is int:
218 if status == 0:
219 status = 'stopped'
220 else:
221 status = 'stopped[%s]' % _exitcode_to_name.get(status, status)
222
223 return '<%s(%s, %s%s)>' % (type(self).__name__, self._name,
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100224 status, self.daemon and ' daemon' or '')
Benjamin Petersone711caf2008-06-11 16:44:04 +0000225
226 ##
227
228 def _bootstrap(self):
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100229 from . import util, context
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100230 global _current_process, _process_counter, _children
Benjamin Petersone711caf2008-06-11 16:44:04 +0000231
232 try:
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100233 if self._start_method is not None:
234 context._force_start_method(self._start_method)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100235 _process_counter = itertools.count(1)
236 _children = set()
Victor Stinnera6d865c2016-03-25 09:29:50 +0100237 util._close_stdin()
Victor Stinner0f83b152011-06-17 12:31:49 +0200238 old_process = _current_process
Benjamin Petersone711caf2008-06-11 16:44:04 +0000239 _current_process = self
Victor Stinner0f83b152011-06-17 12:31:49 +0200240 try:
241 util._finalizer_registry.clear()
242 util._run_after_forkers()
243 finally:
244 # delay finalization of the old process object until after
245 # _run_after_forkers() is executed
246 del old_process
Benjamin Petersone711caf2008-06-11 16:44:04 +0000247 util.info('child process calling self.run()')
248 try:
249 self.run()
250 exitcode = 0
251 finally:
252 util._exit_function()
253 except SystemExit as e:
254 if not e.args:
255 exitcode = 1
Richard Oudkerk29471de2012-06-06 19:04:57 +0100256 elif isinstance(e.args[0], int):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000257 exitcode = e.args[0]
258 else:
Richard Oudkerk29471de2012-06-06 19:04:57 +0100259 sys.stderr.write(str(e.args[0]) + '\n')
Richard Oudkerk8731d7b2013-11-17 17:24:11 +0000260 exitcode = 1
Benjamin Petersone711caf2008-06-11 16:44:04 +0000261 except:
262 exitcode = 1
263 import traceback
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000264 sys.stderr.write('Process %s:\n' % self.name)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000265 traceback.print_exc()
Antoine Pitrou84a0fbf2012-01-27 10:52:37 +0100266 finally:
267 util.info('process exiting with exitcode %d' % exitcode)
268 sys.stdout.flush()
269 sys.stderr.flush()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000270
Benjamin Petersone711caf2008-06-11 16:44:04 +0000271 return exitcode
272
273#
274# We subclass bytes to avoid accidental transmission of auth keys over network
275#
276
277class AuthenticationString(bytes):
278 def __reduce__(self):
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100279 from .context import get_spawning_popen
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100280 if get_spawning_popen() is None:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000281 raise TypeError(
282 'Pickling an AuthenticationString object is '
283 'disallowed for security reasons'
284 )
285 return AuthenticationString, (bytes(self),)
286
287#
288# Create object representing the main process
289#
290
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100291class _MainProcess(BaseProcess):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000292
293 def __init__(self):
294 self._identity = ()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000295 self._name = 'MainProcess'
296 self._parent_pid = None
297 self._popen = None
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100298 self._config = {'authkey': AuthenticationString(os.urandom(32)),
Richard Oudkerke9436972013-11-02 17:05:07 +0000299 'semprefix': '/mp'}
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100300 # Note that some versions of FreeBSD only allow named
Richard Oudkerke9436972013-11-02 17:05:07 +0000301 # semaphores to have names of up to 14 characters. Therefore
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100302 # we choose a short prefix.
Richard Oudkerke9436972013-11-02 17:05:07 +0000303 #
304 # On MacOSX in a sandbox it may be necessary to use a
305 # different prefix -- see #19478.
306 #
307 # Everything in self._config will be inherited by descendant
308 # processes.
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100309
Benjamin Petersone711caf2008-06-11 16:44:04 +0000310
311_current_process = _MainProcess()
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100312_process_counter = itertools.count(1)
313_children = set()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000314del _MainProcess
315
316#
317# Give names to some return codes
318#
319
320_exitcode_to_name = {}
321
322for name, signum in list(signal.__dict__.items()):
323 if name[:3]=='SIG' and '_' not in name:
324 _exitcode_to_name[-signum] = name
Antoine Pitrouc081c0c2011-07-15 22:12:24 +0200325
326# For debug and leak testing
327_dangling = WeakSet()