blob: 68959bf9f435a90f77e85b91a6fc6c8b0d09420c [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()
Amaury Forgeot d'Arc768008c2008-08-20 09:04:46 +0000237 if sys.stdin is not None:
238 try:
Alexandre Vassalottic57a84f2009-07-17 12:07:01 +0000239 sys.stdin.close()
240 sys.stdin = open(os.devnull)
Amaury Forgeot d'Arc768008c2008-08-20 09:04:46 +0000241 except (OSError, ValueError):
242 pass
Victor Stinner0f83b152011-06-17 12:31:49 +0200243 old_process = _current_process
Benjamin Petersone711caf2008-06-11 16:44:04 +0000244 _current_process = self
Victor Stinner0f83b152011-06-17 12:31:49 +0200245 try:
246 util._finalizer_registry.clear()
247 util._run_after_forkers()
248 finally:
249 # delay finalization of the old process object until after
250 # _run_after_forkers() is executed
251 del old_process
Benjamin Petersone711caf2008-06-11 16:44:04 +0000252 util.info('child process calling self.run()')
253 try:
254 self.run()
255 exitcode = 0
256 finally:
257 util._exit_function()
258 except SystemExit as e:
259 if not e.args:
260 exitcode = 1
Richard Oudkerk29471de2012-06-06 19:04:57 +0100261 elif isinstance(e.args[0], int):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000262 exitcode = e.args[0]
263 else:
Richard Oudkerk29471de2012-06-06 19:04:57 +0100264 sys.stderr.write(str(e.args[0]) + '\n')
Richard Oudkerk8731d7b2013-11-17 17:24:11 +0000265 exitcode = 1
Benjamin Petersone711caf2008-06-11 16:44:04 +0000266 except:
267 exitcode = 1
268 import traceback
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000269 sys.stderr.write('Process %s:\n' % self.name)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000270 traceback.print_exc()
Antoine Pitrou84a0fbf2012-01-27 10:52:37 +0100271 finally:
272 util.info('process exiting with exitcode %d' % exitcode)
273 sys.stdout.flush()
274 sys.stderr.flush()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000275
Benjamin Petersone711caf2008-06-11 16:44:04 +0000276 return exitcode
277
278#
279# We subclass bytes to avoid accidental transmission of auth keys over network
280#
281
282class AuthenticationString(bytes):
283 def __reduce__(self):
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100284 from .context import get_spawning_popen
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100285 if get_spawning_popen() is None:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000286 raise TypeError(
287 'Pickling an AuthenticationString object is '
288 'disallowed for security reasons'
289 )
290 return AuthenticationString, (bytes(self),)
291
292#
293# Create object representing the main process
294#
295
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100296class _MainProcess(BaseProcess):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000297
298 def __init__(self):
299 self._identity = ()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000300 self._name = 'MainProcess'
301 self._parent_pid = None
302 self._popen = None
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100303 self._config = {'authkey': AuthenticationString(os.urandom(32)),
Richard Oudkerke9436972013-11-02 17:05:07 +0000304 'semprefix': '/mp'}
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100305 # Note that some versions of FreeBSD only allow named
Richard Oudkerke9436972013-11-02 17:05:07 +0000306 # semaphores to have names of up to 14 characters. Therefore
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100307 # we choose a short prefix.
Richard Oudkerke9436972013-11-02 17:05:07 +0000308 #
309 # On MacOSX in a sandbox it may be necessary to use a
310 # different prefix -- see #19478.
311 #
312 # Everything in self._config will be inherited by descendant
313 # processes.
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100314
Benjamin Petersone711caf2008-06-11 16:44:04 +0000315
316_current_process = _MainProcess()
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100317_process_counter = itertools.count(1)
318_children = set()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000319del _MainProcess
320
321#
322# Give names to some return codes
323#
324
325_exitcode_to_name = {}
326
327for name, signum in list(signal.__dict__.items()):
328 if name[:3]=='SIG' and '_' not in name:
329 _exitcode_to_name[-signum] = name
Antoine Pitrouc081c0c2011-07-15 22:12:24 +0200330
331# For debug and leak testing
332_dangling = WeakSet()