bpo-40091: Fix a hang at fork in the logging module (GH-19416)
Fix a hang at fork in the logging module: the new private _at_fork_reinit() method is now used to reinitialize locks at fork in the child process. The createLock() method is no longer used at fork.
This commit is contained in:
parent
25a6833f79
commit
4c3da783cf
|
@ -234,11 +234,9 @@ if not hasattr(os, 'register_at_fork'): # Windows and friends.
|
||||||
def _register_at_fork_reinit_lock(instance):
|
def _register_at_fork_reinit_lock(instance):
|
||||||
pass # no-op when os.register_at_fork does not exist.
|
pass # no-op when os.register_at_fork does not exist.
|
||||||
else:
|
else:
|
||||||
# A collection of instances with a createLock method (logging.Handler)
|
# A collection of instances with a _at_fork_reinit method (logging.Handler)
|
||||||
# to be called in the child after forking. The weakref avoids us keeping
|
# to be called in the child after forking. The weakref avoids us keeping
|
||||||
# discarded Handler instances alive. A set is used to avoid accumulating
|
# discarded Handler instances alive.
|
||||||
# duplicate registrations as createLock() is responsible for registering
|
|
||||||
# a new Handler instance with this set in the first place.
|
|
||||||
_at_fork_reinit_lock_weakset = weakref.WeakSet()
|
_at_fork_reinit_lock_weakset = weakref.WeakSet()
|
||||||
|
|
||||||
def _register_at_fork_reinit_lock(instance):
|
def _register_at_fork_reinit_lock(instance):
|
||||||
|
@ -249,16 +247,12 @@ else:
|
||||||
_releaseLock()
|
_releaseLock()
|
||||||
|
|
||||||
def _after_at_fork_child_reinit_locks():
|
def _after_at_fork_child_reinit_locks():
|
||||||
# _acquireLock() was called in the parent before forking.
|
|
||||||
for handler in _at_fork_reinit_lock_weakset:
|
for handler in _at_fork_reinit_lock_weakset:
|
||||||
try:
|
handler._at_fork_reinit()
|
||||||
handler.createLock()
|
|
||||||
except Exception as err:
|
|
||||||
# Similar to what PyErr_WriteUnraisable does.
|
|
||||||
print("Ignoring exception from logging atfork", instance,
|
|
||||||
"._reinit_lock() method:", err, file=sys.stderr)
|
|
||||||
_releaseLock() # Acquired by os.register_at_fork(before=.
|
|
||||||
|
|
||||||
|
# _acquireLock() was called in the parent before forking.
|
||||||
|
# The lock is reinitialized to unlocked state.
|
||||||
|
_lock._at_fork_reinit()
|
||||||
|
|
||||||
os.register_at_fork(before=_acquireLock,
|
os.register_at_fork(before=_acquireLock,
|
||||||
after_in_child=_after_at_fork_child_reinit_locks,
|
after_in_child=_after_at_fork_child_reinit_locks,
|
||||||
|
@ -891,6 +885,9 @@ class Handler(Filterer):
|
||||||
self.lock = threading.RLock()
|
self.lock = threading.RLock()
|
||||||
_register_at_fork_reinit_lock(self)
|
_register_at_fork_reinit_lock(self)
|
||||||
|
|
||||||
|
def _at_fork_reinit(self):
|
||||||
|
self.lock._at_fork_reinit()
|
||||||
|
|
||||||
def acquire(self):
|
def acquire(self):
|
||||||
"""
|
"""
|
||||||
Acquire the I/O thread lock.
|
Acquire the I/O thread lock.
|
||||||
|
@ -2168,6 +2165,9 @@ class NullHandler(Handler):
|
||||||
def createLock(self):
|
def createLock(self):
|
||||||
self.lock = None
|
self.lock = None
|
||||||
|
|
||||||
|
def _at_fork_reinit(self):
|
||||||
|
pass
|
||||||
|
|
||||||
# Warnings integration
|
# Warnings integration
|
||||||
|
|
||||||
_warnings_showwarning = None
|
_warnings_showwarning = None
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix a hang at fork in the logging module: the new private _at_fork_reinit()
|
||||||
|
method is now used to reinitialize locks at fork in the child process.
|
Loading…
Reference in New Issue