Skip to content

Commit 38c6773

Browse files
bpo-1596321: Fix threading._shutdown() for the main thread (pythonGH-28549)
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 95d3137) Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent c7fdd68 commit 38c6773

File tree

3 files changed

+53
-8
lines changed

3 files changed

+53
-8
lines changed

Lib/test/test_threading.py

+33
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,39 @@ def test_debug_deprecation(self):
928928
b'is deprecated and will be removed in Python 3.12')
929929
self.assertIn(msg, err)
930930

931+
def test_import_from_another_thread(self):
932+
# bpo-1596321: If the threading module is first import from a thread
933+
# different than the main thread, threading._shutdown() must handle
934+
# this case without logging an error at Python exit.
935+
code = textwrap.dedent('''
936+
import _thread
937+
import sys
938+
939+
event = _thread.allocate_lock()
940+
event.acquire()
941+
942+
def import_threading():
943+
import threading
944+
event.release()
945+
946+
if 'threading' in sys.modules:
947+
raise Exception('threading is already imported')
948+
949+
_thread.start_new_thread(import_threading, ())
950+
951+
# wait until the threading module is imported
952+
event.acquire()
953+
event.release()
954+
955+
if 'threading' not in sys.modules:
956+
raise Exception('threading is not imported')
957+
958+
# don't wait until the thread completes
959+
''')
960+
rc, out, err = assert_python_ok("-c", code)
961+
self.assertEqual(out, b'')
962+
self.assertEqual(err, b'')
963+
931964

932965
class ThreadJoinOnShutdown(BaseTestCase):
933966

Lib/threading.py

+17-8
Original file line numberDiff line numberDiff line change
@@ -1523,20 +1523,29 @@ def _shutdown():
15231523

15241524
global _SHUTTING_DOWN
15251525
_SHUTTING_DOWN = True
1526-
# Main thread
1527-
tlock = _main_thread._tstate_lock
1528-
# The main thread isn't finished yet, so its thread state lock can't have
1529-
# been released.
1530-
assert tlock is not None
1531-
assert tlock.locked()
1532-
tlock.release()
1533-
_main_thread._stop()
15341526

15351527
# Call registered threading atexit functions before threads are joined.
15361528
# Order is reversed, similar to atexit.
15371529
for atexit_call in reversed(_threading_atexits):
15381530
atexit_call()
15391531

1532+
# Main thread
1533+
if _main_thread.ident == get_ident():
1534+
tlock = _main_thread._tstate_lock
1535+
# The main thread isn't finished yet, so its thread state lock can't
1536+
# have been released.
1537+
assert tlock is not None
1538+
assert tlock.locked()
1539+
tlock.release()
1540+
_main_thread._stop()
1541+
else:
1542+
# bpo-1596321: _shutdown() must be called in the main thread.
1543+
# If the threading module was not imported by the main thread,
1544+
# _main_thread is the thread which imported the threading module.
1545+
# In this case, ignore _main_thread, similar behavior than for threads
1546+
# spawned by C libraries or using _thread.start_new_thread().
1547+
pass
1548+
15401549
# Join all non-deamon threads
15411550
while True:
15421551
with _shutdown_locks_lock:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix the :func:`threading._shutdown` function when the :mod:`threading` module
2+
was imported first from a thread different than the main thread: no longer log
3+
an error at Python exit.

0 commit comments

Comments
 (0)