blob: ce4ce43cfca6fbd75d144f9cad2e3a31ea0516df [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
Vitor Pereiraba75af72017-07-18 16:34:23 +0100125 def kill(self):
126 '''
127 Terminate process; sends SIGKILL signal or uses TerminateProcess()
128 '''
129 self._check_closed()
130 self._popen.kill()
131
Benjamin Petersone711caf2008-06-11 16:44:04 +0000132 def join(self, timeout=None):
133 '''
134 Wait until child process terminates
135 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200136 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000137 assert self._parent_pid == os.getpid(), 'can only join a child process'
138 assert self._popen is not None, 'can only join a started process'
139 res = self._popen.wait(timeout)
140 if res is not None:
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100141 _children.discard(self)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000142
143 def is_alive(self):
144 '''
145 Return whether process is alive
146 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200147 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000148 if self is _current_process:
149 return True
150 assert self._parent_pid == os.getpid(), 'can only test a child process'
151 if self._popen is None:
152 return False
153 self._popen.poll()
154 return self._popen.returncode is None
155
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200156 def close(self):
157 '''
158 Close the Process object.
159
160 This method releases resources held by the Process object. It is
161 an error to call this method if the child process is still running.
162 '''
163 if self._popen is not None:
164 if self._popen.poll() is None:
165 raise ValueError("Cannot close a process while it is still running. "
166 "You should first call join() or terminate().")
167 self._popen.close()
168 self._popen = None
169 del self._sentinel
170 _children.discard(self)
171 self._closed = True
172
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000173 @property
174 def name(self):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000175 return self._name
176
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000177 @name.setter
178 def name(self, name):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000179 assert isinstance(name, str), 'name must be a string'
180 self._name = name
181
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000182 @property
183 def daemon(self):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000184 '''
185 Return whether process is a daemon
186 '''
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100187 return self._config.get('daemon', False)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000188
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000189 @daemon.setter
190 def daemon(self, daemonic):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000191 '''
192 Set whether process is a daemon
193 '''
194 assert self._popen is None, 'process has already started'
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100195 self._config['daemon'] = daemonic
Benjamin Petersone711caf2008-06-11 16:44:04 +0000196
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000197 @property
198 def authkey(self):
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100199 return self._config['authkey']
Benjamin Petersone711caf2008-06-11 16:44:04 +0000200
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000201 @authkey.setter
202 def authkey(self, authkey):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000203 '''
204 Set authorization key of process
205 '''
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100206 self._config['authkey'] = AuthenticationString(authkey)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000207
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000208 @property
209 def exitcode(self):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000210 '''
211 Return exit code of process or `None` if it has yet to stop
212 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200213 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000214 if self._popen is None:
215 return self._popen
216 return self._popen.poll()
217
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000218 @property
219 def ident(self):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000220 '''
Florent Xiclunab519d232010-03-04 16:10:55 +0000221 Return identifier (PID) of process or `None` if it has yet to start
Benjamin Petersone711caf2008-06-11 16:44:04 +0000222 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200223 self._check_closed()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000224 if self is _current_process:
225 return os.getpid()
226 else:
227 return self._popen and self._popen.pid
228
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000229 pid = ident
Benjamin Petersone711caf2008-06-11 16:44:04 +0000230
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200231 @property
232 def sentinel(self):
233 '''
234 Return a file descriptor (Unix) or handle (Windows) suitable for
235 waiting for process termination.
236 '''
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200237 self._check_closed()
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200238 try:
239 return self._sentinel
240 except AttributeError:
Serhiy Storchaka5affd232017-04-05 09:37:24 +0300241 raise ValueError("process not started") from None
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200242
Benjamin Petersone711caf2008-06-11 16:44:04 +0000243 def __repr__(self):
244 if self is _current_process:
245 status = 'started'
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200246 elif self._closed:
247 status = 'closed'
Benjamin Petersone711caf2008-06-11 16:44:04 +0000248 elif self._parent_pid != os.getpid():
249 status = 'unknown'
250 elif self._popen is None:
251 status = 'initial'
252 else:
253 if self._popen.poll() is not None:
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000254 status = self.exitcode
Benjamin Petersone711caf2008-06-11 16:44:04 +0000255 else:
256 status = 'started'
257
258 if type(status) is int:
259 if status == 0:
260 status = 'stopped'
261 else:
262 status = 'stopped[%s]' % _exitcode_to_name.get(status, status)
263
264 return '<%s(%s, %s%s)>' % (type(self).__name__, self._name,
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100265 status, self.daemon and ' daemon' or '')
Benjamin Petersone711caf2008-06-11 16:44:04 +0000266
267 ##
268
269 def _bootstrap(self):
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100270 from . import util, context
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100271 global _current_process, _process_counter, _children
Benjamin Petersone711caf2008-06-11 16:44:04 +0000272
273 try:
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100274 if self._start_method is not None:
275 context._force_start_method(self._start_method)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100276 _process_counter = itertools.count(1)
277 _children = set()
Victor Stinnera6d865c2016-03-25 09:29:50 +0100278 util._close_stdin()
Victor Stinner0f83b152011-06-17 12:31:49 +0200279 old_process = _current_process
Benjamin Petersone711caf2008-06-11 16:44:04 +0000280 _current_process = self
Victor Stinner0f83b152011-06-17 12:31:49 +0200281 try:
282 util._finalizer_registry.clear()
283 util._run_after_forkers()
284 finally:
285 # delay finalization of the old process object until after
286 # _run_after_forkers() is executed
287 del old_process
Benjamin Petersone711caf2008-06-11 16:44:04 +0000288 util.info('child process calling self.run()')
289 try:
290 self.run()
291 exitcode = 0
292 finally:
293 util._exit_function()
294 except SystemExit as e:
295 if not e.args:
296 exitcode = 1
Richard Oudkerk29471de2012-06-06 19:04:57 +0100297 elif isinstance(e.args[0], int):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000298 exitcode = e.args[0]
299 else:
Richard Oudkerk29471de2012-06-06 19:04:57 +0100300 sys.stderr.write(str(e.args[0]) + '\n')
Richard Oudkerk8731d7b2013-11-17 17:24:11 +0000301 exitcode = 1
Benjamin Petersone711caf2008-06-11 16:44:04 +0000302 except:
303 exitcode = 1
304 import traceback
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000305 sys.stderr.write('Process %s:\n' % self.name)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000306 traceback.print_exc()
Antoine Pitrou84a0fbf2012-01-27 10:52:37 +0100307 finally:
308 util.info('process exiting with exitcode %d' % exitcode)
309 sys.stdout.flush()
310 sys.stderr.flush()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000311
Benjamin Petersone711caf2008-06-11 16:44:04 +0000312 return exitcode
313
314#
315# We subclass bytes to avoid accidental transmission of auth keys over network
316#
317
318class AuthenticationString(bytes):
319 def __reduce__(self):
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100320 from .context import get_spawning_popen
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100321 if get_spawning_popen() is None:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000322 raise TypeError(
323 'Pickling an AuthenticationString object is '
324 'disallowed for security reasons'
325 )
326 return AuthenticationString, (bytes(self),)
327
328#
329# Create object representing the main process
330#
331
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100332class _MainProcess(BaseProcess):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000333
334 def __init__(self):
335 self._identity = ()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000336 self._name = 'MainProcess'
337 self._parent_pid = None
338 self._popen = None
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200339 self._closed = False
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100340 self._config = {'authkey': AuthenticationString(os.urandom(32)),
Richard Oudkerke9436972013-11-02 17:05:07 +0000341 'semprefix': '/mp'}
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100342 # Note that some versions of FreeBSD only allow named
Richard Oudkerke9436972013-11-02 17:05:07 +0000343 # semaphores to have names of up to 14 characters. Therefore
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100344 # we choose a short prefix.
Richard Oudkerke9436972013-11-02 17:05:07 +0000345 #
346 # On MacOSX in a sandbox it may be necessary to use a
347 # different prefix -- see #19478.
348 #
349 # Everything in self._config will be inherited by descendant
350 # processes.
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100351
Antoine Pitrou13e96cc2017-06-24 19:22:23 +0200352 def close(self):
353 pass
354
Benjamin Petersone711caf2008-06-11 16:44:04 +0000355
356_current_process = _MainProcess()
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100357_process_counter = itertools.count(1)
358_children = set()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000359del _MainProcess
360
361#
362# Give names to some return codes
363#
364
365_exitcode_to_name = {}
366
367for name, signum in list(signal.__dict__.items()):
368 if name[:3]=='SIG' and '_' not in name:
369 _exitcode_to_name[-signum] = name
Antoine Pitrouc081c0c2011-07-15 22:12:24 +0200370
371# For debug and leak testing
372_dangling = WeakSet()