Skip to content

bpo-40091: Fix a hang at fork in the logging module #19416

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.