blob: 47746cb179d1e2cf86beda8ebdd7d6d0d88d8b06 [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
58from pickle import _Pickler as Pickler
59class ForkingPickler(Pickler):
60 dispatch = Pickler.dispatch.copy()
61 @classmethod
62 def register(cls, type, reduce):
63 def dispatcher(self, obj):
64 rv = reduce(obj)
65 if isinstance(rv, str):
66 self.save_global(obj, rv)
67 else:
68 self.save_reduce(obj=obj, *rv)
69 cls.dispatch[type] = dispatcher
70
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()
132 sys.stdout.flush()
133 sys.stderr.flush()
134 os._exit(code)
135
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200136 # `w` will be closed when the child exits, at which point `r`
137 # will become ready for reading (using e.g. select()).
138 os.close(w)
139 util.Finalize(self, os.close, (r,))
140
Benjamin Petersone711caf2008-06-11 16:44:04 +0000141 def poll(self, flag=os.WNOHANG):
142 if self.returncode is None:
Florent Xicluna998171f2010-03-08 13:32:17 +0000143 try:
144 pid, sts = os.waitpid(self.pid, flag)
145 except os.error:
146 # Child process not yet created. See #1731717
147 # e.errno == errno.ECHILD == 10
148 return None
Benjamin Petersone711caf2008-06-11 16:44:04 +0000149 if pid == self.pid:
150 if os.WIFSIGNALED(sts):
151 self.returncode = -os.WTERMSIG(sts)
152 else:
153 assert os.WIFEXITED(sts)
154 self.returncode = os.WEXITSTATUS(sts)
155 return self.returncode
156
157 def wait(self, timeout=None):
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200158 if self.returncode is None:
159 if timeout is not None:
160 r = _select([self.sentinel], [], [], timeout)[0]
161 if not r:
162 return None
163 # This shouldn't block if select() returned successfully.
164 return self.poll(os.WNOHANG if timeout == 0.0 else 0)
165 return self.returncode
Benjamin Petersone711caf2008-06-11 16:44:04 +0000166
167 def terminate(self):
168 if self.returncode is None:
169 try:
170 os.kill(self.pid, signal.SIGTERM)
Florent Xicluna04842a82011-11-11 20:05:50 +0100171 except OSError:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000172 if self.wait(timeout=0.1) is None:
173 raise
174
175 @staticmethod
176 def thread_is_spawning():
177 return False
178
179#
180# Windows
181#
182
183else:
184 import _thread
185 import msvcrt
186 import _subprocess
Benjamin Petersone711caf2008-06-11 16:44:04 +0000187
Florent Xicluna04842a82011-11-11 20:05:50 +0100188 from pickle import load, HIGHEST_PROTOCOL
Antoine Pitrou87cf2202011-05-09 17:04:27 +0200189 from _multiprocessing import win32
Benjamin Petersone711caf2008-06-11 16:44:04 +0000190
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +0000191 def dump(obj, file, protocol=None):
192 ForkingPickler(file, protocol).dump(obj)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000193
194 #
195 #
196 #
197
198 TERMINATE = 0x10000
199 WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False))
brian.curtine2f29982011-04-11 17:56:23 -0500200 WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
Benjamin Petersone711caf2008-06-11 16:44:04 +0000201
202 exit = win32.ExitProcess
203 close = win32.CloseHandle
204
205 #
206 # _python_exe is the assumed path to the python executable.
207 # People embedding Python want to modify it.
208 #
209
brian.curtine2f29982011-04-11 17:56:23 -0500210 if WINSERVICE:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000211 _python_exe = os.path.join(sys.exec_prefix, 'python.exe')
212 else:
213 _python_exe = sys.executable
214
215 def set_executable(exe):
216 global _python_exe
217 _python_exe = exe
218
219 #
220 #
221 #
222
223 def duplicate(handle, target_process=None, inheritable=False):
224 if target_process is None:
225 target_process = _subprocess.GetCurrentProcess()
226 return _subprocess.DuplicateHandle(
227 _subprocess.GetCurrentProcess(), handle, target_process,
228 0, inheritable, _subprocess.DUPLICATE_SAME_ACCESS
229 ).Detach()
230
231 #
232 # We define a Popen class similar to the one from subprocess, but
233 # whose constructor takes a process object as its argument.
234 #
235
236 class Popen(object):
237 '''
238 Start a subprocess to run the code of a process object
239 '''
240 _tls = _thread._local()
241
242 def __init__(self, process_obj):
243 # create pipe for communication with child
244 rfd, wfd = os.pipe()
245
246 # get handle for read end of the pipe and make it inheritable
247 rhandle = duplicate(msvcrt.get_osfhandle(rfd), inheritable=True)
248 os.close(rfd)
249
250 # start process
251 cmd = get_command_line() + [rhandle]
252 cmd = ' '.join('"%s"' % x for x in cmd)
253 hp, ht, pid, tid = _subprocess.CreateProcess(
254 _python_exe, cmd, None, None, 1, 0, None, None, None
255 )
256 ht.Close()
257 close(rhandle)
258
259 # set attributes of self
260 self.pid = pid
261 self.returncode = None
262 self._handle = hp
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200263 self.sentinel = int(hp)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000264
265 # send information to child
266 prep_data = get_preparation_data(process_obj._name)
267 to_child = os.fdopen(wfd, 'wb')
268 Popen._tls.process_handle = int(hp)
269 try:
270 dump(prep_data, to_child, HIGHEST_PROTOCOL)
271 dump(process_obj, to_child, HIGHEST_PROTOCOL)
272 finally:
273 del Popen._tls.process_handle
274 to_child.close()
275
276 @staticmethod
277 def thread_is_spawning():
278 return getattr(Popen._tls, 'process_handle', None) is not None
279
280 @staticmethod
281 def duplicate_for_child(handle):
282 return duplicate(handle, Popen._tls.process_handle)
283
284 def wait(self, timeout=None):
285 if self.returncode is None:
286 if timeout is None:
287 msecs = _subprocess.INFINITE
288 else:
289 msecs = max(0, int(timeout * 1000 + 0.5))
290
291 res = _subprocess.WaitForSingleObject(int(self._handle), msecs)
292 if res == _subprocess.WAIT_OBJECT_0:
293 code = _subprocess.GetExitCodeProcess(self._handle)
294 if code == TERMINATE:
295 code = -signal.SIGTERM
296 self.returncode = code
297
298 return self.returncode
299
300 def poll(self):
301 return self.wait(timeout=0)
302
303 def terminate(self):
304 if self.returncode is None:
305 try:
306 _subprocess.TerminateProcess(int(self._handle), TERMINATE)
307 except WindowsError:
308 if self.wait(timeout=0.1) is None:
309 raise
310
311 #
312 #
313 #
314
315 def is_forking(argv):
316 '''
317 Return whether commandline indicates we are forking
318 '''
319 if len(argv) >= 2 and argv[1] == '--multiprocessing-fork':
320 assert len(argv) == 3
321 return True
322 else:
323 return False
324
325
326 def freeze_support():
327 '''
328 Run code for process object if this in not the main process
329 '''
330 if is_forking(sys.argv):
331 main()
332 sys.exit()
333
334
335 def get_command_line():
336 '''
337 Returns prefix of command line used for spawning a child process
338 '''
339 if process.current_process()._identity==() and is_forking(sys.argv):
340 raise RuntimeError('''
341 Attempt to start a new process before the current process
342 has finished its bootstrapping phase.
343
344 This probably means that you are on Windows and you have
345 forgotten to use the proper idiom in the main module:
346
347 if __name__ == '__main__':
348 freeze_support()
349 ...
350
351 The "freeze_support()" line can be omitted if the program
352 is not going to be frozen to produce a Windows executable.''')
353
354 if getattr(sys, 'frozen', False):
355 return [sys.executable, '--multiprocessing-fork']
356 else:
357 prog = 'from multiprocessing.forking import main; main()'
358 return [_python_exe, '-c', prog, '--multiprocessing-fork']
359
360
361 def main():
362 '''
363 Run code specifed by data received over pipe
364 '''
365 assert is_forking(sys.argv)
366
367 handle = int(sys.argv[-1])
368 fd = msvcrt.open_osfhandle(handle, os.O_RDONLY)
369 from_parent = os.fdopen(fd, 'rb')
370
371 process.current_process()._inheriting = True
372 preparation_data = load(from_parent)
373 prepare(preparation_data)
374 self = load(from_parent)
375 process.current_process()._inheriting = False
376
377 from_parent.close()
378
379 exitcode = self._bootstrap()
380 exit(exitcode)
381
382
383 def get_preparation_data(name):
384 '''
385 Return info about parent needed by child to unpickle process object
386 '''
387 from .util import _logger, _log_to_stderr
388
389 d = dict(
390 name=name,
391 sys_path=sys.path,
392 sys_argv=sys.argv,
393 log_to_stderr=_log_to_stderr,
394 orig_dir=process.ORIGINAL_DIR,
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000395 authkey=process.current_process().authkey,
Benjamin Petersone711caf2008-06-11 16:44:04 +0000396 )
397
398 if _logger is not None:
399 d['log_level'] = _logger.getEffectiveLevel()
400
brian.curtine2f29982011-04-11 17:56:23 -0500401 if not WINEXE and not WINSERVICE:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000402 main_path = getattr(sys.modules['__main__'], '__file__', None)
403 if not main_path and sys.argv[0] not in ('', '-c'):
404 main_path = sys.argv[0]
405 if main_path is not None:
406 if not os.path.isabs(main_path) and \
407 process.ORIGINAL_DIR is not None:
408 main_path = os.path.join(process.ORIGINAL_DIR, main_path)
409 d['main_path'] = os.path.normpath(main_path)
410
411 return d
412
413 #
414 # Make (Pipe)Connection picklable
415 #
416
Antoine Pitrou87cf2202011-05-09 17:04:27 +0200417 # Late import because of circular import
418 from .connection import Connection, PipeConnection
419
Benjamin Petersone711caf2008-06-11 16:44:04 +0000420 def reduce_connection(conn):
421 if not Popen.thread_is_spawning():
422 raise RuntimeError(
423 'By default %s objects can only be shared between processes\n'
424 'using inheritance' % type(conn).__name__
425 )
426 return type(conn), (Popen.duplicate_for_child(conn.fileno()),
427 conn.readable, conn.writable)
428
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +0000429 ForkingPickler.register(Connection, reduce_connection)
430 ForkingPickler.register(PipeConnection, reduce_connection)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000431
432#
433# Prepare current process
434#
435
436old_main_modules = []
437
438def prepare(data):
439 '''
440 Try to get current process ready to unpickle process object
441 '''
442 old_main_modules.append(sys.modules['__main__'])
443
444 if 'name' in data:
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000445 process.current_process().name = data['name']
Benjamin Petersone711caf2008-06-11 16:44:04 +0000446
447 if 'authkey' in data:
448 process.current_process()._authkey = data['authkey']
449
450 if 'log_to_stderr' in data and data['log_to_stderr']:
451 util.log_to_stderr()
452
453 if 'log_level' in data:
454 util.get_logger().setLevel(data['log_level'])
455
456 if 'sys_path' in data:
457 sys.path = data['sys_path']
458
459 if 'sys_argv' in data:
460 sys.argv = data['sys_argv']
461
462 if 'dir' in data:
463 os.chdir(data['dir'])
464
465 if 'orig_dir' in data:
466 process.ORIGINAL_DIR = data['orig_dir']
467
468 if 'main_path' in data:
Nick Coghlan793ee1f2011-01-30 01:24:08 +0000469 # XXX (ncoghlan): The following code makes several bogus
470 # assumptions regarding the relationship between __file__
471 # and a module's real name. See PEP 302 and issue #10845
Benjamin Petersone711caf2008-06-11 16:44:04 +0000472 main_path = data['main_path']
473 main_name = os.path.splitext(os.path.basename(main_path))[0]
474 if main_name == '__init__':
475 main_name = os.path.basename(os.path.dirname(main_path))
476
Nick Coghlan793ee1f2011-01-30 01:24:08 +0000477 if main_name == '__main__':
478 main_module = sys.modules['__main__']
479 main_module.__file__ = main_path
480 elif main_name != 'ipython':
481 # Main modules not actually called __main__.py may
482 # contain additional code that should still be executed
Benjamin Petersone711caf2008-06-11 16:44:04 +0000483 import imp
484
485 if main_path is None:
486 dirs = None
487 elif os.path.basename(main_path).startswith('__init__.py'):
488 dirs = [os.path.dirname(os.path.dirname(main_path))]
489 else:
490 dirs = [os.path.dirname(main_path)]
491
492 assert main_name not in sys.modules, main_name
493 file, path_name, etc = imp.find_module(main_name, dirs)
494 try:
495 # We would like to do "imp.load_module('__main__', ...)"
496 # here. However, that would cause 'if __name__ ==
497 # "__main__"' clauses to be executed.
498 main_module = imp.load_module(
499 '__parents_main__', file, path_name, etc
500 )
501 finally:
502 if file:
503 file.close()
504
505 sys.modules['__main__'] = main_module
506 main_module.__name__ = '__main__'
507
508 # Try to make the potentially picklable objects in
509 # sys.modules['__main__'] realize they are in the main
510 # module -- somewhat ugly.
511 for obj in list(main_module.__dict__.values()):
512 try:
513 if obj.__module__ == '__parents_main__':
514 obj.__module__ = '__main__'
515 except Exception:
516 pass