mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Issue #13841: Make child processes exit using sys.exit() on Windows
This commit is contained in:
parent
bc07cb883e
commit
73d9a292ae
6 changed files with 54 additions and 55 deletions
|
@ -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:
|
||||||
#
|
#
|
||||||
|
|
||||||
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:
|
||||||
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:
|
||||||
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
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
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
|
||||||
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):
|
||||||
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):
|
||||||
|
|
||||||
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
|
||||||
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.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue