]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-1596321: Fix threading._shutdown() for the main thread (GH-28549) (GH-28589)
authorVictor Stinner <vstinner@python.org>
Mon, 27 Sep 2021 21:40:22 +0000 (23:40 +0200)
committerGitHub <noreply@github.com>
Mon, 27 Sep 2021 21:40:22 +0000 (23:40 +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.

(cherry picked from commit 95d31370829b7d729667588e0a9943217401ea5b)

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 a57085b75d58adf7be0927836b736d26ef574b6b..af480b9014ed99d228adf5cf820e9ea64da98acf 100644 (file)
@@ -814,6 +814,39 @@ class ThreadTests(BaseTestCase):
             threading.Thread(target=noop).start()
             # Thread.join() is not called
 
+    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 1c98b3b2cdf46b303e9982895e74f2b15ddbbbbf..448f152600c53490072446a747f408b4cc549784 100644 (file)
@@ -1433,20 +1433,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.