[Python-checkins] bpo-42350: Fix Thread._reset_internal_locks() (GH-23268)

miss-islington webhook-mailer at python.org
Mon Nov 16 10:17:21 EST 2020


https://github.com/python/cpython/commit/cf70854f10096446115408b5a94030b30594a459
commit: cf70854f10096446115408b5a94030b30594a459
branch: 3.9
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: miss-islington <31488909+miss-islington at users.noreply.github.com>
date: 2020-11-16T07:17:17-08:00
summary:

bpo-42350: Fix Thread._reset_internal_locks() (GH-23268)


Fix the threading.Thread class at fork: do nothing if the thread is
already stopped (ex: fork called at Python exit). Previously, an
error was logged in the child process.
(cherry picked from commit 5909a494cd3ba43143b28bd439773ed85a485dfc)

Co-authored-by: Victor Stinner <vstinner at python.org>

files:
A Misc/NEWS.d/next/Library/2020-11-13-18-53-50.bpo-42350.rsql7V.rst
M Lib/test/test_threading.py
M Lib/threading.py

diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py
index 81e5f70d6d6ae..c21cdf8eb7be9 100644
--- a/Lib/test/test_threading.py
+++ b/Lib/test/test_threading.py
@@ -439,6 +439,35 @@ def test_daemon_param(self):
         t = threading.Thread(daemon=True)
         self.assertTrue(t.daemon)
 
+    @unittest.skipUnless(hasattr(os, 'fork'), 'needs os.fork()')
+    def test_fork_at_exit(self):
+        # bpo-42350: Calling os.fork() after threading._shutdown() must
+        # not log an error.
+        code = textwrap.dedent("""
+            import atexit
+            import os
+            import sys
+            from test.support import wait_process
+
+            # Import the threading module to register its "at fork" callback
+            import threading
+
+            def exit_handler():
+                pid = os.fork()
+                if not pid:
+                    print("child process ok", file=sys.stderr, flush=True)
+                    # child process
+                    sys.exit()
+                else:
+                    wait_process(pid, exitcode=0)
+
+            # exit_handler() will be called after threading._shutdown()
+            atexit.register(exit_handler)
+        """)
+        _, out, err = assert_python_ok("-c", code)
+        self.assertEqual(out, b'')
+        self.assertEqual(err.rstrip(), b'child process ok')
+
     @unittest.skipUnless(hasattr(os, 'fork'), 'test needs fork()')
     def test_dummy_thread_after_fork(self):
         # Issue #14308: a dummy thread in the active list doesn't mess up
diff --git a/Lib/threading.py b/Lib/threading.py
index ab29db77a747a..d96d99a713e90 100644
--- a/Lib/threading.py
+++ b/Lib/threading.py
@@ -826,8 +826,12 @@ def _reset_internal_locks(self, is_alive):
         # they may be in an invalid state leading to a deadlock or crash.
         self._started._at_fork_reinit()
         if is_alive:
-            self._tstate_lock._at_fork_reinit()
-            self._tstate_lock.acquire()
+            # bpo-42350: If the fork happens when the thread is already stopped
+            # (ex: after threading._shutdown() has been called), _tstate_lock
+            # is None. Do nothing in this case.
+            if self._tstate_lock is not None:
+                self._tstate_lock._at_fork_reinit()
+                self._tstate_lock.acquire()
         else:
             # The thread isn't alive after fork: it doesn't have a tstate
             # anymore.
diff --git a/Misc/NEWS.d/next/Library/2020-11-13-18-53-50.bpo-42350.rsql7V.rst b/Misc/NEWS.d/next/Library/2020-11-13-18-53-50.bpo-42350.rsql7V.rst
new file mode 100644
index 0000000000000..090ea2266633e
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-11-13-18-53-50.bpo-42350.rsql7V.rst
@@ -0,0 +1,3 @@
+Fix the :class:`threading.Thread` class at fork: do nothing if the thread is
+already stopped (ex: fork called at Python exit). Previously, an error was
+logged in the child process.



More information about the Python-checkins mailing list