]> granicus.if.org Git - python/commitdiff
Issue #13841: Make child processes exit using sys.exit() on Windows
authorRichard Oudkerk <shibturn@gmail.com>
Thu, 14 Jun 2012 14:30:10 +0000 (15:30 +0100)
committerRichard Oudkerk <shibturn@gmail.com>
Thu, 14 Jun 2012 14:30:10 +0000 (15:30 +0100)
Lib/multiprocessing/forking.py
Lib/multiprocessing/managers.py
Lib/multiprocessing/util.py
Lib/test/support.py
Lib/test/test_multiprocessing.py
Misc/NEWS

index 3a474cd7d990f61e51a652c72ba578a892a1fc6c..4baf5486c0f3b05794d97b655e9521fbe3fbb4d0 100644 (file)
@@ -13,7 +13,7 @@ import signal
 
 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
@@ -75,7 +75,6 @@ else:
 #
 
 if sys.platform != 'win32':
-    exit = os._exit
     duplicate = os.dup
     close = os.close
 
@@ -168,7 +167,6 @@ else:
     WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False))
     WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
 
-    exit = _winapi.ExitProcess
     close = _winapi.CloseHandle
 
     #
@@ -349,7 +347,7 @@ else:
         from_parent.close()
 
         exitcode = self._bootstrap()
-        exit(exitcode)
+        sys.exit(exitcode)
 
 
     def get_preparation_data(name):
index 705909540005bec38847bee5d3282284af8a8ea3..817d2321d85568bfb6c0f55fdd1029bb239bc1fc 100644 (file)
@@ -22,7 +22,7 @@ import queue
 from traceback import format_exc
 from multiprocessing import Process, current_process, active_children, Pool, util, connection
 from multiprocessing.process import AuthenticationString
-from multiprocessing.forking import exit, Popen, ForkingPickler
+from multiprocessing.forking import Popen, ForkingPickler
 from time import time as _time
 
 #
@@ -140,28 +140,38 @@ class Server(object):
         self.id_to_obj = {'0': (None, ())}
         self.id_to_refcount = {}
         self.mutex = threading.RLock()
-        self.stop = 0
 
     def serve_forever(self):
         '''
         Run the server forever
         '''
+        self.stop_event = threading.Event()
         current_process()._manager_server = self
         try:
+            accepter = threading.Thread(target=self.accepter)
+            accepter.daemon = True
+            accepter.start()
             try:
-                while 1:
-                    try:
-                        c = self.listener.accept()
-                    except (OSError, IOError):
-                        continue
-                    t = threading.Thread(target=self.handle_request, args=(c,))
-                    t.daemon = True
-                    t.start()
+                while not self.stop_event.is_set():
+                    self.stop_event.wait(1)
             except (KeyboardInterrupt, SystemExit):
                 pass
         finally:
-            self.stop = 999
-            self.listener.close()
+            if sys.stdout != sys.__stdout__:
+                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):
         '''
@@ -208,7 +218,7 @@ class Server(object):
         send = conn.send
         id_to_obj = self.id_to_obj
 
-        while not self.stop:
+        while not self.stop_event.is_set():
 
             try:
                 methodname = obj = None
@@ -318,32 +328,13 @@ class Server(object):
         Shutdown this process
         '''
         try:
-            try:
-                util.debug('manager received shutdown message')
-                c.send(('#RETURN', None))
-
-                if sys.stdout != sys.__stdout__:
-                    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()
+            util.debug('manager received shutdown message')
+            c.send(('#RETURN', None))
+        except:
+            import traceback
+            traceback.print_exc()
         finally:
-            exit(0)
+            self.stop_event.set()
 
     def create(self, c, typeid, *args, **kwds):
         '''
index 48abe383fa4b3707dc9cb7b801a1005addb95103..8a6aede162a2388b4a7e7ce7d58e2c111c1423c5 100644 (file)
@@ -269,21 +269,24 @@ _exiting = False
 def _exit_function():
     global _exiting
 
-    info('process shutting down')
-    debug('running all "atexit" finalizers with priority >= 0')
-    _run_finalizers(0)
+    if not _exiting:
+        _exiting = True
 
-    for p in active_children():
-        if p._daemonic:
-            info('calling terminate() for daemon %s', p.name)
-            p._popen.terminate()
+        info('process shutting down')
+        debug('running all "atexit" finalizers with priority >= 0')
+        _run_finalizers(0)
 
-    for p in active_children():
-        info('calling join() for process %s', p.name)
-        p.join()
+        for p in active_children():
+            if p._daemonic:
+                info('calling terminate() for daemon %s', p.name)
+                p._popen.terminate()
 
-    debug('running the remaining "atexit" finalizers')
-    _run_finalizers()
+        for p in active_children():
+            info('calling join() for process %s', p.name)
+            p.join()
+
+        debug('running the remaining "atexit" finalizers')
+        _run_finalizers()
 
 atexit.register(_exit_function)
 
index 6749a5115e09fbd916c989483f86578329230d41..3ff1df51d19963ff680c8d7b0d2381ed0a8aabec 100644 (file)
@@ -1593,7 +1593,7 @@ def strip_python_stderr(stderr):
     This will typically be run on the result of the communicate() method
     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
 
 def args_from_interpreter_flags():
index 65e7b0be6ebea73c8cb5ab23681bd88f9b330690..e4ad904ed4d0e8c49b00f358122e798f853eb50e 100644 (file)
@@ -1564,6 +1564,11 @@ class _TestMyManager(BaseTestCase):
 
         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
 #
index bfb9f8015aca60fcdfced3dd0f7d52b48e40cc1e..058555f4452f800531979ad1e4d13f55aa5629c7 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -21,6 +21,8 @@ Core and Builtins
 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.
   Patch by Robin Schreiber.