blob: 15fdb0e08b138b7ba3cb7872d271735a5f26a6ff [file] [log] [blame]
Benjamin Petersone711caf2008-06-11 16:44:04 +00001#
2# Module for starting a process object using os.fork() or CreateProcess()
3#
4# multiprocessing/forking.py
5#
R. David Murray3fc969a2010-12-14 01:38:16 +00006# Copyright (c) 2006-2008, R Oudkerk
7# All rights reserved.
8#
9# Redistribution and use in source and binary forms, with or without
10# modification, are permitted provided that the following conditions
11# are met:
12#
13# 1. Redistributions of source code must retain the above copyright
14# notice, this list of conditions and the following disclaimer.
15# 2. Redistributions in binary form must reproduce the above copyright
16# notice, this list of conditions and the following disclaimer in the
17# documentation and/or other materials provided with the distribution.
18# 3. Neither the name of author nor the names of any contributors may be
19# used to endorse or promote products derived from this software
20# without specific prior written permission.
21#
22# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
23# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32# SUCH DAMAGE.
Benjamin Petersone711caf2008-06-11 16:44:04 +000033#
34
35import os
36import sys
37import signal
38
39from multiprocessing import util, process
40
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +000041__all__ = ['Popen', 'assert_spawning', 'exit', 'duplicate', 'close', 'ForkingPickler']
Benjamin Petersone711caf2008-06-11 16:44:04 +000042
43#
44# Check that the current thread is spawning a child process
45#
46
47def assert_spawning(self):
48 if not Popen.thread_is_spawning():
49 raise RuntimeError(
50 '%s objects should only be shared between processes'
51 ' through inheritance' % type(self).__name__
52 )
53
54#
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +000055# Try making some callable types picklable
56#
57
Antoine Pitrou846fd302012-03-17 00:23:04 +010058from pickle import Pickler
59from copyreg import dispatch_table
60
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +000061class ForkingPickler(Pickler):
Antoine Pitrou846fd302012-03-17 00:23:04 +010062 _extra_reducers = {}
63 def __init__(self, *args):
64 Pickler.__init__(self, *args)
65 self.dispatch_table = dispatch_table.copy()
66 self.dispatch_table.update(self._extra_reducers)
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +000067 @classmethod
68 def register(cls, type, reduce):
Antoine Pitrou846fd302012-03-17 00:23:04 +010069 cls._extra_reducers[type] = reduce
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +000070
71def _reduce_method(m):
72 if m.__self__ is None:
73 return getattr, (m.__class__, m.__func__.__name__)
74 else:
75 return getattr, (m.__self__, m.__func__.__name__)
76class _C:
77 def f(self):
78 pass
79ForkingPickler.register(type(_C().f), _reduce_method)
80
81
82def _reduce_method_descriptor(m):
83 return getattr, (m.__objclass__, m.__name__)
84ForkingPickler.register(type(list.append), _reduce_method_descriptor)
85ForkingPickler.register(type(int.__add__), _reduce_method_descriptor)
86
87try:
88 from functools import partial
89except ImportError:
90 pass
91else:
92 def _reduce_partial(p):
93 return _rebuild_partial, (p.func, p.args, p.keywords or {})
94 def _rebuild_partial(func, args, keywords):
95 return partial(func, *args, **keywords)
96 ForkingPickler.register(partial, _reduce_partial)
97
98#
Benjamin Petersone711caf2008-06-11 16:44:04 +000099# Unix
100#
101
102if sys.platform != 'win32':
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200103 import select
Benjamin Petersone711caf2008-06-11 16:44:04 +0000104
105 exit = os._exit
106 duplicate = os.dup
107 close = os.close
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200108 _select = util._eintr_retry(select.select)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000109
110 #
111 # We define a Popen class similar to the one from subprocess, but
112 # whose constructor takes a process object as its argument.
113 #
114
115 class Popen(object):
116
117 def __init__(self, process_obj):
118 sys.stdout.flush()
119 sys.stderr.flush()
120 self.returncode = None
121
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200122 r, w = os.pipe()
123 self.sentinel = r
124
Benjamin Petersone711caf2008-06-11 16:44:04 +0000125 self.pid = os.fork()
126 if self.pid == 0:
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200127 os.close(r)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000128 if 'random' in sys.modules:
129 import random
130 random.seed()
131 code = process_obj._bootstrap()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000132 os._exit(code)
133
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200134 # `w` will be closed when the child exits, at which point `r`
135 # will become ready for reading (using e.g. select()).
136 os.close(w)
137 util.Finalize(self, os.close, (r,))
138
Benjamin Petersone711caf2008-06-11 16:44:04 +0000139 def poll(self, flag=os.WNOHANG):
140 if self.returncode is None:
Florent Xicluna998171f2010-03-08 13:32:17 +0000141 try:
142 pid, sts = os.waitpid(self.pid, flag)
143 except os.error:
144 # Child process not yet created. See #1731717
145 # e.errno == errno.ECHILD == 10
146 return None
Benjamin Petersone711caf2008-06-11 16:44:04 +0000147 if pid == self.pid:
148 if os.WIFSIGNALED(sts):
149 self.returncode = -os.WTERMSIG(sts)
150 else:
151 assert os.WIFEXITED(sts)
152 self.returncode = os.WEXITSTATUS(sts)
153 return self.returncode
154
155 def wait(self, timeout=None):
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200156 if self.returncode is None:
157 if timeout is not None:
158 r = _select([self.sentinel], [], [], timeout)[0]
159 if not r:
160 return None
161 # This shouldn't block if select() returned successfully.
162 return self.poll(os.WNOHANG if timeout == 0.0 else 0)
163 return self.returncode
Benjamin Petersone711caf2008-06-11 16:44:04 +0000164
165 def terminate(self):
166 if self.returncode is None:
167 try:
168 os.kill(self.pid, signal.SIGTERM)
Florent Xicluna04842a82011-11-11 20:05:50 +0100169 except OSError:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000170 if self.wait(timeout=0.1) is None:
171 raise
172
173 @staticmethod
174 def thread_is_spawning():
175 return False
176
177#
178# Windows
179#
180
181else:
182 import _thread
183 import msvcrt
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200184 import _winapi
Benjamin Petersone711caf2008-06-11 16:44:04 +0000185
Florent Xicluna04842a82011-11-11 20:05:50 +0100186 from pickle import load, HIGHEST_PROTOCOL
Benjamin Petersone711caf2008-06-11 16:44:04 +0000187
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +0000188 def dump(obj, file, protocol=None):
189 ForkingPickler(file, protocol).dump(obj)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000190
191 #
192 #
193 #
194
195 TERMINATE = 0x10000
196 WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False))
brian.curtine2f29982011-04-11 17:56:23 -0500197 WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
Benjamin Petersone711caf2008-06-11 16:44:04 +0000198
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200199 exit = _winapi.ExitProcess
200 close = _winapi.CloseHandle
Benjamin Petersone711caf2008-06-11 16:44:04 +0000201
202 #
203 # _python_exe is the assumed path to the python executable.
204 # People embedding Python want to modify it.
205 #
206
brian.curtine2f29982011-04-11 17:56:23 -0500207 if WINSERVICE:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000208 _python_exe = os.path.join(sys.exec_prefix, 'python.exe')
209 else:
210 _python_exe = sys.executable
211
212 def set_executable(exe):
213 global _python_exe
214 _python_exe = exe
215
216 #
217 #
218 #
219
220 def duplicate(handle, target_process=None, inheritable=False):
221 if target_process is None:
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200222 target_process = _winapi.GetCurrentProcess()
223 return _winapi.DuplicateHandle(
224 _winapi.GetCurrentProcess(), handle, target_process,
225 0, inheritable, _winapi.DUPLICATE_SAME_ACCESS
226 )
Benjamin Petersone711caf2008-06-11 16:44:04 +0000227
228 #
229 # We define a Popen class similar to the one from subprocess, but
230 # whose constructor takes a process object as its argument.
231 #
232
233 class Popen(object):
234 '''
235 Start a subprocess to run the code of a process object
236 '''
237 _tls = _thread._local()
238
239 def __init__(self, process_obj):
240 # create pipe for communication with child
241 rfd, wfd = os.pipe()
242
243 # get handle for read end of the pipe and make it inheritable
244 rhandle = duplicate(msvcrt.get_osfhandle(rfd), inheritable=True)
245 os.close(rfd)
246
247 # start process
248 cmd = get_command_line() + [rhandle]
249 cmd = ' '.join('"%s"' % x for x in cmd)
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200250 hp, ht, pid, tid = _winapi.CreateProcess(
Benjamin Petersone711caf2008-06-11 16:44:04 +0000251 _python_exe, cmd, None, None, 1, 0, None, None, None
252 )
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200253 _winapi.CloseHandle(ht)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000254 close(rhandle)
255
256 # set attributes of self
257 self.pid = pid
258 self.returncode = None
259 self._handle = hp
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200260 self.sentinel = int(hp)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000261
262 # send information to child
263 prep_data = get_preparation_data(process_obj._name)
264 to_child = os.fdopen(wfd, 'wb')
265 Popen._tls.process_handle = int(hp)
266 try:
267 dump(prep_data, to_child, HIGHEST_PROTOCOL)
268 dump(process_obj, to_child, HIGHEST_PROTOCOL)
269 finally:
270 del Popen._tls.process_handle
271 to_child.close()
272
273 @staticmethod
274 def thread_is_spawning():
275 return getattr(Popen._tls, 'process_handle', None) is not None
276
277 @staticmethod
278 def duplicate_for_child(handle):
279 return duplicate(handle, Popen._tls.process_handle)
280
281 def wait(self, timeout=None):
282 if self.returncode is None:
283 if timeout is None:
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200284 msecs = _winapi.INFINITE
Benjamin Petersone711caf2008-06-11 16:44:04 +0000285 else:
286 msecs = max(0, int(timeout * 1000 + 0.5))
287
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200288 res = _winapi.WaitForSingleObject(int(self._handle), msecs)
289 if res == _winapi.WAIT_OBJECT_0:
290 code = _winapi.GetExitCodeProcess(self._handle)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000291 if code == TERMINATE:
292 code = -signal.SIGTERM
293 self.returncode = code
294
295 return self.returncode
296
297 def poll(self):
298 return self.wait(timeout=0)
299
300 def terminate(self):
301 if self.returncode is None:
302 try:
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200303 _winapi.TerminateProcess(int(self._handle), TERMINATE)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000304 except WindowsError:
305 if self.wait(timeout=0.1) is None:
306 raise
307
308 #
309 #
310 #
311
312 def is_forking(argv):
313 '''
314 Return whether commandline indicates we are forking
315 '''
316 if len(argv) >= 2 and argv[1] == '--multiprocessing-fork':
317 assert len(argv) == 3
318 return True
319 else:
320 return False
321
322
323 def freeze_support():
324 '''
325 Run code for process object if this in not the main process
326 '''
327 if is_forking(sys.argv):
328 main()
329 sys.exit()
330
331
332 def get_command_line():
333 '''
334 Returns prefix of command line used for spawning a child process
335 '''
336 if process.current_process()._identity==() and is_forking(sys.argv):
337 raise RuntimeError('''
338 Attempt to start a new process before the current process
339 has finished its bootstrapping phase.
340
341 This probably means that you are on Windows and you have
342 forgotten to use the proper idiom in the main module:
343
344 if __name__ == '__main__':
345 freeze_support()
346 ...
347
348 The "freeze_support()" line can be omitted if the program
349 is not going to be frozen to produce a Windows executable.''')
350
351 if getattr(sys, 'frozen', False):
352 return [sys.executable, '--multiprocessing-fork']
353 else:
354 prog = 'from multiprocessing.forking import main; main()'
355 return [_python_exe, '-c', prog, '--multiprocessing-fork']
356
357
358 def main():
359 '''
360 Run code specifed by data received over pipe
361 '''
362 assert is_forking(sys.argv)
363
364 handle = int(sys.argv[-1])
365 fd = msvcrt.open_osfhandle(handle, os.O_RDONLY)
366 from_parent = os.fdopen(fd, 'rb')
367
368 process.current_process()._inheriting = True
369 preparation_data = load(from_parent)
370 prepare(preparation_data)
371 self = load(from_parent)
372 process.current_process()._inheriting = False
373
374 from_parent.close()
375
376 exitcode = self._bootstrap()
377 exit(exitcode)
378
379
380 def get_preparation_data(name):
381 '''
382 Return info about parent needed by child to unpickle process object
383 '''
384 from .util import _logger, _log_to_stderr
385
386 d = dict(
387 name=name,
388 sys_path=sys.path,
389 sys_argv=sys.argv,
390 log_to_stderr=_log_to_stderr,
391 orig_dir=process.ORIGINAL_DIR,
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000392 authkey=process.current_process().authkey,
Benjamin Petersone711caf2008-06-11 16:44:04 +0000393 )
394
395 if _logger is not None:
396 d['log_level'] = _logger.getEffectiveLevel()
397
brian.curtine2f29982011-04-11 17:56:23 -0500398 if not WINEXE and not WINSERVICE:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000399 main_path = getattr(sys.modules['__main__'], '__file__', None)
400 if not main_path and sys.argv[0] not in ('', '-c'):
401 main_path = sys.argv[0]
402 if main_path is not None:
403 if not os.path.isabs(main_path) and \
404 process.ORIGINAL_DIR is not None:
405 main_path = os.path.join(process.ORIGINAL_DIR, main_path)
406 d['main_path'] = os.path.normpath(main_path)
407
408 return d
409
Benjamin Petersone711caf2008-06-11 16:44:04 +0000410#
411# Prepare current process
412#
413
414old_main_modules = []
415
416def prepare(data):
417 '''
418 Try to get current process ready to unpickle process object
419 '''
420 old_main_modules.append(sys.modules['__main__'])
421
422 if 'name' in data:
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000423 process.current_process().name = data['name']
Benjamin Petersone711caf2008-06-11 16:44:04 +0000424
425 if 'authkey' in data:
426 process.current_process()._authkey = data['authkey']
427
428 if 'log_to_stderr' in data and data['log_to_stderr']:
429 util.log_to_stderr()
430
431 if 'log_level' in data:
432 util.get_logger().setLevel(data['log_level'])
433
434 if 'sys_path' in data:
435 sys.path = data['sys_path']
436
437 if 'sys_argv' in data:
438 sys.argv = data['sys_argv']
439
440 if 'dir' in data:
441 os.chdir(data['dir'])
442
443 if 'orig_dir' in data:
444 process.ORIGINAL_DIR = data['orig_dir']
445
446 if 'main_path' in data:
Nick Coghlan793ee1f2011-01-30 01:24:08 +0000447 # XXX (ncoghlan): The following code makes several bogus
448 # assumptions regarding the relationship between __file__
449 # and a module's real name. See PEP 302 and issue #10845
Benjamin Petersone711caf2008-06-11 16:44:04 +0000450 main_path = data['main_path']
451 main_name = os.path.splitext(os.path.basename(main_path))[0]
452 if main_name == '__init__':
453 main_name = os.path.basename(os.path.dirname(main_path))
454
Nick Coghlan793ee1f2011-01-30 01:24:08 +0000455 if main_name == '__main__':
456 main_module = sys.modules['__main__']
457 main_module.__file__ = main_path
458 elif main_name != 'ipython':
459 # Main modules not actually called __main__.py may
460 # contain additional code that should still be executed
Benjamin Petersone711caf2008-06-11 16:44:04 +0000461 import imp
462
463 if main_path is None:
464 dirs = None
465 elif os.path.basename(main_path).startswith('__init__.py'):
466 dirs = [os.path.dirname(os.path.dirname(main_path))]
467 else:
468 dirs = [os.path.dirname(main_path)]
469
470 assert main_name not in sys.modules, main_name
471 file, path_name, etc = imp.find_module(main_name, dirs)
472 try:
473 # We would like to do "imp.load_module('__main__', ...)"
474 # here. However, that would cause 'if __name__ ==
475 # "__main__"' clauses to be executed.
476 main_module = imp.load_module(
477 '__parents_main__', file, path_name, etc
478 )
479 finally:
480 if file:
481 file.close()
482
483 sys.modules['__main__'] = main_module
484 main_module.__name__ = '__main__'
485
486 # Try to make the potentially picklable objects in
487 # sys.modules['__main__'] realize they are in the main
488 # module -- somewhat ugly.
489 for obj in list(main_module.__dict__.values()):
490 try:
491 if obj.__module__ == '__parents_main__':
492 obj.__module__ = '__main__'
493 except Exception:
494 pass