blob: 780f2d0c273472cf0a14eee0d58c530da92b5175 [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 Pitrouee84a602017-08-16 20:53:28 +020020import threading
Antoine Pitrouc081c0c2011-07-15 22:12:24 +020021from _weakrefset import WeakSet
Benjamin Petersone711caf2008-06-11 16:44:04 +000022
23#
24#
25#
26
27try:
28 ORIGINAL_DIR = os.path.abspath(os.getcwd())
29except OSError:
30 ORIGINAL_DIR = None
31
Benjamin Petersone711caf2008-06-11 16:44:04 +000032#
33# Public functions
34#
35
36def current_process():
37 '''
38 Return process object representing the current process
39 '''
40 return _current_process
41
42def active_children():
43 '''
44 Return list of process objects corresponding to live child processes
45 '''
46 _cleanup()
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010047 return list(_children)
Benjamin Petersone711caf2008-06-11 16:44:04 +000048
49#
50#
51#
52
53def _cleanup():
54 # check for processes which have finished
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010055 for p in list(_children):
Benjamin Petersone711caf2008-06-11 16:44:04 +000056 if p._popen.poll() is not None:
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010057 _children.discard(p)
Benjamin Petersone711caf2008-06-11 16:44:04 +000058
59#
60# The `Process` class
61#
62
Richard Oudkerkb1694cf2013-10-16 16:41:56 +010063class BaseProcess(object):
Benjamin Petersone711caf2008-06-11 16:44:04 +000064 '''
65 Process objects represent activity that is run in a separate process
66
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010067 The class is analogous to `threading.Thread`
Benjamin Petersone711caf2008-06-11 16:44:04 +000068 '''
Richard Oudkerkb1694cf2013-10-16 16:41:56 +010069 def _Popen(self):
70 raise NotImplementedError
Benjamin Petersone711caf2008-06-11 16:44:04 +000071
Antoine Pitrou0bd4deb2011-02-25 22:07:43 +000072 def __init__(self, group=None, target=None, name=None, args=(), kwargs={},
73 *, daemon=None):
Benjamin Petersone711caf2008-06-11 16:44:04 +000074 assert group is None, 'group argument must be None for now'
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010075 count = next(_process_counter)
Benjamin Petersone711caf2008-06-11 16:44:04 +000076 self._identity = _current_process._identity + (count,)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010077 self._config = _current_process._config.copy()
Benjamin Petersone711caf2008-06-11 16:44:04 +000078 self._parent_pid = os.getpid()
79 self._popen = None
Antoine Pitrou13e96cc2017-06-24 19:22:23 +020080 self._closed = False
Benjamin Petersone711caf2008-06-11 16:44:04 +000081 self._target = target
82 self._args = tuple(args)
83 self._kwargs = dict(kwargs)
84 self._name = name or type(self).__name__ + '-' + \
85 ':'.join(str(i) for i in self._identity)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010086 if daemon is not None:
87 self.daemon = daemon
Antoine Pitrouc081c0c2011-07-15 22:12:24 +020088 _dangling.add(self)
Benjamin Petersone711caf2008-06-11 16:44:04 +000089
Antoine Pitrou13e96cc2017-06-24 19:22:23 +020090 def _check_closed(self):
91 if self._closed:
92 raise ValueError("process object is closed")
93
Benjamin Petersone711caf2008-06-11 16:44:04 +000094 def run(self):
95 '''
96 Method to be run in sub-process; can be overridden in sub-class
97 '''
98 if self._target:
99 self._target(*self._args, **self._kwargs)
100
101 def start(self):
102 '''
103 Start child process
104 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200105 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000106 assert self._popen is None, 'cannot start a process twice'
107 assert self._parent_pid == os.getpid(), \
108 'can only start a process object created by current process'
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100109 assert not _current_process._config.get('daemon'), \
Benjamin Petersone711caf2008-06-11 16:44:04 +0000110 'daemonic processes are not allowed to have children'
111 _cleanup()
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100112 self._popen = self._Popen(self)
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200113 self._sentinel = self._popen.sentinel
Antoine Pitrou79d37ae2017-06-28 12:29:08 +0200114 # Avoid a refcycle if the target function holds an indirect
115 # reference to the process object (see bpo-30775)
116 del self._target, self._args, self._kwargs
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100117 _children.add(self)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000118
119 def terminate(self):
120 '''
121 Terminate process; sends SIGTERM signal or uses TerminateProcess()
122 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200123 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000124 self._popen.terminate()
125
Vitor Pereiraba75af72017-07-18 16:34:23 +0100126 def kill(self):
127 '''
128 Terminate process; sends SIGKILL signal or uses TerminateProcess()
129 '''
130 self._check_closed()
131 self._popen.kill()
132
Benjamin Petersone711caf2008-06-11 16:44:04 +0000133 def join(self, timeout=None):
134 '''
135 Wait until child process terminates
136 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200137 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000138 assert self._parent_pid == os.getpid(), 'can only join a child process'
139 assert self._popen is not None, 'can only join a started process'
140 res = self._popen.wait(timeout)
141 if res is not None:
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100142 _children.discard(self)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000143
144 def is_alive(self):
145 '''
146 Return whether process is alive
147 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200148 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000149 if self is _current_process:
150 return True
151 assert self._parent_pid == os.getpid(), 'can only test a child process'
Victor Stinner2db64822017-07-26 02:32:42 +0200152
Benjamin Petersone711caf2008-06-11 16:44:04 +0000153 if self._popen is None:
154 return False
Victor Stinner2db64822017-07-26 02:32:42 +0200155
156 returncode = self._popen.poll()
157 if returncode is None:
158 return True
159 else:
160 _children.discard(self)
161 return False
Benjamin Petersone711caf2008-06-11 16:44:04 +0000162
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200163 def close(self):
164 '''
165 Close the Process object.
166
167 This method releases resources held by the Process object. It is
168 an error to call this method if the child process is still running.
169 '''
170 if self._popen is not None:
171 if self._popen.poll() is None:
172 raise ValueError("Cannot close a process while it is still running. "
173 "You should first call join() or terminate().")
174 self._popen.close()
175 self._popen = None
176 del self._sentinel
177 _children.discard(self)
178 self._closed = True
179
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000180 @property
181 def name(self):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000182 return self._name
183
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000184 @name.setter
185 def name(self, name):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000186 assert isinstance(name, str), 'name must be a string'
187 self._name = name
188
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000189 @property
190 def daemon(self):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000191 '''
192 Return whether process is a daemon
193 '''
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100194 return self._config.get('daemon', False)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000195
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000196 @daemon.setter
197 def daemon(self, daemonic):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000198 '''
199 Set whether process is a daemon
200 '''
201 assert self._popen is None, 'process has already started'
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100202 self._config['daemon'] = daemonic
Benjamin Petersone711caf2008-06-11 16:44:04 +0000203
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000204 @property
205 def authkey(self):
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100206 return self._config['authkey']
Benjamin Petersone711caf2008-06-11 16:44:04 +0000207
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000208 @authkey.setter
209 def authkey(self, authkey):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000210 '''
211 Set authorization key of process
212 '''
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100213 self._config['authkey'] = AuthenticationString(authkey)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000214
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000215 @property
216 def exitcode(self):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000217 '''
218 Return exit code of process or `None` if it has yet to stop
219 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200220 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000221 if self._popen is None:
222 return self._popen
223 return self._popen.poll()
224
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000225 @property
226 def ident(self):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000227 '''
Florent Xiclunab519d232010-03-04 16:10:55 +0000228 Return identifier (PID) of process or `None` if it has yet to start
Benjamin Petersone711caf2008-06-11 16:44:04 +0000229 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200230 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000231 if self is _current_process:
232 return os.getpid()
233 else:
234 return self._popen and self._popen.pid
235
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000236 pid = ident
Benjamin Petersone711caf2008-06-11 16:44:04 +0000237
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200238 @property
239 def sentinel(self):
240 '''
241 Return a file descriptor (Unix) or handle (Windows) suitable for
242 waiting for process termination.
243 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200244 self._check_closed()
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200245 try:
246 return self._sentinel
247 except AttributeError:
Serhiy Storchaka5affd232017-04-05 09:37:24 +0300248 raise ValueError("process not started") from None
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200249
Benjamin Petersone711caf2008-06-11 16:44:04 +0000250 def __repr__(self):
Victor Stinner7acd50a2018-12-14 12:58:52 +0100251 exitcode = None
Benjamin Petersone711caf2008-06-11 16:44:04 +0000252 if self is _current_process:
253 status = 'started'
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200254 elif self._closed:
255 status = 'closed'
Benjamin Petersone711caf2008-06-11 16:44:04 +0000256 elif self._parent_pid != os.getpid():
257 status = 'unknown'
258 elif self._popen is None:
259 status = 'initial'
260 else:
Victor Stinner7acd50a2018-12-14 12:58:52 +0100261 exitcode = self._popen.poll()
262 if exitcode is not None:
263 status = 'stopped'
Benjamin Petersone711caf2008-06-11 16:44:04 +0000264 else:
265 status = 'started'
266
Victor Stinner7acd50a2018-12-14 12:58:52 +0100267 info = [type(self).__name__, 'name=%r' % self._name]
268 if self._popen is not None:
269 info.append('pid=%s' % self._popen.pid)
270 info.append('parent=%s' % self._parent_pid)
271 info.append(status)
272 if exitcode is not None:
273 exitcode = _exitcode_to_name.get(exitcode, exitcode)
274 info.append('exitcode=%s' % exitcode)
275 if self.daemon:
276 info.append('daemon')
277 return '<%s>' % ' '.join(info)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000278
279 ##
280
281 def _bootstrap(self):
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100282 from . import util, context
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100283 global _current_process, _process_counter, _children
Benjamin Petersone711caf2008-06-11 16:44:04 +0000284
285 try:
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100286 if self._start_method is not None:
287 context._force_start_method(self._start_method)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100288 _process_counter = itertools.count(1)
289 _children = set()
Victor Stinnera6d865c2016-03-25 09:29:50 +0100290 util._close_stdin()
Victor Stinner0f83b152011-06-17 12:31:49 +0200291 old_process = _current_process
Benjamin Petersone711caf2008-06-11 16:44:04 +0000292 _current_process = self
Victor Stinner0f83b152011-06-17 12:31:49 +0200293 try:
294 util._finalizer_registry.clear()
295 util._run_after_forkers()
296 finally:
297 # delay finalization of the old process object until after
298 # _run_after_forkers() is executed
299 del old_process
Benjamin Petersone711caf2008-06-11 16:44:04 +0000300 util.info('child process calling self.run()')
301 try:
302 self.run()
303 exitcode = 0
304 finally:
305 util._exit_function()
306 except SystemExit as e:
307 if not e.args:
308 exitcode = 1
Richard Oudkerk29471de2012-06-06 19:04:57 +0100309 elif isinstance(e.args[0], int):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000310 exitcode = e.args[0]
311 else:
Richard Oudkerk29471de2012-06-06 19:04:57 +0100312 sys.stderr.write(str(e.args[0]) + '\n')
Richard Oudkerk8731d7b2013-11-17 17:24:11 +0000313 exitcode = 1
Benjamin Petersone711caf2008-06-11 16:44:04 +0000314 except:
315 exitcode = 1
316 import traceback
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000317 sys.stderr.write('Process %s:\n' % self.name)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000318 traceback.print_exc()
Antoine Pitrou84a0fbf2012-01-27 10:52:37 +0100319 finally:
Antoine Pitrouee84a602017-08-16 20:53:28 +0200320 threading._shutdown()
Antoine Pitrou84a0fbf2012-01-27 10:52:37 +0100321 util.info('process exiting with exitcode %d' % exitcode)
Antoine Pitroue756f662018-03-11 19:21:38 +0100322 util._flush_std_streams()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000323
Benjamin Petersone711caf2008-06-11 16:44:04 +0000324 return exitcode
325
326#
327# We subclass bytes to avoid accidental transmission of auth keys over network
328#
329
330class AuthenticationString(bytes):
331 def __reduce__(self):
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100332 from .context import get_spawning_popen
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100333 if get_spawning_popen() is None:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000334 raise TypeError(
335 'Pickling an AuthenticationString object is '
336 'disallowed for security reasons'
337 )
338 return AuthenticationString, (bytes(self),)
339
340#
341# Create object representing the main process
342#
343
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100344class _MainProcess(BaseProcess):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000345
346 def __init__(self):
347 self._identity = ()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000348 self._name = 'MainProcess'
349 self._parent_pid = None
350 self._popen = None
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200351 self._closed = False
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100352 self._config = {'authkey': AuthenticationString(os.urandom(32)),
Richard Oudkerke9436972013-11-02 17:05:07 +0000353 'semprefix': '/mp'}
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100354 # Note that some versions of FreeBSD only allow named
Richard Oudkerke9436972013-11-02 17:05:07 +0000355 # semaphores to have names of up to 14 characters. Therefore
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100356 # we choose a short prefix.
Richard Oudkerke9436972013-11-02 17:05:07 +0000357 #
358 # On MacOSX in a sandbox it may be necessary to use a
359 # different prefix -- see #19478.
360 #
361 # Everything in self._config will be inherited by descendant
362 # processes.
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100363
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200364 def close(self):
365 pass
366
Benjamin Petersone711caf2008-06-11 16:44:04 +0000367
368_current_process = _MainProcess()
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100369_process_counter = itertools.count(1)
370_children = set()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000371del _MainProcess
372
373#
374# Give names to some return codes
375#
376
377_exitcode_to_name = {}
378
379for name, signum in list(signal.__dict__.items()):
380 if name[:3]=='SIG' and '_' not in name:
Victor Stinner7acd50a2018-12-14 12:58:52 +0100381 _exitcode_to_name[-signum] = f'-{name}'
Antoine Pitrouc081c0c2011-07-15 22:12:24 +0200382
383# For debug and leak testing
384_dangling = WeakSet()