]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-40091: Fix a hang at fork in the logging module (GH-19416)
authorVictor Stinner <vstinner@python.org>
Mon, 13 Apr 2020 22:25:34 +0000 (00:25 +0200)
committerGitHub <noreply@github.com>
Mon, 13 Apr 2020 22:25:34 +0000 (00:25 +0200)
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.

Lib/logging/__init__.py
Misc/NEWS.d/next/Library/2020-04-07-23-26-25.bpo-40091.5M9AW5.rst [new file with mode: 0644]

index 59d5fa5b64d2232f37e422f4acc739df3ce6da27..84a177559908a2c31aba8488dd1db73f5b4f78d1 100644 (file)
@@ -234,11 +234,9 @@ if not hasattr(os, 'register_at_fork'):  # Windows and friends.
     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):
@@ -249,16 +247,12 @@ else:
             _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,
@@ -891,6 +885,9 @@ class Handler(Filterer):
         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.
@@ -2168,6 +2165,9 @@ class NullHandler(Handler):
     def createLock(self):
         self.lock = None
 
+    def _at_fork_reinit(self):
+        pass
+
 # Warnings integration
 
 _warnings_showwarning = None
diff --git a/Misc/NEWS.d/next/Library/2020-04-07-23-26-25.bpo-40091.5M9AW5.rst b/Misc/NEWS.d/next/Library/2020-04-07-23-26-25.bpo-40091.5M9AW5.rst
new file mode 100644 (file)
index 0000000..4a98aa5
--- /dev/null
@@ -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.