diff options
author | Alexander Belopolsky <alexander.belopolsky@gmail.com> | 2012-09-09 17:20:58 (GMT) |
---|---|---|
committer | Alexander Belopolsky <alexander.belopolsky@gmail.com> | 2012-09-09 17:20:58 (GMT) |
commit | f36c49d124af3cbe97ff5e1dd2a4e1a85f2575d2 (patch) | |
tree | 73d6da1ed30aebd68d3a0fc3ce39d7c40237b968 | |
parent | 5497295917ec9ea054eb3e6daf0c06060901cc7e (diff) | |
download | cpython-f36c49d124af3cbe97ff5e1dd2a4e1a85f2575d2.zip cpython-f36c49d124af3cbe97ff5e1dd2a4e1a85f2575d2.tar.gz cpython-f36c49d124af3cbe97ff5e1dd2a4e1a85f2575d2.tar.bz2 |
Issue #15881: Fixed atexit hook in multiprocessing.
-rw-r--r-- | Doc/ACKS.txt | 1 | ||||
-rw-r--r-- | Lib/multiprocessing/util.py | 41 |
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() |