Commit 73d9a292 authored by Richard Oudkerk's avatar Richard Oudkerk

Issue #13841: Make child processes exit using sys.exit() on Windows

parent bc07cb88
...@@ -13,7 +13,7 @@ import signal ...@@ -13,7 +13,7 @@ import signal
from multiprocessing import util, process from multiprocessing import util, process
__all__ = ['Popen', 'assert_spawning', 'exit', 'duplicate', 'close', 'ForkingPickler'] __all__ = ['Popen', 'assert_spawning', 'duplicate', 'close', 'ForkingPickler']
# #
# Check that the current thread is spawning a child process # Check that the current thread is spawning a child process
...@@ -75,7 +75,6 @@ else: ...@@ -75,7 +75,6 @@ else:
# #
if sys.platform != 'win32': if sys.platform != 'win32':
exit = os._exit
duplicate = os.dup duplicate = os.dup
close = os.close close = os.close
...@@ -168,7 +167,6 @@ else: ...@@ -168,7 +167,6 @@ else:
WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False)) WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False))
WINSERVICE = sys.executable.lower().endswith("pythonservice.exe") WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
exit = _winapi.ExitProcess
close = _winapi.CloseHandle close = _winapi.CloseHandle
# #
...@@ -349,7 +347,7 @@ else: ...@@ -349,7 +347,7 @@ else:
from_parent.close() from_parent.close()
exitcode = self._bootstrap() exitcode = self._bootstrap()
exit(exitcode) sys.exit(exitcode)
def get_preparation_data(name): def get_preparation_data(name):
......
...@@ -22,7 +22,7 @@ import queue ...@@ -22,7 +22,7 @@ import queue
from traceback import format_exc from traceback import format_exc
from multiprocessing import Process, current_process, active_children, Pool, util, connection from multiprocessing import Process, current_process, active_children, Pool, util, connection
from multiprocessing.process import AuthenticationString from multiprocessing.process import AuthenticationString
from multiprocessing.forking import exit, Popen, ForkingPickler from multiprocessing.forking import Popen, ForkingPickler
from time import time as _time from time import time as _time
# #
...@@ -140,28 +140,38 @@ class Server(object): ...@@ -140,28 +140,38 @@ class Server(object):
self.id_to_obj = {'0': (None, ())} self.id_to_obj = {'0': (None, ())}
self.id_to_refcount = {} self.id_to_refcount = {}
self.mutex = threading.RLock() self.mutex = threading.RLock()
self.stop = 0
def serve_forever(self): def serve_forever(self):
''' '''
Run the server forever Run the server forever
''' '''
self.stop_event = threading.Event()
current_process()._manager_server = self current_process()._manager_server = self
try: try:
accepter = threading.Thread(target=self.accepter)
accepter.daemon = True
accepter.start()
try: try:
while 1: while not self.stop_event.is_set():
try: self.stop_event.wait(1)
c = self.listener.accept()
except (OSError, IOError):
continue
t = threading.Thread(target=self.handle_request, args=(c,))
t.daemon = True
t.start()
except (KeyboardInterrupt, SystemExit): except (KeyboardInterrupt, SystemExit):
pass pass
finally: finally:
self.stop = 999 if sys.stdout != sys.__stdout__:
self.listener.close() util.debug('resetting stdout, stderr')
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
sys.exit(0)
def accepter(self):
while True:
try:
c = self.listener.accept()
except (OSError, IOError):
continue
t = threading.Thread(target=self.handle_request, args=(c,))
t.daemon = True
t.start()
def handle_request(self, c): def handle_request(self, c):
''' '''
...@@ -208,7 +218,7 @@ class Server(object): ...@@ -208,7 +218,7 @@ class Server(object):
send = conn.send send = conn.send
id_to_obj = self.id_to_obj id_to_obj = self.id_to_obj
while not self.stop: while not self.stop_event.is_set():
try: try:
methodname = obj = None methodname = obj = None
...@@ -318,32 +328,13 @@ class Server(object): ...@@ -318,32 +328,13 @@ class Server(object):
Shutdown this process Shutdown this process
''' '''
try: try:
try: util.debug('manager received shutdown message')
util.debug('manager received shutdown message') c.send(('#RETURN', None))
c.send(('#RETURN', None)) except:
import traceback
if sys.stdout != sys.__stdout__: traceback.print_exc()
util.debug('resetting stdout, stderr')
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
util._run_finalizers(0)
for p in active_children():
util.debug('terminating a child process of manager')
p.terminate()
for p in active_children():
util.debug('terminating a child process of manager')
p.join()
util._run_finalizers()
util.info('manager exiting with exitcode 0')
except:
import traceback
traceback.print_exc()
finally: finally:
exit(0) self.stop_event.set()
def create(self, c, typeid, *args, **kwds): def create(self, c, typeid, *args, **kwds):
''' '''
......
...@@ -269,21 +269,24 @@ _exiting = False ...@@ -269,21 +269,24 @@ _exiting = False
def _exit_function(): def _exit_function():
global _exiting global _exiting
info('process shutting down') if not _exiting:
debug('running all "atexit" finalizers with priority >= 0') _exiting = True
_run_finalizers(0)
for p in active_children(): info('process shutting down')
if p._daemonic: debug('running all "atexit" finalizers with priority >= 0')
info('calling terminate() for daemon %s', p.name) _run_finalizers(0)
p._popen.terminate()
for p in active_children(): for p in active_children():
info('calling join() for process %s', p.name) if p._daemonic:
p.join() info('calling terminate() for daemon %s', p.name)
p._popen.terminate()
debug('running the remaining "atexit" finalizers') for p in active_children():
_run_finalizers() info('calling join() for process %s', p.name)
p.join()
debug('running the remaining "atexit" finalizers')
_run_finalizers()
atexit.register(_exit_function) atexit.register(_exit_function)
......
...@@ -1593,7 +1593,7 @@ def strip_python_stderr(stderr): ...@@ -1593,7 +1593,7 @@ def strip_python_stderr(stderr):
This will typically be run on the result of the communicate() method This will typically be run on the result of the communicate() method
of a subprocess.Popen object. of a subprocess.Popen object.
""" """
stderr = re.sub(br"\[\d+ refs\]\r?\n?$", b"", stderr).strip() stderr = re.sub(br"\[\d+ refs\]\r?\n?", b"", stderr).strip()
return stderr return stderr
def args_from_interpreter_flags(): def args_from_interpreter_flags():
......
...@@ -1564,6 +1564,11 @@ class _TestMyManager(BaseTestCase): ...@@ -1564,6 +1564,11 @@ class _TestMyManager(BaseTestCase):
manager.shutdown() manager.shutdown()
# If the manager process exited cleanly then the exitcode
# will be zero. Otherwise (after a short timeout)
# terminate() is used, resulting in an exitcode of -SIGTERM.
self.assertEqual(manager._process.exitcode, 0)
# #
# Test of connecting to a remote server and using xmlrpclib for serialization # Test of connecting to a remote server and using xmlrpclib for serialization
# #
......
...@@ -21,6 +21,8 @@ Core and Builtins ...@@ -21,6 +21,8 @@ Core and Builtins
Library Library
------- -------
- Issue #13841: Make child processes exit using sys.exit() on Windows.
- Issue #14936: curses_panel was converted to PEP 3121 and PEP 384 API. - Issue #14936: curses_panel was converted to PEP 3121 and PEP 384 API.
Patch by Robin Schreiber. Patch by Robin Schreiber.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment