blob: fde97b7a67e4c8d8370dc341f6e1b300e01da383 [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
Antoine Pitrou13e96cc2017-06-24 19:22:23 +020079 self._closed = False
Benjamin Petersone711caf2008-06-11 16:44:04 +000080 self._target = target
81 self._args = tuple(args)
82 self._kwargs = dict(kwargs)
83 self._name = name or type(self).__name__ + '-' + \
84 ':'.join(str(i) for i in self._identity)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010085 if daemon is not None:
86 self.daemon = daemon
Antoine Pitrouc081c0c2011-07-15 22:12:24 +020087 _dangling.add(self)
Benjamin Petersone711caf2008-06-11 16:44:04 +000088
Antoine Pitrou13e96cc2017-06-24 19:22:23 +020089 def _check_closed(self):
90 if self._closed:
91 raise ValueError("process object is closed")
92
Benjamin Petersone711caf2008-06-11 16:44:04 +000093 def run(self):
94 '''
95 Method to be run in sub-process; can be overridden in sub-class
96 '''
97 if self._target:
98 self._target(*self._args, **self._kwargs)
99
100 def start(self):
101 '''
102 Start child process
103 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200104 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000105 assert self._popen is None, 'cannot start a process twice'
106 assert self._parent_pid == os.getpid(), \
107 'can only start a process object created by current process'
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100108 assert not _current_process._config.get('daemon'), \
Benjamin Petersone711caf2008-06-11 16:44:04 +0000109 'daemonic processes are not allowed to have children'
110 _cleanup()
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100111 self._popen = self._Popen(self)
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200112 self._sentinel = self._popen.sentinel
Antoine Pitrou79d37ae2017-06-28 12:29:08 +0200113 # Avoid a refcycle if the target function holds an indirect
114 # reference to the process object (see bpo-30775)
115 del self._target, self._args, self._kwargs
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100116 _children.add(self)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000117
118 def terminate(self):
119 '''
120 Terminate process; sends SIGTERM signal or uses TerminateProcess()
121 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200122 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000123 self._popen.terminate()
124
125 def join(self, timeout=None):
126 '''
127 Wait until child process terminates
128 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200129 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000130 assert self._parent_pid == os.getpid(), 'can only join a child process'
131 assert self._popen is not None, 'can only join a started process'
132 res = self._popen.wait(timeout)
133 if res is not None:
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100134 _children.discard(self)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000135
136 def is_alive(self):
137 '''
138 Return whether process is alive
139 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200140 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000141 if self is _current_process:
142 return True
143 assert self._parent_pid == os.getpid(), 'can only test a child process'
144 if self._popen is None:
145 return False
146 self._popen.poll()
147 return self._popen.returncode is None
148
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200149 def close(self):
150 '''
151 Close the Process object.
152
153 This method releases resources held by the Process object. It is
154 an error to call this method if the child process is still running.
155 '''
156 if self._popen is not None:
157 if self._popen.poll() is None:
158 raise ValueError("Cannot close a process while it is still running. "
159 "You should first call join() or terminate().")
160 self._popen.close()
161 self._popen = None
162 del self._sentinel
163 _children.discard(self)
164 self._closed = True
165
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000166 @property
167 def name(self):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000168 return self._name
169
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000170 @name.setter
171 def name(self, name):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000172 assert isinstance(name, str), 'name must be a string'
173 self._name = name
174
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000175 @property
176 def daemon(self):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000177 '''
178 Return whether process is a daemon
179 '''
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100180 return self._config.get('daemon', False)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000181
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000182 @daemon.setter
183 def daemon(self, daemonic):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000184 '''
185 Set whether process is a daemon
186 '''
187 assert self._popen is None, 'process has already started'
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100188 self._config['daemon'] = daemonic
Benjamin Petersone711caf2008-06-11 16:44:04 +0000189
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000190 @property
191 def authkey(self):
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100192 return self._config['authkey']
Benjamin Petersone711caf2008-06-11 16:44:04 +0000193
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000194 @authkey.setter
195 def authkey(self, authkey):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000196 '''
197 Set authorization key of process
198 '''
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100199 self._config['authkey'] = AuthenticationString(authkey)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000200
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000201 @property
202 def exitcode(self):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000203 '''
204 Return exit code of process or `None` if it has yet to stop
205 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200206 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000207 if self._popen is None:
208 return self._popen
209 return self._popen.poll()
210
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000211 @property
212 def ident(self):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000213 '''
Florent Xiclunab519d232010-03-04 16:10:55 +0000214 Return identifier (PID) of process or `None` if it has yet to start
Benjamin Petersone711caf2008-06-11 16:44:04 +0000215 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200216 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000217 if self is _current_process:
218 return os.getpid()
219 else:
220 return self._popen and self._popen.pid
221
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000222 pid = ident
Benjamin Petersone711caf2008-06-11 16:44:04 +0000223
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200224 @property
225 def sentinel(self):
226 '''
227 Return a file descriptor (Unix) or handle (Windows) suitable for
228 waiting for process termination.
229 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200230 self._check_closed()
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200231 try:
232 return self._sentinel
233 except AttributeError:
Serhiy Storchaka5affd232017-04-05 09:37:24 +0300234 raise ValueError("process not started") from None
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200235
Benjamin Petersone711caf2008-06-11 16:44:04 +0000236 def __repr__(self):
237 if self is _current_process:
238 status = 'started'
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200239 elif self._closed:
240 status = 'closed'
Benjamin Petersone711caf2008-06-11 16:44:04 +0000241 elif self._parent_pid != os.getpid():
242 status = 'unknown'
243 elif self._popen is None:
244 status = 'initial'
245 else:
246 if self._popen.poll() is not None:
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000247 status = self.exitcode
Benjamin Petersone711caf2008-06-11 16:44:04 +0000248 else:
249 status = 'started'
250
251 if type(status) is int:
252 if status == 0:
253 status = 'stopped'
254 else:
255 status = 'stopped[%s]' % _exitcode_to_name.get(status, status)
256
257 return '<%s(%s, %s%s)>' % (type(self).__name__, self._name,
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100258 status, self.daemon and ' daemon' or '')
Benjamin Petersone711caf2008-06-11 16:44:04 +0000259
260 ##
261
262 def _bootstrap(self):
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100263 from . import util, context
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100264 global _current_process, _process_counter, _children
Benjamin Petersone711caf2008-06-11 16:44:04 +0000265
266 try:
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100267 if self._start_method is not None:
268 context._force_start_method(self._start_method)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100269 _process_counter = itertools.count(1)
270 _children = set()
Victor Stinnera6d865c2016-03-25 09:29:50 +0100271 util._close_stdin()
Victor Stinner0f83b152011-06-17 12:31:49 +0200272 old_process = _current_process
Benjamin Petersone711caf2008-06-11 16:44:04 +0000273 _current_process = self
Victor Stinner0f83b152011-06-17 12:31:49 +0200274 try:
275 util._finalizer_registry.clear()
276 util._run_after_forkers()
277 finally:
278 # delay finalization of the old process object until after
279 # _run_after_forkers() is executed
280 del old_process
Benjamin Petersone711caf2008-06-11 16:44:04 +0000281 util.info('child process calling self.run()')
282 try:
283 self.run()
284 exitcode = 0
285 finally:
286 util._exit_function()
287 except SystemExit as e:
288 if not e.args:
289 exitcode = 1
Richard Oudkerk29471de2012-06-06 19:04:57 +0100290 elif isinstance(e.args[0], int):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000291 exitcode = e.args[0]
292 else:
Richard Oudkerk29471de2012-06-06 19:04:57 +0100293 sys.stderr.write(str(e.args[0]) + '\n')
Richard Oudkerk8731d7b2013-11-17 17:24:11 +0000294 exitcode = 1
Benjamin Petersone711caf2008-06-11 16:44:04 +0000295 except:
296 exitcode = 1
297 import traceback
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000298 sys.stderr.write('Process %s:\n' % self.name)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000299 traceback.print_exc()
Antoine Pitrou84a0fbf2012-01-27 10:52:37 +0100300 finally:
301 util.info('process exiting with exitcode %d' % exitcode)
302 sys.stdout.flush()
303 sys.stderr.flush()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000304
Benjamin Petersone711caf2008-06-11 16:44:04 +0000305 return exitcode
306
307#
308# We subclass bytes to avoid accidental transmission of auth keys over network
309#
310
311class AuthenticationString(bytes):
312 def __reduce__(self):
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100313 from .context import get_spawning_popen
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100314 if get_spawning_popen() is None:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000315 raise TypeError(
316 'Pickling an AuthenticationString object is '
317 'disallowed for security reasons'
318 )
319 return AuthenticationString, (bytes(self),)
320
321#
322# Create object representing the main process
323#
324
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100325class _MainProcess(BaseProcess):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000326
327 def __init__(self):
328 self._identity = ()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000329 self._name = 'MainProcess'
330 self._parent_pid = None
331 self._popen = None
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200332 self._closed = False
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100333 self._config = {'authkey': AuthenticationString(os.urandom(32)),
Richard Oudkerke9436972013-11-02 17:05:07 +0000334 'semprefix': '/mp'}
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100335 # Note that some versions of FreeBSD only allow named
Richard Oudkerke9436972013-11-02 17:05:07 +0000336 # semaphores to have names of up to 14 characters. Therefore
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100337 # we choose a short prefix.
Richard Oudkerke9436972013-11-02 17:05:07 +0000338 #
339 # On MacOSX in a sandbox it may be necessary to use a
340 # different prefix -- see #19478.
341 #
342 # Everything in self._config will be inherited by descendant
343 # processes.
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100344
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200345 def close(self):
346 pass
347
Benjamin Petersone711caf2008-06-11 16:44:04 +0000348
349_current_process = _MainProcess()
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100350_process_counter = itertools.count(1)
351_children = set()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000352del _MainProcess
353
354#
355# Give names to some return codes
356#
357
358_exitcode_to_name = {}
359
360for name, signum in list(signal.__dict__.items()):
361 if name[:3]=='SIG' and '_' not in name:
362 _exitcode_to_name[-signum] = name
Antoine Pitrouc081c0c2011-07-15 22:12:24 +0200363
364# For debug and leak testing
365_dangling = WeakSet()