summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Belopolsky <alexander.belopolsky@gmail.com>2012-09-09 17:20:58 (GMT)
committerAlexander Belopolsky <alexander.belopolsky@gmail.com>2012-09-09 17:20:58 (GMT)
commitf36c49d124af3cbe97ff5e1dd2a4e1a85f2575d2 (patch)
tree73d6da1ed30aebd68d3a0fc3ce39d7c40237b968
parent5497295917ec9ea054eb3e6daf0c06060901cc7e (diff)
downloadcpython-f36c49d124af3cbe97ff5e1dd2a4e1a85f2575d2.zip
cpython-f36c49d124af3cbe97ff5e1dd2a4e1a85f2575d2.tar.gz
cpython-f36c49d124af3cbe97ff5e1dd2a4e1a85f2575d2.tar.bz2
Issue #15881: Fixed atexit hook in multiprocessing.
-rw-r--r--Doc/ACKS.txt1
-rw-r--r--Lib/multiprocessing/util.py41
2 files changed, 33 insertions, 9 deletions
diff --git a/Doc/ACKS.txt b/Doc/ACKS.txt
index 68aff56..a063827 100644
--- a/Doc/ACKS.txt
+++ b/Doc/ACKS.txt
@@ -238,3 +238,4 @@ docs@python.org), and we'll be glad to correct the problem.
* Moshe Zadka
* Milan Zamazal
* Cheng Zhang
+ * Chris McDonough
diff --git a/Lib/multiprocessing/util.py b/Lib/multiprocessing/util.py
index 8a6aede..bc2d656 100644
--- a/Lib/multiprocessing/util.py
+++ b/Lib/multiprocessing/util.py
@@ -235,6 +235,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:
@@ -266,7 +272,13 @@ def is_exiting():
_exiting = False
-def _exit_function():
+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.
+
global _exiting
if not _exiting:
@@ -276,14 +288,25 @@ def _exit_function():
debug('running all "atexit" finalizers with priority >= 0')
_run_finalizers(0)
- 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()
+ 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()