Skip to content

Commit

Permalink
bpo-40091: Fix a hang at fork in the logging module (GH-19416)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
vstinner authored Apr 13, 2020
1 parent 25a6833 commit 4c3da78
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 12 deletions.
24 changes: 12 additions & 12 deletions Lib/logging/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,11 +234,9 @@ def _releaseLock():
def _register_at_fork_reinit_lock(instance):
pass # no-op when os.register_at_fork does not exist.
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
# discarded Handler instances alive. A set is used to avoid accumulating
# duplicate registrations as createLock() is responsible for registering
# a new Handler instance with this set in the first place.
# discarded Handler instances alive.
_at_fork_reinit_lock_weakset = weakref.WeakSet()

def _register_at_fork_reinit_lock(instance):
Expand All @@ -249,16 +247,12 @@ def _register_at_fork_reinit_lock(instance):
_releaseLock()

def _after_at_fork_child_reinit_locks():
# _acquireLock() was called in the parent before forking.
for handler in _at_fork_reinit_lock_weakset:
try:
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=.
handler._at_fork_reinit()

# _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,
after_in_child=_after_at_fork_child_reinit_locks,
Expand Down Expand Up @@ -891,6 +885,9 @@ def createLock(self):
self.lock = threading.RLock()
_register_at_fork_reinit_lock(self)

def _at_fork_reinit(self):
self.lock._at_fork_reinit()

def acquire(self):
"""
Acquire the I/O thread lock.
Expand Down Expand Up @@ -2168,6 +2165,9 @@ def emit(self, record):
def createLock(self):
self.lock = None

def _at_fork_reinit(self):
pass

# Warnings integration

_warnings_showwarning = None
Expand Down
Original file line number Diff line number Diff line change
@@ -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.

0 comments on commit 4c3da78

Please sign in to comment.