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