blob: 2729afe2bbd01e6728d082aa19c9aa250b1894de [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):
212 # create pipe for communication with child
213 rfd, wfd = os.pipe()
214
215 # get handle for read end of the pipe and make it inheritable
216 rhandle = duplicate(msvcrt.get_osfhandle(rfd), inheritable=True)
217 os.close(rfd)
218
219 # start process
220 cmd = get_command_line() + [rhandle]
221 cmd = ' '.join('"%s"' % x for x in cmd)
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200222 hp, ht, pid, tid = _winapi.CreateProcess(
Benjamin Petersone711caf2008-06-11 16:44:04 +0000223 _python_exe, cmd, None, None, 1, 0, None, None, None
224 )
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200225 _winapi.CloseHandle(ht)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000226 close(rhandle)
227
228 # set attributes of self
229 self.pid = pid
230 self.returncode = None
231 self._handle = hp
Antoine Pitrou176f07d2011-06-06 19:35:31 +0200232 self.sentinel = int(hp)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000233
234 # send information to child
235 prep_data = get_preparation_data(process_obj._name)
236 to_child = os.fdopen(wfd, 'wb')
237 Popen._tls.process_handle = int(hp)
238 try:
239 dump(prep_data, to_child, HIGHEST_PROTOCOL)
240 dump(process_obj, to_child, HIGHEST_PROTOCOL)
241 finally:
242 del Popen._tls.process_handle
243 to_child.close()
244
245 @staticmethod
246 def thread_is_spawning():
247 return getattr(Popen._tls, 'process_handle', None) is not None
248
249 @staticmethod
250 def duplicate_for_child(handle):
251 return duplicate(handle, Popen._tls.process_handle)
252
253 def wait(self, timeout=None):
254 if self.returncode is None:
255 if timeout is None:
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200256 msecs = _winapi.INFINITE
Benjamin Petersone711caf2008-06-11 16:44:04 +0000257 else:
258 msecs = max(0, int(timeout * 1000 + 0.5))
259
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200260 res = _winapi.WaitForSingleObject(int(self._handle), msecs)
261 if res == _winapi.WAIT_OBJECT_0:
262 code = _winapi.GetExitCodeProcess(self._handle)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000263 if code == TERMINATE:
264 code = -signal.SIGTERM
265 self.returncode = code
266
267 return self.returncode
268
269 def poll(self):
270 return self.wait(timeout=0)
271
272 def terminate(self):
273 if self.returncode is None:
274 try:
Antoine Pitrou23bba4c2012-04-18 20:51:15 +0200275 _winapi.TerminateProcess(int(self._handle), TERMINATE)
Benjamin Petersone711caf2008-06-11 16:44:04 +0000276 except WindowsError:
277 if self.wait(timeout=0.1) is None:
278 raise
279
280 #
281 #
282 #
283
284 def is_forking(argv):
285 '''
286 Return whether commandline indicates we are forking
287 '''
288 if len(argv) >= 2 and argv[1] == '--multiprocessing-fork':
289 assert len(argv) == 3
290 return True
291 else:
292 return False
293
294
295 def freeze_support():
296 '''
297 Run code for process object if this in not the main process
298 '''
299 if is_forking(sys.argv):
300 main()
301 sys.exit()
302
303
304 def get_command_line():
305 '''
306 Returns prefix of command line used for spawning a child process
307 '''
308 if process.current_process()._identity==() and is_forking(sys.argv):
309 raise RuntimeError('''
310 Attempt to start a new process before the current process
311 has finished its bootstrapping phase.
312
313 This probably means that you are on Windows and you have
314 forgotten to use the proper idiom in the main module:
315
316 if __name__ == '__main__':
317 freeze_support()
318 ...
319
320 The "freeze_support()" line can be omitted if the program
321 is not going to be frozen to produce a Windows executable.''')
322
323 if getattr(sys, 'frozen', False):
324 return [sys.executable, '--multiprocessing-fork']
325 else:
326 prog = 'from multiprocessing.forking import main; main()'
327 return [_python_exe, '-c', prog, '--multiprocessing-fork']
328
329
330 def main():
331 '''
332 Run code specifed by data received over pipe
333 '''
334 assert is_forking(sys.argv)
335
336 handle = int(sys.argv[-1])
337 fd = msvcrt.open_osfhandle(handle, os.O_RDONLY)
338 from_parent = os.fdopen(fd, 'rb')
339
340 process.current_process()._inheriting = True
341 preparation_data = load(from_parent)
342 prepare(preparation_data)
343 self = load(from_parent)
344 process.current_process()._inheriting = False
345
346 from_parent.close()
347
348 exitcode = self._bootstrap()
349 exit(exitcode)
350
351
352 def get_preparation_data(name):
353 '''
354 Return info about parent needed by child to unpickle process object
355 '''
356 from .util import _logger, _log_to_stderr
357
358 d = dict(
359 name=name,
360 sys_path=sys.path,
361 sys_argv=sys.argv,
362 log_to_stderr=_log_to_stderr,
363 orig_dir=process.ORIGINAL_DIR,
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000364 authkey=process.current_process().authkey,
Benjamin Petersone711caf2008-06-11 16:44:04 +0000365 )
366
367 if _logger is not None:
368 d['log_level'] = _logger.getEffectiveLevel()
369
brian.curtine2f29982011-04-11 17:56:23 -0500370 if not WINEXE and not WINSERVICE:
Benjamin Petersone711caf2008-06-11 16:44:04 +0000371 main_path = getattr(sys.modules['__main__'], '__file__', None)
372 if not main_path and sys.argv[0] not in ('', '-c'):
373 main_path = sys.argv[0]
374 if main_path is not None:
375 if not os.path.isabs(main_path) and \
376 process.ORIGINAL_DIR is not None:
377 main_path = os.path.join(process.ORIGINAL_DIR, main_path)
378 d['main_path'] = os.path.normpath(main_path)
379
380 return d
381
Benjamin Petersone711caf2008-06-11 16:44:04 +0000382#
383# Prepare current process
384#
385
386old_main_modules = []
387
388def prepare(data):
389 '''
390 Try to get current process ready to unpickle process object
391 '''
392 old_main_modules.append(sys.modules['__main__'])
393
394 if 'name' in data:
Benjamin Peterson58ea9fe2008-08-19 19:17:39 +0000395 process.current_process().name = data['name']
Benjamin Petersone711caf2008-06-11 16:44:04 +0000396
397 if 'authkey' in data:
398 process.current_process()._authkey = data['authkey']
399
400 if 'log_to_stderr' in data and data['log_to_stderr']:
401 util.log_to_stderr()
402
403 if 'log_level' in data:
404 util.get_logger().setLevel(data['log_level'])
405
406 if 'sys_path' in data:
407 sys.path = data['sys_path']
408
409 if 'sys_argv' in data:
410 sys.argv = data['sys_argv']
411
412 if 'dir' in data:
413 os.chdir(data['dir'])
414
415 if 'orig_dir' in data:
416 process.ORIGINAL_DIR = data['orig_dir']
417
418 if 'main_path' in data:
Nick Coghlan793ee1f2011-01-30 01:24:08 +0000419 # XXX (ncoghlan): The following code makes several bogus
420 # assumptions regarding the relationship between __file__
421 # and a module's real name. See PEP 302 and issue #10845
Benjamin Petersone711caf2008-06-11 16:44:04 +0000422 main_path = data['main_path']
423 main_name = os.path.splitext(os.path.basename(main_path))[0]
424 if main_name == '__init__':
425 main_name = os.path.basename(os.path.dirname(main_path))
426
Nick Coghlan793ee1f2011-01-30 01:24:08 +0000427 if main_name == '__main__':
428 main_module = sys.modules['__main__']
429 main_module.__file__ = main_path
430 elif main_name != 'ipython':
431 # Main modules not actually called __main__.py may
432 # contain additional code that should still be executed
Benjamin Petersone711caf2008-06-11 16:44:04 +0000433 import imp
434
435 if main_path is None:
436 dirs = None
437 elif os.path.basename(main_path).startswith('__init__.py'):
438 dirs = [os.path.dirname(os.path.dirname(main_path))]
439 else:
440 dirs = [os.path.dirname(main_path)]
441
442 assert main_name not in sys.modules, main_name
443 file, path_name, etc = imp.find_module(main_name, dirs)
444 try:
445 # We would like to do "imp.load_module('__main__', ...)"
446 # here. However, that would cause 'if __name__ ==
447 # "__main__"' clauses to be executed.
448 main_module = imp.load_module(
449 '__parents_main__', file, path_name, etc
450 )
451 finally:
452 if file:
453 file.close()
454
455 sys.modules['__main__'] = main_module
456 main_module.__name__ = '__main__'
457
458 # Try to make the potentially picklable objects in
459 # sys.modules['__main__'] realize they are in the main
460 # module -- somewhat ugly.
461 for obj in list(main_module.__dict__.values()):
462 try:
463 if obj.__module__ == '__parents_main__':
464 obj.__module__ = '__main__'
465 except Exception:
466 pass