]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-1596321: Fix threading._shutdown() for the main thread (GH-28549)
authorVictor Stinner <vstinner@python.org>
Mon, 27 Sep 2021 21:09:00 +0000 (23:09 +0200)
committerGitHub <noreply@github.com>
Mon, 27 Sep 2021 21:09:00 +0000 (23:09 +0200)
Fix the threading._shutdown() function when the threading module was
imported first from a thread different than the main thread: no
longer log an error at Python exit.

Lib/test/test_threading.py
Lib/threading.py
Misc/NEWS.d/next/Library/2021-09-24-17-20-23.bpo-1596321.3nhPUk.rst [new file with mode: 0644]

index f5ba16ea08ececd35db0761acb69f6ec5a1ae851..a8f3c139b24be15f601c80e1bb5d66c8d580c840 100644 (file)
@@ -928,6 +928,39 @@ class ThreadTests(BaseTestCase):
                b'is deprecated and will be removed in Python 3.12')
         self.assertIn(msg, err)
 
+    def test_import_from_another_thread(self):
+        # bpo-1596321: If the threading module is first import from a thread
+        # different than the main thread, threading._shutdown() must handle
+        # this case without logging an error at Python exit.
+        code = textwrap.dedent('''
+            import _thread
+            import sys
+
+            event = _thread.allocate_lock()
+            event.acquire()
+
+            def import_threading():
+                import threading
+                event.release()
+
+            if 'threading' in sys.modules:
+                raise Exception('threading is already imported')
+
+            _thread.start_new_thread(import_threading, ())
+
+            # wait until the threading module is imported
+            event.acquire()
+            event.release()
+
+            if 'threading' not in sys.modules:
+                raise Exception('threading is not imported')
+
+            # don't wait until the thread completes
+        ''')
+        rc, out, err = assert_python_ok("-c", code)
+        self.assertEqual(out, b'')
+        self.assertEqual(err, b'')
+
 
 class ThreadJoinOnShutdown(BaseTestCase):
 
index 1c74a8d81e35395511d70f98bd1d0de3ded7ed05..9b0419c296fcd84e08bc1c1a55f0ab0146c7c89e 100644 (file)
@@ -1517,20 +1517,29 @@ def _shutdown():
 
     global _SHUTTING_DOWN
     _SHUTTING_DOWN = True
-    # Main thread
-    tlock = _main_thread._tstate_lock
-    # The main thread isn't finished yet, so its thread state lock can't have
-    # been released.
-    assert tlock is not None
-    assert tlock.locked()
-    tlock.release()
-    _main_thread._stop()
 
     # Call registered threading atexit functions before threads are joined.
     # Order is reversed, similar to atexit.
     for atexit_call in reversed(_threading_atexits):
         atexit_call()
 
+    # Main thread
+    if _main_thread.ident == get_ident():
+        tlock = _main_thread._tstate_lock
+        # The main thread isn't finished yet, so its thread state lock can't
+        # have been released.
+        assert tlock is not None
+        assert tlock.locked()
+        tlock.release()
+        _main_thread._stop()
+    else:
+        # bpo-1596321: _shutdown() must be called in the main thread.
+        # If the threading module was not imported by the main thread,
+        # _main_thread is the thread which imported the threading module.
+        # In this case, ignore _main_thread, similar behavior than for threads
+        # spawned by C libraries or using _thread.start_new_thread().
+        pass
+
     # Join all non-deamon threads
     while True:
         with _shutdown_locks_lock:
diff --git a/Misc/NEWS.d/next/Library/2021-09-24-17-20-23.bpo-1596321.3nhPUk.rst b/Misc/NEWS.d/next/Library/2021-09-24-17-20-23.bpo-1596321.3nhPUk.rst
new file mode 100644 (file)
index 0000000..61a3e5a
--- /dev/null
@@ -0,0 +1,3 @@
+Fix the :func:`threading._shutdown` function when the :mod:`threading` module
+was imported first from a thread different than the main thread: no longer log
+an error at Python exit.