blob: 37c9a10e7cc97ff2fc21c43c722102cdfe498fbd [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
Richard Oudkerk3e268aa2012-04-30 12:13:55 +01007# Licensed to PSF under a Contributor Agreement.
Benjamin Petersone711caf2008-06-11 16:44:04 +00008#
9
Charles-François Natalia6550752013-03-24 15:21:49 +010010import io
Benjamin Petersone711caf2008-06-11 16:44:04 +000011import os
Charles-François Natalia6550752013-03-24 15:21:49 +010012import pickle
Benjamin Petersone711caf2008-06-11 16:44:04 +000013import sys
14import signal
Richard Oudkerk7aaa1ef2013-02-26 12:39:57 +000015import errno
Benjamin Petersone711caf2008-06-11 16:44:04 +000016
17from multiprocessing import util, process
18
Richard Oudkerk73d9a292012-06-14 15:30:10 +010019__all__ = ['Popen', 'assert_spawning', 'duplicate', 'close', 'ForkingPickler']
Benjamin Petersone711caf2008-06-11 16:44:04 +000020
21#
22# Check that the current thread is spawning a child process
23#
24
25def assert_spawning(self):
26 if not Popen.thread_is_spawning():
27 raise RuntimeError(
28 '%s objects should only be shared between processes'
29 ' through inheritance' % type(self).__name__
30 )
31
32#
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +000033# Try making some callable types picklable
34#
35
Antoine Pitrou846fd302012-03-17 00:23:04 +010036from pickle import Pickler
37from copyreg import dispatch_table
38
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +000039class ForkingPickler(Pickler):
Antoine Pitrou846fd302012-03-17 00:23:04 +010040 _extra_reducers = {}
41 def __init__(self, *args):
42 Pickler.__init__(self, *args)
43 self.dispatch_table = dispatch_table.copy()
44 self.dispatch_table.update(self._extra_reducers)
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +000045 @classmethod
46 def register(cls, type, reduce):
Antoine Pitrou846fd302012-03-17 00:23:04 +010047 cls._extra_reducers[type] = reduce
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +000048
Charles-François Natalia6550752013-03-24 15:21:49 +010049 @staticmethod
50 def dumps(obj):
51 buf = io.BytesIO()
52 ForkingPickler(buf, pickle.HIGHEST_PROTOCOL).dump(obj)
53 return buf.getbuffer()
54
55 loads = pickle.loads
56
57
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +000058def _reduce_method(m):
59 if m.__self__ is None:
60 return getattr, (m.__class__, m.__func__.__name__)
61 else:
62 return getattr, (m.__self__, m.__func__.__name__)
63class _C:
64 def f(self):
65 pass
66ForkingPickler.register(type(_C().f), _reduce_method)
67
68
69def _reduce_method_descriptor(m):
70 return getattr, (m.__objclass__, m.__name__)
71ForkingPickler.register(type(list.append), _reduce_method_descriptor)
72ForkingPickler.register(type(int.__add__), _reduce_method_descriptor)
73
74try:
75 from functools import partial
76except ImportError:
77 pass
78else:
79 def _reduce_partial(p):
80 return _rebuild_partial, (p.func, p.args, p.keywords or {})
81 def _rebuild_partial(func, args, keywords):
82 return partial(func, *args, **keywords)
83 ForkingPickler.register(partial, _reduce_partial)
84
85#
Benjamin Petersone711caf2008-06-11 16:44:04 +000086# Unix
87#
88
89if sys.platform != 'win32':
Benjamin Petersone711caf2008-06-11 16:44:04 +000090 duplicate = os.dup
91 close = os.close
92
93 #
94 # We define a Popen class similar to the one from subprocess, but
95 # whose constructor takes a process object as its argument.
96 #
97
98 class Popen(object):
99
100 def __init__(self, process_obj):
101 sys.stdout.flush()
102 sys.stderr.flush()
103 self.returncode = None
104
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200105 r, w = os.pipe()
106 self.sentinel = r
107
Benjamin Petersone711caf2008-06-11 16:44:04 +0000108 self.pid = os.fork()
109 if self.pid == 0:
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200110 os.close(r)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000111 if 'random' in sys.modules:
112 import random
113 random.seed()
114 code = process_obj._bootstrap()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000115 os._exit(code)
116
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200117 # `w` will be closed when the child exits, at which point `r`
118 # will become ready for reading (using e.g. select()).
119 os.close(w)
120 util.Finalize(self, os.close, (r,))
121
Benjamin Petersone711caf2008-06-11 16:44:04 +0000122 def poll(self, flag=os.WNOHANG):
123 if self.returncode is None:
Richard Oudkerk7aaa1ef2013-02-26 12:39:57 +0000124 while True:
125 try:
126 pid, sts = os.waitpid(self.pid, flag)
Richard Oudkerk03200252013-02-26 13:11:11 +0000127 except OSError as e:
Richard Oudkerk7aaa1ef2013-02-26 12:39:57 +0000128 if e.errno == errno.EINTR:
129 continue
130 # Child process not yet created. See #1731717
131 # e.errno == errno.ECHILD == 10
132 return None
133 else:
134 break
Benjamin Petersone711caf2008-06-11 16:44:04 +0000135 if pid == self.pid:
136 if os.WIFSIGNALED(sts):
137 self.returncode = -os.WTERMSIG(sts)
138 else:
139 assert os.WIFEXITED(sts)
140 self.returncode = os.WEXITSTATUS(sts)
141 return self.returncode
142
143 def wait(self, timeout=None):
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200144 if self.returncode is None:
145 if timeout is not None:
Richard Oudkerk59d54042012-05-10 16:11:12 +0100146 from .connection import wait
147 if not wait([self.sentinel], timeout):
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200148 return None
Richard Oudkerk59d54042012-05-10 16:11:12 +0100149 # This shouldn't block if wait() returned successfully.
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200150 return self.poll(os.WNOHANG if timeout == 0.0 else 0)
151 return self.returncode
Benjamin Petersone711caf2008-06-11 16:44:04 +0000152
153 def terminate(self):
154 if self.returncode is None:
155 try:
156 os.kill(self.pid, signal.SIGTERM)
Florent Xicluna04842a82011-11-11 20:05:50 +0100157 except OSError:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000158 if self.wait(timeout=0.1) is None:
159 raise
160
161 @staticmethod
162 def thread_is_spawning():
163 return False
164
165#
166# Windows
167#
168
169else:
170 import _thread
171 import msvcrt
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200172 import _winapi
Benjamin Petersone711caf2008-06-11 16:44:04 +0000173
Florent Xicluna04842a82011-11-11 20:05:50 +0100174 from pickle import load, HIGHEST_PROTOCOL
Benjamin Petersone711caf2008-06-11 16:44:04 +0000175
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +0000176 def dump(obj, file, protocol=None):
177 ForkingPickler(file, protocol).dump(obj)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000178
179 #
180 #
181 #
182
183 TERMINATE = 0x10000
184 WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False))
brian.curtine2f29982011-04-11 17:56:23 -0500185 WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
Benjamin Petersone711caf2008-06-11 16:44:04 +0000186
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200187 close = _winapi.CloseHandle
Benjamin Petersone711caf2008-06-11 16:44:04 +0000188
189 #
190 # _python_exe is the assumed path to the python executable.
191 # People embedding Python want to modify it.
192 #
193
brian.curtine2f29982011-04-11 17:56:23 -0500194 if WINSERVICE:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000195 _python_exe = os.path.join(sys.exec_prefix, 'python.exe')
196 else:
197 _python_exe = sys.executable
198
199 def set_executable(exe):
200 global _python_exe
201 _python_exe = exe
202
203 #
204 #
205 #
206
207 def duplicate(handle, target_process=None, inheritable=False):
208 if target_process is None:
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200209 target_process = _winapi.GetCurrentProcess()
210 return _winapi.DuplicateHandle(
211 _winapi.GetCurrentProcess(), handle, target_process,
212 0, inheritable, _winapi.DUPLICATE_SAME_ACCESS
213 )
Benjamin Petersone711caf2008-06-11 16:44:04 +0000214
215 #
216 # We define a Popen class similar to the one from subprocess, but
217 # whose constructor takes a process object as its argument.
218 #
219
220 class Popen(object):
221 '''
222 Start a subprocess to run the code of a process object
223 '''
224 _tls = _thread._local()
225
226 def __init__(self, process_obj):
Richard Oudkerkbd7b5dd2012-06-04 18:59:10 +0100227 cmd = ' '.join('"%s"' % x for x in get_command_line())
228 prep_data = get_preparation_data(process_obj._name)
229
Benjamin Petersone711caf2008-06-11 16:44:04 +0000230 # create pipe for communication with child
231 rfd, wfd = os.pipe()
232
233 # get handle for read end of the pipe and make it inheritable
234 rhandle = duplicate(msvcrt.get_osfhandle(rfd), inheritable=True)
235 os.close(rfd)
236
Richard Oudkerkbd7b5dd2012-06-04 18:59:10 +0100237 with open(wfd, 'wb', closefd=True) as to_child:
238 # start process
239 try:
240 hp, ht, pid, tid = _winapi.CreateProcess(
241 _python_exe, cmd + (' %s' % rhandle),
242 None, None, 1, 0, None, None, None
243 )
244 _winapi.CloseHandle(ht)
245 finally:
246 close(rhandle)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000247
Richard Oudkerkbd7b5dd2012-06-04 18:59:10 +0100248 # set attributes of self
249 self.pid = pid
250 self.returncode = None
251 self._handle = hp
252 self.sentinel = int(hp)
Richard Oudkerk021f4c22012-11-15 18:16:35 +0000253 util.Finalize(self, _winapi.CloseHandle, (self.sentinel,))
Benjamin Petersone711caf2008-06-11 16:44:04 +0000254
Richard Oudkerkbd7b5dd2012-06-04 18:59:10 +0100255 # send information to child
256 Popen._tls.process_handle = int(hp)
257 try:
258 dump(prep_data, to_child, HIGHEST_PROTOCOL)
259 dump(process_obj, to_child, HIGHEST_PROTOCOL)
260 finally:
261 del Popen._tls.process_handle
Benjamin Petersone711caf2008-06-11 16:44:04 +0000262
263 @staticmethod
264 def thread_is_spawning():
265 return getattr(Popen._tls, 'process_handle', None) is not None
266
267 @staticmethod
268 def duplicate_for_child(handle):
269 return duplicate(handle, Popen._tls.process_handle)
270
271 def wait(self, timeout=None):
272 if self.returncode is None:
273 if timeout is None:
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200274 msecs = _winapi.INFINITE
Benjamin Petersone711caf2008-06-11 16:44:04 +0000275 else:
276 msecs = max(0, int(timeout * 1000 + 0.5))
277
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200278 res = _winapi.WaitForSingleObject(int(self._handle), msecs)
279 if res == _winapi.WAIT_OBJECT_0:
280 code = _winapi.GetExitCodeProcess(self._handle)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000281 if code == TERMINATE:
282 code = -signal.SIGTERM
283 self.returncode = code
284
285 return self.returncode
286
287 def poll(self):
288 return self.wait(timeout=0)
289
290 def terminate(self):
291 if self.returncode is None:
292 try:
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200293 _winapi.TerminateProcess(int(self._handle), TERMINATE)
Richard Oudkerkb3035802012-08-01 17:44:18 +0100294 except OSError:
295 if self.wait(timeout=1.0) is None:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000296 raise
297
298 #
299 #
300 #
301
302 def is_forking(argv):
303 '''
304 Return whether commandline indicates we are forking
305 '''
306 if len(argv) >= 2 and argv[1] == '--multiprocessing-fork':
307 assert len(argv) == 3
308 return True
309 else:
310 return False
311
312
313 def freeze_support():
314 '''
315 Run code for process object if this in not the main process
316 '''
317 if is_forking(sys.argv):
318 main()
319 sys.exit()
320
321
322 def get_command_line():
323 '''
324 Returns prefix of command line used for spawning a child process
325 '''
Richard Oudkerke88a2442012-08-14 11:41:32 +0100326 if getattr(process.current_process(), '_inheriting', False):
Benjamin Petersone711caf2008-06-11 16:44:04 +0000327 raise RuntimeError('''
328 Attempt to start a new process before the current process
329 has finished its bootstrapping phase.
330
331 This probably means that you are on Windows and you have
332 forgotten to use the proper idiom in the main module:
333
334 if __name__ == '__main__':
335 freeze_support()
336 ...
337
338 The "freeze_support()" line can be omitted if the program
339 is not going to be frozen to produce a Windows executable.''')
340
341 if getattr(sys, 'frozen', False):
342 return [sys.executable, '--multiprocessing-fork']
343 else:
344 prog = 'from multiprocessing.forking import main; main()'
Richard Oudkerk77c84f22012-05-18 14:28:02 +0100345 opts = util._args_from_interpreter_flags()
346 return [_python_exe] + opts + ['-c', prog, '--multiprocessing-fork']
Benjamin Petersone711caf2008-06-11 16:44:04 +0000347
348
349 def main():
350 '''
351 Run code specifed by data received over pipe
352 '''
353 assert is_forking(sys.argv)
354
355 handle = int(sys.argv[-1])
356 fd = msvcrt.open_osfhandle(handle, os.O_RDONLY)
357 from_parent = os.fdopen(fd, 'rb')
358
359 process.current_process()._inheriting = True
360 preparation_data = load(from_parent)
361 prepare(preparation_data)
362 self = load(from_parent)
363 process.current_process()._inheriting = False
364
365 from_parent.close()
366
367 exitcode = self._bootstrap()
Richard Oudkerk73d9a292012-06-14 15:30:10 +0100368 sys.exit(exitcode)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000369
370
371 def get_preparation_data(name):
372 '''
373 Return info about parent needed by child to unpickle process object
374 '''
375 from .util import _logger, _log_to_stderr
376
377 d = dict(
378 name=name,
379 sys_path=sys.path,
380 sys_argv=sys.argv,
381 log_to_stderr=_log_to_stderr,
382 orig_dir=process.ORIGINAL_DIR,
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000383 authkey=process.current_process().authkey,
Benjamin Petersone711caf2008-06-11 16:44:04 +0000384 )
385
386 if _logger is not None:
387 d['log_level'] = _logger.getEffectiveLevel()
388
brian.curtine2f29982011-04-11 17:56:23 -0500389 if not WINEXE and not WINSERVICE:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000390 main_path = getattr(sys.modules['__main__'], '__file__', None)
391 if not main_path and sys.argv[0] not in ('', '-c'):
392 main_path = sys.argv[0]
393 if main_path is not None:
394 if not os.path.isabs(main_path) and \
395 process.ORIGINAL_DIR is not None:
396 main_path = os.path.join(process.ORIGINAL_DIR, main_path)
397 d['main_path'] = os.path.normpath(main_path)
398
399 return d
400
Benjamin Petersone711caf2008-06-11 16:44:04 +0000401#
402# Prepare current process
403#
404
405old_main_modules = []
406
407def prepare(data):
408 '''
409 Try to get current process ready to unpickle process object
410 '''
411 old_main_modules.append(sys.modules['__main__'])
412
413 if 'name' in data:
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000414 process.current_process().name = data['name']
Benjamin Petersone711caf2008-06-11 16:44:04 +0000415
416 if 'authkey' in data:
417 process.current_process()._authkey = data['authkey']
418
419 if 'log_to_stderr' in data and data['log_to_stderr']:
420 util.log_to_stderr()
421
422 if 'log_level' in data:
423 util.get_logger().setLevel(data['log_level'])
424
425 if 'sys_path' in data:
426 sys.path = data['sys_path']
427
428 if 'sys_argv' in data:
429 sys.argv = data['sys_argv']
430
431 if 'dir' in data:
432 os.chdir(data['dir'])
433
434 if 'orig_dir' in data:
435 process.ORIGINAL_DIR = data['orig_dir']
436
437 if 'main_path' in data:
Nick Coghlan793ee1f2011-01-30 01:24:08 +0000438 # XXX (ncoghlan): The following code makes several bogus
439 # assumptions regarding the relationship between __file__
440 # and a module's real name. See PEP 302 and issue #10845
Benjamin Petersone711caf2008-06-11 16:44:04 +0000441 main_path = data['main_path']
442 main_name = os.path.splitext(os.path.basename(main_path))[0]
443 if main_name == '__init__':
444 main_name = os.path.basename(os.path.dirname(main_path))
445
Nick Coghlan793ee1f2011-01-30 01:24:08 +0000446 if main_name == '__main__':
447 main_module = sys.modules['__main__']
448 main_module.__file__ = main_path
449 elif main_name != 'ipython':
450 # Main modules not actually called __main__.py may
451 # contain additional code that should still be executed
Benjamin Petersone711caf2008-06-11 16:44:04 +0000452 import imp
453
454 if main_path is None:
455 dirs = None
456 elif os.path.basename(main_path).startswith('__init__.py'):
457 dirs = [os.path.dirname(os.path.dirname(main_path))]
458 else:
459 dirs = [os.path.dirname(main_path)]
460
461 assert main_name not in sys.modules, main_name
Richard Oudkerk5046e972012-10-08 13:07:00 +0100462 sys.modules.pop('__mp_main__', None)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000463 file, path_name, etc = imp.find_module(main_name, dirs)
464 try:
Richard Oudkerk5046e972012-10-08 13:07:00 +0100465 # We should not do 'imp.load_module("__main__", ...)'
466 # since that would execute 'if __name__ == "__main__"'
467 # clauses, potentially causing a psuedo fork bomb.
Benjamin Petersone711caf2008-06-11 16:44:04 +0000468 main_module = imp.load_module(
Richard Oudkerk5046e972012-10-08 13:07:00 +0100469 '__mp_main__', file, path_name, etc
Benjamin Petersone711caf2008-06-11 16:44:04 +0000470 )
471 finally:
472 if file:
473 file.close()
474
Richard Oudkerk5046e972012-10-08 13:07:00 +0100475 sys.modules['__main__'] = sys.modules['__mp_main__'] = main_module