blob: ca03e955ff134383098468cc9cdae6cbeded50b9 [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
10import os
11import sys
12import signal
13
14from multiprocessing import util, process
15
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +000016__all__ = ['Popen', 'assert_spawning', 'exit', 'duplicate', 'close', 'ForkingPickler']
Benjamin Petersone711caf2008-06-11 16:44:04 +000017
18#
19# Check that the current thread is spawning a child process
20#
21
22def assert_spawning(self):
23 if not Popen.thread_is_spawning():
24 raise RuntimeError(
25 '%s objects should only be shared between processes'
26 ' through inheritance' % type(self).__name__
27 )
28
29#
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +000030# Try making some callable types picklable
31#
32
Antoine Pitrou846fd302012-03-17 00:23:04 +010033from pickle import Pickler
34from copyreg import dispatch_table
35
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +000036class ForkingPickler(Pickler):
Antoine Pitrou846fd302012-03-17 00:23:04 +010037 _extra_reducers = {}
38 def __init__(self, *args):
39 Pickler.__init__(self, *args)
40 self.dispatch_table = dispatch_table.copy()
41 self.dispatch_table.update(self._extra_reducers)
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +000042 @classmethod
43 def register(cls, type, reduce):
Antoine Pitrou846fd302012-03-17 00:23:04 +010044 cls._extra_reducers[type] = reduce
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +000045
46def _reduce_method(m):
47 if m.__self__ is None:
48 return getattr, (m.__class__, m.__func__.__name__)
49 else:
50 return getattr, (m.__self__, m.__func__.__name__)
51class _C:
52 def f(self):
53 pass
54ForkingPickler.register(type(_C().f), _reduce_method)
55
56
57def _reduce_method_descriptor(m):
58 return getattr, (m.__objclass__, m.__name__)
59ForkingPickler.register(type(list.append), _reduce_method_descriptor)
60ForkingPickler.register(type(int.__add__), _reduce_method_descriptor)
61
62try:
63 from functools import partial
64except ImportError:
65 pass
66else:
67 def _reduce_partial(p):
68 return _rebuild_partial, (p.func, p.args, p.keywords or {})
69 def _rebuild_partial(func, args, keywords):
70 return partial(func, *args, **keywords)
71 ForkingPickler.register(partial, _reduce_partial)
72
73#
Benjamin Petersone711caf2008-06-11 16:44:04 +000074# Unix
75#
76
77if sys.platform != 'win32':
Antoine Pitrou176f07d2011-06-06 19:35:31 +020078 import select
Benjamin Petersone711caf2008-06-11 16:44:04 +000079
80 exit = os._exit
81 duplicate = os.dup
82 close = os.close
Antoine Pitrou176f07d2011-06-06 19:35:31 +020083 _select = util._eintr_retry(select.select)
Benjamin Petersone711caf2008-06-11 16:44:04 +000084
85 #
86 # We define a Popen class similar to the one from subprocess, but
87 # whose constructor takes a process object as its argument.
88 #
89
90 class Popen(object):
91
92 def __init__(self, process_obj):
93 sys.stdout.flush()
94 sys.stderr.flush()
95 self.returncode = None
96
Antoine Pitrou176f07d2011-06-06 19:35:31 +020097 r, w = os.pipe()
98 self.sentinel = r
99
Benjamin Petersone711caf2008-06-11 16:44:04 +0000100 self.pid = os.fork()
101 if self.pid == 0:
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200102 os.close(r)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000103 if 'random' in sys.modules:
104 import random
105 random.seed()
106 code = process_obj._bootstrap()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000107 os._exit(code)
108
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200109 # `w` will be closed when the child exits, at which point `r`
110 # will become ready for reading (using e.g. select()).
111 os.close(w)
112 util.Finalize(self, os.close, (r,))
113
Benjamin Petersone711caf2008-06-11 16:44:04 +0000114 def poll(self, flag=os.WNOHANG):
115 if self.returncode is None:
Florent Xicluna998171f2010-03-08 13:32:17 +0000116 try:
117 pid, sts = os.waitpid(self.pid, flag)
118 except os.error:
119 # Child process not yet created. See #1731717
120 # e.errno == errno.ECHILD == 10
121 return None
Benjamin Petersone711caf2008-06-11 16:44:04 +0000122 if pid == self.pid:
123 if os.WIFSIGNALED(sts):
124 self.returncode = -os.WTERMSIG(sts)
125 else:
126 assert os.WIFEXITED(sts)
127 self.returncode = os.WEXITSTATUS(sts)
128 return self.returncode
129
130 def wait(self, timeout=None):
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200131 if self.returncode is None:
132 if timeout is not None:
133 r = _select([self.sentinel], [], [], timeout)[0]
134 if not r:
135 return None
136 # This shouldn't block if select() returned successfully.
137 return self.poll(os.WNOHANG if timeout == 0.0 else 0)
138 return self.returncode
Benjamin Petersone711caf2008-06-11 16:44:04 +0000139
140 def terminate(self):
141 if self.returncode is None:
142 try:
143 os.kill(self.pid, signal.SIGTERM)
Florent Xicluna04842a82011-11-11 20:05:50 +0100144 except OSError:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000145 if self.wait(timeout=0.1) is None:
146 raise
147
148 @staticmethod
149 def thread_is_spawning():
150 return False
151
152#
153# Windows
154#
155
156else:
157 import _thread
158 import msvcrt
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200159 import _winapi
Benjamin Petersone711caf2008-06-11 16:44:04 +0000160
Florent Xicluna04842a82011-11-11 20:05:50 +0100161 from pickle import load, HIGHEST_PROTOCOL
Benjamin Petersone711caf2008-06-11 16:44:04 +0000162
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +0000163 def dump(obj, file, protocol=None):
164 ForkingPickler(file, protocol).dump(obj)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000165
166 #
167 #
168 #
169
170 TERMINATE = 0x10000
171 WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False))
brian.curtine2f29982011-04-11 17:56:23 -0500172 WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
Benjamin Petersone711caf2008-06-11 16:44:04 +0000173
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200174 exit = _winapi.ExitProcess
175 close = _winapi.CloseHandle
Benjamin Petersone711caf2008-06-11 16:44:04 +0000176
177 #
178 # _python_exe is the assumed path to the python executable.
179 # People embedding Python want to modify it.
180 #
181
brian.curtine2f29982011-04-11 17:56:23 -0500182 if WINSERVICE:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000183 _python_exe = os.path.join(sys.exec_prefix, 'python.exe')
184 else:
185 _python_exe = sys.executable
186
187 def set_executable(exe):
188 global _python_exe
189 _python_exe = exe
190
191 #
192 #
193 #
194
195 def duplicate(handle, target_process=None, inheritable=False):
196 if target_process is None:
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200197 target_process = _winapi.GetCurrentProcess()
198 return _winapi.DuplicateHandle(
199 _winapi.GetCurrentProcess(), handle, target_process,
200 0, inheritable, _winapi.DUPLICATE_SAME_ACCESS
201 )
Benjamin Petersone711caf2008-06-11 16:44:04 +0000202
203 #
204 # We define a Popen class similar to the one from subprocess, but
205 # whose constructor takes a process object as its argument.
206 #
207
208 class Popen(object):
209 '''
210 Start a subprocess to run the code of a process object
211 '''
212 _tls = _thread._local()
213
214 def __init__(self, process_obj):
215 # create pipe for communication with child
216 rfd, wfd = os.pipe()
217
218 # get handle for read end of the pipe and make it inheritable
219 rhandle = duplicate(msvcrt.get_osfhandle(rfd), inheritable=True)
220 os.close(rfd)
221
222 # start process
223 cmd = get_command_line() + [rhandle]
224 cmd = ' '.join('"%s"' % x for x in cmd)
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200225 hp, ht, pid, tid = _winapi.CreateProcess(
Benjamin Petersone711caf2008-06-11 16:44:04 +0000226 _python_exe, cmd, None, None, 1, 0, None, None, None
227 )
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200228 _winapi.CloseHandle(ht)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000229 close(rhandle)
230
231 # set attributes of self
232 self.pid = pid
233 self.returncode = None
234 self._handle = hp
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200235 self.sentinel = int(hp)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000236
237 # send information to child
238 prep_data = get_preparation_data(process_obj._name)
239 to_child = os.fdopen(wfd, 'wb')
240 Popen._tls.process_handle = int(hp)
241 try:
242 dump(prep_data, to_child, HIGHEST_PROTOCOL)
243 dump(process_obj, to_child, HIGHEST_PROTOCOL)
244 finally:
245 del Popen._tls.process_handle
246 to_child.close()
247
248 @staticmethod
249 def thread_is_spawning():
250 return getattr(Popen._tls, 'process_handle', None) is not None
251
252 @staticmethod
253 def duplicate_for_child(handle):
254 return duplicate(handle, Popen._tls.process_handle)
255
256 def wait(self, timeout=None):
257 if self.returncode is None:
258 if timeout is None:
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200259 msecs = _winapi.INFINITE
Benjamin Petersone711caf2008-06-11 16:44:04 +0000260 else:
261 msecs = max(0, int(timeout * 1000 + 0.5))
262
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200263 res = _winapi.WaitForSingleObject(int(self._handle), msecs)
264 if res == _winapi.WAIT_OBJECT_0:
265 code = _winapi.GetExitCodeProcess(self._handle)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000266 if code == TERMINATE:
267 code = -signal.SIGTERM
268 self.returncode = code
269
270 return self.returncode
271
272 def poll(self):
273 return self.wait(timeout=0)
274
275 def terminate(self):
276 if self.returncode is None:
277 try:
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200278 _winapi.TerminateProcess(int(self._handle), TERMINATE)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000279 except WindowsError:
280 if self.wait(timeout=0.1) is None:
281 raise
282
283 #
284 #
285 #
286
287 def is_forking(argv):
288 '''
289 Return whether commandline indicates we are forking
290 '''
291 if len(argv) >= 2 and argv[1] == '--multiprocessing-fork':
292 assert len(argv) == 3
293 return True
294 else:
295 return False
296
297
298 def freeze_support():
299 '''
300 Run code for process object if this in not the main process
301 '''
302 if is_forking(sys.argv):
303 main()
304 sys.exit()
305
306
307 def get_command_line():
308 '''
309 Returns prefix of command line used for spawning a child process
310 '''
311 if process.current_process()._identity==() and is_forking(sys.argv):
312 raise RuntimeError('''
313 Attempt to start a new process before the current process
314 has finished its bootstrapping phase.
315
316 This probably means that you are on Windows and you have
317 forgotten to use the proper idiom in the main module:
318
319 if __name__ == '__main__':
320 freeze_support()
321 ...
322
323 The "freeze_support()" line can be omitted if the program
324 is not going to be frozen to produce a Windows executable.''')
325
326 if getattr(sys, 'frozen', False):
327 return [sys.executable, '--multiprocessing-fork']
328 else:
329 prog = 'from multiprocessing.forking import main; main()'
330 return [_python_exe, '-c', prog, '--multiprocessing-fork']
331
332
333 def main():
334 '''
335 Run code specifed by data received over pipe
336 '''
337 assert is_forking(sys.argv)
338
339 handle = int(sys.argv[-1])
340 fd = msvcrt.open_osfhandle(handle, os.O_RDONLY)
341 from_parent = os.fdopen(fd, 'rb')
342
343 process.current_process()._inheriting = True
344 preparation_data = load(from_parent)
345 prepare(preparation_data)
346 self = load(from_parent)
347 process.current_process()._inheriting = False
348
349 from_parent.close()
350
351 exitcode = self._bootstrap()
352 exit(exitcode)
353
354
355 def get_preparation_data(name):
356 '''
357 Return info about parent needed by child to unpickle process object
358 '''
359 from .util import _logger, _log_to_stderr
360
361 d = dict(
362 name=name,
363 sys_path=sys.path,
364 sys_argv=sys.argv,
365 log_to_stderr=_log_to_stderr,
366 orig_dir=process.ORIGINAL_DIR,
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000367 authkey=process.current_process().authkey,
Benjamin Petersone711caf2008-06-11 16:44:04 +0000368 )
369
370 if _logger is not None:
371 d['log_level'] = _logger.getEffectiveLevel()
372
brian.curtine2f29982011-04-11 17:56:23 -0500373 if not WINEXE and not WINSERVICE:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000374 main_path = getattr(sys.modules['__main__'], '__file__', None)
375 if not main_path and sys.argv[0] not in ('', '-c'):
376 main_path = sys.argv[0]
377 if main_path is not None:
378 if not os.path.isabs(main_path) and \
379 process.ORIGINAL_DIR is not None:
380 main_path = os.path.join(process.ORIGINAL_DIR, main_path)
381 d['main_path'] = os.path.normpath(main_path)
382
383 return d
384
Benjamin Petersone711caf2008-06-11 16:44:04 +0000385#
386# Prepare current process
387#
388
389old_main_modules = []
390
391def prepare(data):
392 '''
393 Try to get current process ready to unpickle process object
394 '''
395 old_main_modules.append(sys.modules['__main__'])
396
397 if 'name' in data:
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000398 process.current_process().name = data['name']
Benjamin Petersone711caf2008-06-11 16:44:04 +0000399
400 if 'authkey' in data:
401 process.current_process()._authkey = data['authkey']
402
403 if 'log_to_stderr' in data and data['log_to_stderr']:
404 util.log_to_stderr()
405
406 if 'log_level' in data:
407 util.get_logger().setLevel(data['log_level'])
408
409 if 'sys_path' in data:
410 sys.path = data['sys_path']
411
412 if 'sys_argv' in data:
413 sys.argv = data['sys_argv']
414
415 if 'dir' in data:
416 os.chdir(data['dir'])
417
418 if 'orig_dir' in data:
419 process.ORIGINAL_DIR = data['orig_dir']
420
421 if 'main_path' in data:
Nick Coghlan793ee1f2011-01-30 01:24:08 +0000422 # XXX (ncoghlan): The following code makes several bogus
423 # assumptions regarding the relationship between __file__
424 # and a module's real name. See PEP 302 and issue #10845
Benjamin Petersone711caf2008-06-11 16:44:04 +0000425 main_path = data['main_path']
426 main_name = os.path.splitext(os.path.basename(main_path))[0]
427 if main_name == '__init__':
428 main_name = os.path.basename(os.path.dirname(main_path))
429
Nick Coghlan793ee1f2011-01-30 01:24:08 +0000430 if main_name == '__main__':
431 main_module = sys.modules['__main__']
432 main_module.__file__ = main_path
433 elif main_name != 'ipython':
434 # Main modules not actually called __main__.py may
435 # contain additional code that should still be executed
Benjamin Petersone711caf2008-06-11 16:44:04 +0000436 import imp
437
438 if main_path is None:
439 dirs = None
440 elif os.path.basename(main_path).startswith('__init__.py'):
441 dirs = [os.path.dirname(os.path.dirname(main_path))]
442 else:
443 dirs = [os.path.dirname(main_path)]
444
445 assert main_name not in sys.modules, main_name
446 file, path_name, etc = imp.find_module(main_name, dirs)
447 try:
448 # We would like to do "imp.load_module('__main__', ...)"
449 # here. However, that would cause 'if __name__ ==
450 # "__main__"' clauses to be executed.
451 main_module = imp.load_module(
452 '__parents_main__', file, path_name, etc
453 )
454 finally:
455 if file:
456 file.close()
457
458 sys.modules['__main__'] = main_module
459 main_module.__name__ = '__main__'
460
461 # Try to make the potentially picklable objects in
462 # sys.modules['__main__'] realize they are in the main
463 # module -- somewhat ugly.
464 for obj in list(main_module.__dict__.values()):
465 try:
466 if obj.__module__ == '__parents_main__':
467 obj.__module__ = '__main__'
468 except Exception:
469 pass