blob: 0b2e0b45b2397be0a672b36b7e4c572b3996cf53 [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
Thomas Moreauc09a9f52019-05-20 21:37:05 +020010__all__ = ['BaseProcess', 'current_process', 'active_children',
11 'parent_process']
Benjamin Petersone711caf2008-06-11 16:44:04 +000012
13#
14# Imports
15#
16
17import os
18import sys
19import signal
20import itertools
Antoine Pitrouee84a602017-08-16 20:53:28 +020021import threading
Antoine Pitrouc081c0c2011-07-15 22:12:24 +020022from _weakrefset import WeakSet
Benjamin Petersone711caf2008-06-11 16:44:04 +000023
24#
25#
26#
27
28try:
29 ORIGINAL_DIR = os.path.abspath(os.getcwd())
30except OSError:
31 ORIGINAL_DIR = None
32
Benjamin Petersone711caf2008-06-11 16:44:04 +000033#
34# Public functions
35#
36
37def current_process():
38 '''
39 Return process object representing the current process
40 '''
41 return _current_process
42
43def active_children():
44 '''
45 Return list of process objects corresponding to live child processes
46 '''
47 _cleanup()
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010048 return list(_children)
Benjamin Petersone711caf2008-06-11 16:44:04 +000049
Thomas Moreauc09a9f52019-05-20 21:37:05 +020050
51def parent_process():
52 '''
53 Return process object representing the parent process
54 '''
55 return _parent_process
56
Benjamin Petersone711caf2008-06-11 16:44:04 +000057#
58#
59#
60
61def _cleanup():
62 # check for processes which have finished
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010063 for p in list(_children):
Benjamin Petersone711caf2008-06-11 16:44:04 +000064 if p._popen.poll() is not None:
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010065 _children.discard(p)
Benjamin Petersone711caf2008-06-11 16:44:04 +000066
67#
68# The `Process` class
69#
70
Richard Oudkerkb1694cf2013-10-16 16:41:56 +010071class BaseProcess(object):
Benjamin Petersone711caf2008-06-11 16:44:04 +000072 '''
73 Process objects represent activity that is run in a separate process
74
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010075 The class is analogous to `threading.Thread`
Benjamin Petersone711caf2008-06-11 16:44:04 +000076 '''
Richard Oudkerkb1694cf2013-10-16 16:41:56 +010077 def _Popen(self):
78 raise NotImplementedError
Benjamin Petersone711caf2008-06-11 16:44:04 +000079
Antoine Pitrou0bd4deb2011-02-25 22:07:43 +000080 def __init__(self, group=None, target=None, name=None, args=(), kwargs={},
81 *, daemon=None):
Benjamin Petersone711caf2008-06-11 16:44:04 +000082 assert group is None, 'group argument must be None for now'
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010083 count = next(_process_counter)
Benjamin Petersone711caf2008-06-11 16:44:04 +000084 self._identity = _current_process._identity + (count,)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010085 self._config = _current_process._config.copy()
Benjamin Petersone711caf2008-06-11 16:44:04 +000086 self._parent_pid = os.getpid()
Thomas Moreauc09a9f52019-05-20 21:37:05 +020087 self._parent_name = _current_process.name
Benjamin Petersone711caf2008-06-11 16:44:04 +000088 self._popen = None
Antoine Pitrou13e96cc2017-06-24 19:22:23 +020089 self._closed = False
Benjamin Petersone711caf2008-06-11 16:44:04 +000090 self._target = target
91 self._args = tuple(args)
92 self._kwargs = dict(kwargs)
93 self._name = name or type(self).__name__ + '-' + \
94 ':'.join(str(i) for i in self._identity)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010095 if daemon is not None:
96 self.daemon = daemon
Antoine Pitrouc081c0c2011-07-15 22:12:24 +020097 _dangling.add(self)
Benjamin Petersone711caf2008-06-11 16:44:04 +000098
Antoine Pitrou13e96cc2017-06-24 19:22:23 +020099 def _check_closed(self):
100 if self._closed:
101 raise ValueError("process object is closed")
102
Benjamin Petersone711caf2008-06-11 16:44:04 +0000103 def run(self):
104 '''
105 Method to be run in sub-process; can be overridden in sub-class
106 '''
107 if self._target:
108 self._target(*self._args, **self._kwargs)
109
110 def start(self):
111 '''
112 Start child process
113 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200114 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000115 assert self._popen is None, 'cannot start a process twice'
116 assert self._parent_pid == os.getpid(), \
117 'can only start a process object created by current process'
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100118 assert not _current_process._config.get('daemon'), \
Benjamin Petersone711caf2008-06-11 16:44:04 +0000119 'daemonic processes are not allowed to have children'
120 _cleanup()
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100121 self._popen = self._Popen(self)
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200122 self._sentinel = self._popen.sentinel
Antoine Pitrou79d37ae2017-06-28 12:29:08 +0200123 # Avoid a refcycle if the target function holds an indirect
124 # reference to the process object (see bpo-30775)
125 del self._target, self._args, self._kwargs
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100126 _children.add(self)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000127
128 def terminate(self):
129 '''
130 Terminate process; sends SIGTERM signal or uses TerminateProcess()
131 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200132 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000133 self._popen.terminate()
134
Vitor Pereiraba75af72017-07-18 16:34:23 +0100135 def kill(self):
136 '''
137 Terminate process; sends SIGKILL signal or uses TerminateProcess()
138 '''
139 self._check_closed()
140 self._popen.kill()
141
Benjamin Petersone711caf2008-06-11 16:44:04 +0000142 def join(self, timeout=None):
143 '''
144 Wait until child process terminates
145 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200146 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000147 assert self._parent_pid == os.getpid(), 'can only join a child process'
148 assert self._popen is not None, 'can only join a started process'
149 res = self._popen.wait(timeout)
150 if res is not None:
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100151 _children.discard(self)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000152
153 def is_alive(self):
154 '''
155 Return whether process is alive
156 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200157 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000158 if self is _current_process:
159 return True
160 assert self._parent_pid == os.getpid(), 'can only test a child process'
Victor Stinner2db64822017-07-26 02:32:42 +0200161
Benjamin Petersone711caf2008-06-11 16:44:04 +0000162 if self._popen is None:
163 return False
Victor Stinner2db64822017-07-26 02:32:42 +0200164
165 returncode = self._popen.poll()
166 if returncode is None:
167 return True
168 else:
169 _children.discard(self)
170 return False
Benjamin Petersone711caf2008-06-11 16:44:04 +0000171
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200172 def close(self):
173 '''
174 Close the Process object.
175
176 This method releases resources held by the Process object. It is
177 an error to call this method if the child process is still running.
178 '''
179 if self._popen is not None:
180 if self._popen.poll() is None:
181 raise ValueError("Cannot close a process while it is still running. "
182 "You should first call join() or terminate().")
183 self._popen.close()
184 self._popen = None
185 del self._sentinel
186 _children.discard(self)
187 self._closed = True
188
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000189 @property
190 def name(self):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000191 return self._name
192
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000193 @name.setter
194 def name(self, name):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000195 assert isinstance(name, str), 'name must be a string'
196 self._name = name
197
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000198 @property
199 def daemon(self):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000200 '''
201 Return whether process is a daemon
202 '''
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100203 return self._config.get('daemon', False)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000204
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000205 @daemon.setter
206 def daemon(self, daemonic):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000207 '''
208 Set whether process is a daemon
209 '''
210 assert self._popen is None, 'process has already started'
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100211 self._config['daemon'] = daemonic
Benjamin Petersone711caf2008-06-11 16:44:04 +0000212
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000213 @property
214 def authkey(self):
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100215 return self._config['authkey']
Benjamin Petersone711caf2008-06-11 16:44:04 +0000216
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000217 @authkey.setter
218 def authkey(self, authkey):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000219 '''
220 Set authorization key of process
221 '''
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100222 self._config['authkey'] = AuthenticationString(authkey)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000223
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000224 @property
225 def exitcode(self):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000226 '''
227 Return exit code of process or `None` if it has yet to stop
228 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200229 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000230 if self._popen is None:
231 return self._popen
232 return self._popen.poll()
233
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000234 @property
235 def ident(self):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000236 '''
Florent Xiclunab519d232010-03-04 16:10:55 +0000237 Return identifier (PID) of process or `None` if it has yet to start
Benjamin Petersone711caf2008-06-11 16:44:04 +0000238 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200239 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000240 if self is _current_process:
241 return os.getpid()
242 else:
243 return self._popen and self._popen.pid
244
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000245 pid = ident
Benjamin Petersone711caf2008-06-11 16:44:04 +0000246
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200247 @property
248 def sentinel(self):
249 '''
250 Return a file descriptor (Unix) or handle (Windows) suitable for
251 waiting for process termination.
252 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200253 self._check_closed()
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200254 try:
255 return self._sentinel
256 except AttributeError:
Serhiy Storchaka5affd232017-04-05 09:37:24 +0300257 raise ValueError("process not started") from None
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200258
Benjamin Petersone711caf2008-06-11 16:44:04 +0000259 def __repr__(self):
Victor Stinner7acd50a2018-12-14 12:58:52 +0100260 exitcode = None
Benjamin Petersone711caf2008-06-11 16:44:04 +0000261 if self is _current_process:
262 status = 'started'
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200263 elif self._closed:
264 status = 'closed'
Benjamin Petersone711caf2008-06-11 16:44:04 +0000265 elif self._parent_pid != os.getpid():
266 status = 'unknown'
267 elif self._popen is None:
268 status = 'initial'
269 else:
Victor Stinner7acd50a2018-12-14 12:58:52 +0100270 exitcode = self._popen.poll()
271 if exitcode is not None:
272 status = 'stopped'
Benjamin Petersone711caf2008-06-11 16:44:04 +0000273 else:
274 status = 'started'
275
Victor Stinner7acd50a2018-12-14 12:58:52 +0100276 info = [type(self).__name__, 'name=%r' % self._name]
277 if self._popen is not None:
278 info.append('pid=%s' % self._popen.pid)
279 info.append('parent=%s' % self._parent_pid)
280 info.append(status)
281 if exitcode is not None:
282 exitcode = _exitcode_to_name.get(exitcode, exitcode)
283 info.append('exitcode=%s' % exitcode)
284 if self.daemon:
285 info.append('daemon')
286 return '<%s>' % ' '.join(info)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000287
288 ##
289
Thomas Moreauc09a9f52019-05-20 21:37:05 +0200290 def _bootstrap(self, parent_sentinel=None):
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100291 from . import util, context
Thomas Moreauc09a9f52019-05-20 21:37:05 +0200292 global _current_process, _parent_process, _process_counter, _children
Benjamin Petersone711caf2008-06-11 16:44:04 +0000293
294 try:
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100295 if self._start_method is not None:
296 context._force_start_method(self._start_method)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100297 _process_counter = itertools.count(1)
298 _children = set()
Victor Stinnera6d865c2016-03-25 09:29:50 +0100299 util._close_stdin()
Victor Stinner0f83b152011-06-17 12:31:49 +0200300 old_process = _current_process
Benjamin Petersone711caf2008-06-11 16:44:04 +0000301 _current_process = self
Thomas Moreauc09a9f52019-05-20 21:37:05 +0200302 _parent_process = _ParentProcess(
303 self._parent_name, self._parent_pid, parent_sentinel)
Jake Teslerc6b20be2019-11-19 11:50:12 -0800304 if threading._HAVE_THREAD_NATIVE_ID:
305 threading.main_thread()._set_native_id()
Victor Stinner0f83b152011-06-17 12:31:49 +0200306 try:
307 util._finalizer_registry.clear()
308 util._run_after_forkers()
309 finally:
310 # delay finalization of the old process object until after
311 # _run_after_forkers() is executed
312 del old_process
Benjamin Petersone711caf2008-06-11 16:44:04 +0000313 util.info('child process calling self.run()')
314 try:
315 self.run()
316 exitcode = 0
317 finally:
318 util._exit_function()
319 except SystemExit as e:
Christopher Huntc2ac4cf2020-02-21 17:33:04 +0800320 if e.code is None:
321 exitcode = 0
322 elif isinstance(e.code, int):
323 exitcode = e.code
Benjamin Petersone711caf2008-06-11 16:44:04 +0000324 else:
Christopher Huntc2ac4cf2020-02-21 17:33:04 +0800325 sys.stderr.write(str(e.code) + '\n')
Richard Oudkerk8731d7b2013-11-17 17:24:11 +0000326 exitcode = 1
Benjamin Petersone711caf2008-06-11 16:44:04 +0000327 except:
328 exitcode = 1
329 import traceback
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000330 sys.stderr.write('Process %s:\n' % self.name)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000331 traceback.print_exc()
Antoine Pitrou84a0fbf2012-01-27 10:52:37 +0100332 finally:
Antoine Pitrouee84a602017-08-16 20:53:28 +0200333 threading._shutdown()
Antoine Pitrou84a0fbf2012-01-27 10:52:37 +0100334 util.info('process exiting with exitcode %d' % exitcode)
Antoine Pitroue756f662018-03-11 19:21:38 +0100335 util._flush_std_streams()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000336
Benjamin Petersone711caf2008-06-11 16:44:04 +0000337 return exitcode
338
339#
340# We subclass bytes to avoid accidental transmission of auth keys over network
341#
342
343class AuthenticationString(bytes):
344 def __reduce__(self):
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100345 from .context import get_spawning_popen
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100346 if get_spawning_popen() is None:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000347 raise TypeError(
348 'Pickling an AuthenticationString object is '
349 'disallowed for security reasons'
350 )
351 return AuthenticationString, (bytes(self),)
352
Thomas Moreauc09a9f52019-05-20 21:37:05 +0200353
354#
355# Create object representing the parent process
356#
357
358class _ParentProcess(BaseProcess):
359
360 def __init__(self, name, pid, sentinel):
361 self._identity = ()
362 self._name = name
363 self._pid = pid
364 self._parent_pid = None
365 self._popen = None
366 self._closed = False
367 self._sentinel = sentinel
368 self._config = {}
369
370 def is_alive(self):
371 from multiprocessing.connection import wait
372 return not wait([self._sentinel], timeout=0)
373
374 @property
375 def ident(self):
376 return self._pid
377
378 def join(self, timeout=None):
379 '''
380 Wait until parent process terminates
381 '''
382 from multiprocessing.connection import wait
383 wait([self._sentinel], timeout=timeout)
384
385 pid = ident
386
Benjamin Petersone711caf2008-06-11 16:44:04 +0000387#
388# Create object representing the main process
389#
390
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100391class _MainProcess(BaseProcess):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000392
393 def __init__(self):
394 self._identity = ()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000395 self._name = 'MainProcess'
396 self._parent_pid = None
397 self._popen = None
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200398 self._closed = False
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100399 self._config = {'authkey': AuthenticationString(os.urandom(32)),
Richard Oudkerke9436972013-11-02 17:05:07 +0000400 'semprefix': '/mp'}
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100401 # Note that some versions of FreeBSD only allow named
Richard Oudkerke9436972013-11-02 17:05:07 +0000402 # semaphores to have names of up to 14 characters. Therefore
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100403 # we choose a short prefix.
Richard Oudkerke9436972013-11-02 17:05:07 +0000404 #
405 # On MacOSX in a sandbox it may be necessary to use a
406 # different prefix -- see #19478.
407 #
408 # Everything in self._config will be inherited by descendant
409 # processes.
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100410
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200411 def close(self):
412 pass
413
Benjamin Petersone711caf2008-06-11 16:44:04 +0000414
Thomas Moreauc09a9f52019-05-20 21:37:05 +0200415_parent_process = None
Benjamin Petersone711caf2008-06-11 16:44:04 +0000416_current_process = _MainProcess()
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100417_process_counter = itertools.count(1)
418_children = set()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000419del _MainProcess
420
421#
422# Give names to some return codes
423#
424
425_exitcode_to_name = {}
426
427for name, signum in list(signal.__dict__.items()):
428 if name[:3]=='SIG' and '_' not in name:
Victor Stinner7acd50a2018-12-14 12:58:52 +0100429 _exitcode_to_name[-signum] = f'-{name}'
Antoine Pitrouc081c0c2011-07-15 22:12:24 +0200430
431# For debug and leak testing
432_dangling = WeakSet()