blob: 3a474cd7d990f61e51a652c72ba578a892a1fc6c [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':
Benjamin Petersone711caf2008-06-11 16:44:04 +000078 exit = os._exit
79 duplicate = os.dup
80 close = os.close
81
82 #
83 # We define a Popen class similar to the one from subprocess, but
84 # whose constructor takes a process object as its argument.
85 #
86
87 class Popen(object):
88
89 def __init__(self, process_obj):
90 sys.stdout.flush()
91 sys.stderr.flush()
92 self.returncode = None
93
Antoine Pitrou176f07d2011-06-06 19:35:31 +020094 r, w = os.pipe()
95 self.sentinel = r
96
Benjamin Petersone711caf2008-06-11 16:44:04 +000097 self.pid = os.fork()
98 if self.pid == 0:
Antoine Pitrou176f07d2011-06-06 19:35:31 +020099 os.close(r)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000100 if 'random' in sys.modules:
101 import random
102 random.seed()
103 code = process_obj._bootstrap()
Benjamin Petersone711caf2008-06-11 16:44:04 +0000104 os._exit(code)
105
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200106 # `w` will be closed when the child exits, at which point `r`
107 # will become ready for reading (using e.g. select()).
108 os.close(w)
109 util.Finalize(self, os.close, (r,))
110
Benjamin Petersone711caf2008-06-11 16:44:04 +0000111 def poll(self, flag=os.WNOHANG):
112 if self.returncode is None:
Florent Xicluna998171f2010-03-08 13:32:17 +0000113 try:
114 pid, sts = os.waitpid(self.pid, flag)
115 except os.error:
116 # Child process not yet created. See #1731717
117 # e.errno == errno.ECHILD == 10
118 return None
Benjamin Petersone711caf2008-06-11 16:44:04 +0000119 if pid == self.pid:
120 if os.WIFSIGNALED(sts):
121 self.returncode = -os.WTERMSIG(sts)
122 else:
123 assert os.WIFEXITED(sts)
124 self.returncode = os.WEXITSTATUS(sts)
125 return self.returncode
126
127 def wait(self, timeout=None):
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200128 if self.returncode is None:
129 if timeout is not None:
Richard Oudkerk59d54042012-05-10 16:11:12 +0100130 from .connection import wait
131 if not wait([self.sentinel], timeout):
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200132 return None
Richard Oudkerk59d54042012-05-10 16:11:12 +0100133 # This shouldn't block if wait() returned successfully.
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200134 return self.poll(os.WNOHANG if timeout == 0.0 else 0)
135 return self.returncode
Benjamin Petersone711caf2008-06-11 16:44:04 +0000136
137 def terminate(self):
138 if self.returncode is None:
139 try:
140 os.kill(self.pid, signal.SIGTERM)
Florent Xicluna04842a82011-11-11 20:05:50 +0100141 except OSError:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000142 if self.wait(timeout=0.1) is None:
143 raise
144
145 @staticmethod
146 def thread_is_spawning():
147 return False
148
149#
150# Windows
151#
152
153else:
154 import _thread
155 import msvcrt
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200156 import _winapi
Benjamin Petersone711caf2008-06-11 16:44:04 +0000157
Florent Xicluna04842a82011-11-11 20:05:50 +0100158 from pickle import load, HIGHEST_PROTOCOL
Benjamin Petersone711caf2008-06-11 16:44:04 +0000159
Amaury Forgeot d'Arc949d47d2008-08-19 21:30:55 +0000160 def dump(obj, file, protocol=None):
161 ForkingPickler(file, protocol).dump(obj)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000162
163 #
164 #
165 #
166
167 TERMINATE = 0x10000
168 WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False))
brian.curtine2f29982011-04-11 17:56:23 -0500169 WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
Benjamin Petersone711caf2008-06-11 16:44:04 +0000170
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200171 exit = _winapi.ExitProcess
172 close = _winapi.CloseHandle
Benjamin Petersone711caf2008-06-11 16:44:04 +0000173
174 #
175 # _python_exe is the assumed path to the python executable.
176 # People embedding Python want to modify it.
177 #
178
brian.curtine2f29982011-04-11 17:56:23 -0500179 if WINSERVICE:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000180 _python_exe = os.path.join(sys.exec_prefix, 'python.exe')
181 else:
182 _python_exe = sys.executable
183
184 def set_executable(exe):
185 global _python_exe
186 _python_exe = exe
187
188 #
189 #
190 #
191
192 def duplicate(handle, target_process=None, inheritable=False):
193 if target_process is None:
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200194 target_process = _winapi.GetCurrentProcess()
195 return _winapi.DuplicateHandle(
196 _winapi.GetCurrentProcess(), handle, target_process,
197 0, inheritable, _winapi.DUPLICATE_SAME_ACCESS
198 )
Benjamin Petersone711caf2008-06-11 16:44:04 +0000199
200 #
201 # We define a Popen class similar to the one from subprocess, but
202 # whose constructor takes a process object as its argument.
203 #
204
205 class Popen(object):
206 '''
207 Start a subprocess to run the code of a process object
208 '''
209 _tls = _thread._local()
210
211 def __init__(self, process_obj):
Richard Oudkerkbd7b5dd2012-06-04 18:59:10 +0100212 cmd = ' '.join('"%s"' % x for x in get_command_line())
213 prep_data = get_preparation_data(process_obj._name)
214
Benjamin Petersone711caf2008-06-11 16:44:04 +0000215 # 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
Richard Oudkerkbd7b5dd2012-06-04 18:59:10 +0100222 with open(wfd, 'wb', closefd=True) as to_child:
223 # start process
224 try:
225 hp, ht, pid, tid = _winapi.CreateProcess(
226 _python_exe, cmd + (' %s' % rhandle),
227 None, None, 1, 0, None, None, None
228 )
229 _winapi.CloseHandle(ht)
230 finally:
231 close(rhandle)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000232
Richard Oudkerkbd7b5dd2012-06-04 18:59:10 +0100233 # set attributes of self
234 self.pid = pid
235 self.returncode = None
236 self._handle = hp
237 self.sentinel = int(hp)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000238
Richard Oudkerkbd7b5dd2012-06-04 18:59:10 +0100239 # send information to child
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
Benjamin Petersone711caf2008-06-11 16:44:04 +0000246
247 @staticmethod
248 def thread_is_spawning():
249 return getattr(Popen._tls, 'process_handle', None) is not None
250
251 @staticmethod
252 def duplicate_for_child(handle):
253 return duplicate(handle, Popen._tls.process_handle)
254
255 def wait(self, timeout=None):
256 if self.returncode is None:
257 if timeout is None:
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200258 msecs = _winapi.INFINITE
Benjamin Petersone711caf2008-06-11 16:44:04 +0000259 else:
260 msecs = max(0, int(timeout * 1000 + 0.5))
261
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200262 res = _winapi.WaitForSingleObject(int(self._handle), msecs)
263 if res == _winapi.WAIT_OBJECT_0:
264 code = _winapi.GetExitCodeProcess(self._handle)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000265 if code == TERMINATE:
266 code = -signal.SIGTERM
267 self.returncode = code
268
269 return self.returncode
270
271 def poll(self):
272 return self.wait(timeout=0)
273
274 def terminate(self):
275 if self.returncode is None:
276 try:
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200277 _winapi.TerminateProcess(int(self._handle), TERMINATE)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000278 except WindowsError:
279 if self.wait(timeout=0.1) is None:
280 raise
281
282 #
283 #
284 #
285
286 def is_forking(argv):
287 '''
288 Return whether commandline indicates we are forking
289 '''
290 if len(argv) >= 2 and argv[1] == '--multiprocessing-fork':
291 assert len(argv) == 3
292 return True
293 else:
294 return False
295
296
297 def freeze_support():
298 '''
299 Run code for process object if this in not the main process
300 '''
301 if is_forking(sys.argv):
302 main()
303 sys.exit()
304
305
306 def get_command_line():
307 '''
308 Returns prefix of command line used for spawning a child process
309 '''
310 if process.current_process()._identity==() and is_forking(sys.argv):
311 raise RuntimeError('''
312 Attempt to start a new process before the current process
313 has finished its bootstrapping phase.
314
315 This probably means that you are on Windows and you have
316 forgotten to use the proper idiom in the main module:
317
318 if __name__ == '__main__':
319 freeze_support()
320 ...
321
322 The "freeze_support()" line can be omitted if the program
323 is not going to be frozen to produce a Windows executable.''')
324
325 if getattr(sys, 'frozen', False):
326 return [sys.executable, '--multiprocessing-fork']
327 else:
328 prog = 'from multiprocessing.forking import main; main()'
Richard Oudkerk77c84f22012-05-18 14:28:02 +0100329 opts = util._args_from_interpreter_flags()
330 return [_python_exe] + opts + ['-c', prog, '--multiprocessing-fork']
Benjamin Petersone711caf2008-06-11 16:44:04 +0000331
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