]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Issue #15881: Fixed atexit hook in multiprocessing.
authorAlexander Belopolsky <alexander.belopolsky@gmail.com>
Sun, 9 Sep 2012 17:16:15 +0000 (13:16 -0400)
committerAlexander Belopolsky <alexander.belopolsky@gmail.com>
Sun, 9 Sep 2012 17:16:15 +0000 (13:16 -0400)
Doc/ACKS.txt
Lib/multiprocessing/util.py

index 26f7ea907f659e77fbff16167a8ea03118589fc5..69735ca06c12c2f398e5a9d264f11fba65c0a6de 100644 (file)
@@ -236,3 +236,4 @@ docs@python.org), and we'll be glad to correct the problem.
    * Moshe Zadka
    * Milan Zamazal
    * Cheng Zhang
+   * Chris McDonough
index 30b7a85fa2653e47fc79898507027ff64e3e650c..68d6c1f9c4a13ef313d71174c4537195b1f801a3 100644 (file)
@@ -247,6 +247,12 @@ def _run_finalizers(minpriority=None):
     Finalizers with highest priority are called first; finalizers with
     the same priority will be called in reverse order of creation.
     '''
+    if _finalizer_registry is None:
+        # This function may be called after this module's globals are
+        # destroyed.  See the _exit_function function in this module for more
+        # notes.
+        return
+    
     if minpriority is None:
         f = lambda p : p[0][0] is not None
     else:
@@ -278,24 +284,40 @@ def is_exiting():
 
 _exiting = False
 
-def _exit_function():
-    global _exiting
-
-    info('process shutting down')
-    debug('running all "atexit" finalizers with priority >= 0')
-    _run_finalizers(0)
+def _exit_function(info=info, debug=debug, _run_finalizers=_run_finalizers,
+                   active_children=active_children,
+                   current_process=current_process):
+    # We hold on to references to functions in the arglist due to the
+    # situation described below, where this function is called after this
+    # module's globals are destroyed.
 
-    for p in active_children():
-        if p._daemonic:
-            info('calling terminate() for daemon %s', p.name)
-            p._popen.terminate()
-
-    for p in active_children():
-        info('calling join() for process %s', p.name)
-        p.join()
+    global _exiting
 
-    debug('running the remaining "atexit" finalizers')
-    _run_finalizers()
+    if not _exiting:
+        info('process shutting down')
+        debug('running all "atexit" finalizers with priority >= 0')
+        _run_finalizers(0)
+        if current_process() is not None:
+            # We check if the current process is None here because if
+            # it's None, any call to ``active_children()`` will throw an
+            # AttributeError (active_children winds up trying to get
+            # attributes from util._current_process).  This happens in a
+            # variety of shutdown circumstances that are not well-understood
+            # because module-scope variables are not apparently supposed to
+            # be destroyed until after this function is called.  However,
+            # they are indeed destroyed before this function is called.  See
+            # issues #9775 and #15881.  Also related: #4106, #9205, and #9207.
+            for p in active_children():
+                if p._daemonic:
+                    info('calling terminate() for daemon %s', p.name)
+                    p._popen.terminate()
+
+            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)