blob: b7de5679b1b2f90d56ebc805d489c12d9493c720 [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()
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
184 import _subprocess
Benjamin Petersone711caf2008-06-11 16:44:04 +0000185
Florent Xicluna04842a82011-11-11 20:05:50 +0100186 from pickle import load, HIGHEST_PROTOCOL
Antoine Pitrou87cf2202011-05-09 17:04:27 +0200187 from _multiprocessing import win32
Benjamin Petersone711caf2008-06-11 16:44:04 +0000188
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +0000189 def dump(obj, file, protocol=None):
190 ForkingPickler(file, protocol).dump(obj)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000191
192 #
193 #
194 #
195
196 TERMINATE = 0x10000
197 WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False))
brian.curtine2f29982011-04-11 17:56:23 -0500198 WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
Benjamin Petersone711caf2008-06-11 16:44:04 +0000199
200 exit = win32.ExitProcess
201 close = win32.CloseHandle
202
203 #
204 # _python_exe is the assumed path to the python executable.
205 # People embedding Python want to modify it.
206 #
207
brian.curtine2f29982011-04-11 17:56:23 -0500208 if WINSERVICE:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000209 _python_exe = os.path.join(sys.exec_prefix, 'python.exe')
210 else:
211 _python_exe = sys.executable
212
213 def set_executable(exe):
214 global _python_exe
215 _python_exe = exe
216
217 #
218 #
219 #
220
221 def duplicate(handle, target_process=None, inheritable=False):
222 if target_process is None:
223 target_process = _subprocess.GetCurrentProcess()
224 return _subprocess.DuplicateHandle(
225 _subprocess.GetCurrentProcess(), handle, target_process,
226 0, inheritable, _subprocess.DUPLICATE_SAME_ACCESS
227 ).Detach()
228
229 #
230 # We define a Popen class similar to the one from subprocess, but
231 # whose constructor takes a process object as its argument.
232 #
233
234 class Popen(object):
235 '''
236 Start a subprocess to run the code of a process object
237 '''
238 _tls = _thread._local()
239
240 def __init__(self, process_obj):
241 # create pipe for communication with child
242 rfd, wfd = os.pipe()
243
244 # get handle for read end of the pipe and make it inheritable
245 rhandle = duplicate(msvcrt.get_osfhandle(rfd), inheritable=True)
246 os.close(rfd)
247
248 # start process
249 cmd = get_command_line() + [rhandle]
250 cmd = ' '.join('"%s"' % x for x in cmd)
251 hp, ht, pid, tid = _subprocess.CreateProcess(
252 _python_exe, cmd, None, None, 1, 0, None, None, None
253 )
254 ht.Close()
255 close(rhandle)
256
257 # set attributes of self
258 self.pid = pid
259 self.returncode = None
260 self._handle = hp
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200261 self.sentinel = int(hp)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000262
263 # send information to child
264 prep_data = get_preparation_data(process_obj._name)
265 to_child = os.fdopen(wfd, 'wb')
266 Popen._tls.process_handle = int(hp)
267 try:
268 dump(prep_data, to_child, HIGHEST_PROTOCOL)
269 dump(process_obj, to_child, HIGHEST_PROTOCOL)
270 finally:
271 del Popen._tls.process_handle
272 to_child.close()
273
274 @staticmethod
275 def thread_is_spawning():
276 return getattr(Popen._tls, 'process_handle', None) is not None
277
278 @staticmethod
279 def duplicate_for_child(handle):
280 return duplicate(handle, Popen._tls.process_handle)
281
282 def wait(self, timeout=None):
283 if self.returncode is None:
284 if timeout is None:
285 msecs = _subprocess.INFINITE
286 else:
287 msecs = max(0, int(timeout * 1000 + 0.5))
288
289 res = _subprocess.WaitForSingleObject(int(self._handle), msecs)
290 if res == _subprocess.WAIT_OBJECT_0:
291 code = _subprocess.GetExitCodeProcess(self._handle)
292 if code == TERMINATE:
293 code = -signal.SIGTERM
294 self.returncode = code
295
296 return self.returncode
297
298 def poll(self):
299 return self.wait(timeout=0)
300
301 def terminate(self):
302 if self.returncode is None:
303 try:
304 _subprocess.TerminateProcess(int(self._handle), TERMINATE)
305 except WindowsError:
306 if self.wait(timeout=0.1) is None:
307 raise
308
309 #
310 #
311 #
312
313 def is_forking(argv):
314 '''
315 Return whether commandline indicates we are forking
316 '''
317 if len(argv) >= 2 and argv[1] == '--multiprocessing-fork':
318 assert len(argv) == 3
319 return True
320 else:
321 return False
322
323
324 def freeze_support():
325 '''
326 Run code for process object if this in not the main process
327 '''
328 if is_forking(sys.argv):
329 main()
330 sys.exit()
331
332
333 def get_command_line():
334 '''
335 Returns prefix of command line used for spawning a child process
336 '''
337 if process.current_process()._identity==() and is_forking(sys.argv):
338 raise RuntimeError('''
339 Attempt to start a new process before the current process
340 has finished its bootstrapping phase.
341
342 This probably means that you are on Windows and you have
343 forgotten to use the proper idiom in the main module:
344
345 if __name__ == '__main__':
346 freeze_support()
347 ...
348
349 The "freeze_support()" line can be omitted if the program
350 is not going to be frozen to produce a Windows executable.''')
351
352 if getattr(sys, 'frozen', False):
353 return [sys.executable, '--multiprocessing-fork']
354 else:
355 prog = 'from multiprocessing.forking import main; main()'
356 return [_python_exe, '-c', prog, '--multiprocessing-fork']
357
358
359 def main():
360 '''
361 Run code specifed by data received over pipe
362 '''
363 assert is_forking(sys.argv)
364
365 handle = int(sys.argv[-1])
366 fd = msvcrt.open_osfhandle(handle, os.O_RDONLY)
367 from_parent = os.fdopen(fd, 'rb')
368
369 process.current_process()._inheriting = True
370 preparation_data = load(from_parent)
371 prepare(preparation_data)
372 self = load(from_parent)
373 process.current_process()._inheriting = False
374
375 from_parent.close()
376
377 exitcode = self._bootstrap()
378 exit(exitcode)
379
380
381 def get_preparation_data(name):
382 '''
383 Return info about parent needed by child to unpickle process object
384 '''
385 from .util import _logger, _log_to_stderr
386
387 d = dict(
388 name=name,
389 sys_path=sys.path,
390 sys_argv=sys.argv,
391 log_to_stderr=_log_to_stderr,
392 orig_dir=process.ORIGINAL_DIR,
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000393 authkey=process.current_process().authkey,
Benjamin Petersone711caf2008-06-11 16:44:04 +0000394 )
395
396 if _logger is not None:
397 d['log_level'] = _logger.getEffectiveLevel()
398
brian.curtine2f29982011-04-11 17:56:23 -0500399 if not WINEXE and not WINSERVICE:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000400 main_path = getattr(sys.modules['__main__'], '__file__', None)
401 if not main_path and sys.argv[0] not in ('', '-c'):
402 main_path = sys.argv[0]
403 if main_path is not None:
404 if not os.path.isabs(main_path) and \
405 process.ORIGINAL_DIR is not None:
406 main_path = os.path.join(process.ORIGINAL_DIR, main_path)
407 d['main_path'] = os.path.normpath(main_path)
408
409 return d
410
411 #
412 # Make (Pipe)Connection picklable
413 #
414
Antoine Pitrou87cf2202011-05-09 17:04:27 +0200415 # Late import because of circular import
416 from .connection import Connection, PipeConnection
417
Benjamin Petersone711caf2008-06-11 16:44:04 +0000418 def reduce_connection(conn):
419 if not Popen.thread_is_spawning():
420 raise RuntimeError(
421 'By default %s objects can only be shared between processes\n'
422 'using inheritance' % type(conn).__name__
423 )
424 return type(conn), (Popen.duplicate_for_child(conn.fileno()),
425 conn.readable, conn.writable)
426
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +0000427 ForkingPickler.register(Connection, reduce_connection)
428 ForkingPickler.register(PipeConnection, reduce_connection)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000429
430#
431# Prepare current process
432#
433
434old_main_modules = []
435
436def prepare(data):
437 '''
438 Try to get current process ready to unpickle process object
439 '''
440 old_main_modules.append(sys.modules['__main__'])
441
442 if 'name' in data:
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000443 process.current_process().name = data['name']
Benjamin Petersone711caf2008-06-11 16:44:04 +0000444
445 if 'authkey' in data:
446 process.current_process()._authkey = data['authkey']
447
448 if 'log_to_stderr' in data and data['log_to_stderr']:
449 util.log_to_stderr()
450
451 if 'log_level' in data:
452 util.get_logger().setLevel(data['log_level'])
453
454 if 'sys_path' in data:
455 sys.path = data['sys_path']
456
457 if 'sys_argv' in data:
458 sys.argv = data['sys_argv']
459
460 if 'dir' in data:
461 os.chdir(data['dir'])
462
463 if 'orig_dir' in data:
464 process.ORIGINAL_DIR = data['orig_dir']
465
466 if 'main_path' in data:
Nick Coghlan793ee1f2011-01-30 01:24:08 +0000467 # XXX (ncoghlan): The following code makes several bogus
468 # assumptions regarding the relationship between __file__
469 # and a module's real name. See PEP 302 and issue #10845
Benjamin Petersone711caf2008-06-11 16:44:04 +0000470 main_path = data['main_path']
471 main_name = os.path.splitext(os.path.basename(main_path))[0]
472 if main_name == '__init__':
473 main_name = os.path.basename(os.path.dirname(main_path))
474
Nick Coghlan793ee1f2011-01-30 01:24:08 +0000475 if main_name == '__main__':
476 main_module = sys.modules['__main__']
477 main_module.__file__ = main_path
478 elif main_name != 'ipython':
479 # Main modules not actually called __main__.py may
480 # contain additional code that should still be executed
Benjamin Petersone711caf2008-06-11 16:44:04 +0000481 import imp
482
483 if main_path is None:
484 dirs = None
485 elif os.path.basename(main_path).startswith('__init__.py'):
486 dirs = [os.path.dirname(os.path.dirname(main_path))]
487 else:
488 dirs = [os.path.dirname(main_path)]
489
490 assert main_name not in sys.modules, main_name
491 file, path_name, etc = imp.find_module(main_name, dirs)
492 try:
493 # We would like to do "imp.load_module('__main__', ...)"
494 # here. However, that would cause 'if __name__ ==
495 # "__main__"' clauses to be executed.
496 main_module = imp.load_module(
497 '__parents_main__', file, path_name, etc
498 )
499 finally:
500 if file:
501 file.close()
502
503 sys.modules['__main__'] = main_module
504 main_module.__name__ = '__main__'
505
506 # Try to make the potentially picklable objects in
507 # sys.modules['__main__'] realize they are in the main
508 # module -- somewhat ugly.
509 for obj in list(main_module.__dict__.values()):
510 try:
511 if obj.__module__ == '__parents_main__':
512 obj.__module__ = '__main__'
513 except Exception:
514 pass