Skip to content

Commit 94d19f6

Browse files
authored
bpo-1596321: Fix threading._shutdown() for the main thread (pythonGH-28549) (pythonGH-28589)
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)
1 parent 1ecb641 commit 94d19f6

File tree

3 files changed

+53
-8
lines changed

3 files changed

+53
-8
lines changed

Lib/test/test_threading.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,39 @@ def noop(): pass
814814
threading.Thread(target=noop).start()
815815
# Thread.join() is not called
816816

817+
def test_import_from_another_thread(self):
818+
# bpo-1596321: If the threading module is first import from a thread
819+
# different than the main thread, threading._shutdown() must handle
820+
# this case without logging an error at Python exit.
821+
code = textwrap.dedent('''
822+
import _thread
823+
import sys
824+
825+
event = _thread.allocate_lock()
826+
event.acquire()
827+
828+
def import_threading():
829+
import threading
830+
event.release()
831+
832+
if 'threading' in sys.modules:
833+
raise Exception('threading is already imported')
834+
835+
_thread.start_new_thread(import_threading, ())
836+
837+
# wait until the threading module is imported
838+
event.acquire()
839+
event.release()
840+
841+
if 'threading' not in sys.modules:
842+
raise Exception('threading is not imported')
843+
844+
# don't wait until the thread completes
845+
''')
846+
rc, out, err = assert_python_ok("-c", code)
847+
self.assertEqual(out, b'')
848+
self.assertEqual(err, b'')
849+
817850

818851
class ThreadJoinOnShutdown(BaseTestCase):
819852

Lib/threading.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,20 +1433,29 @@ def _shutdown():
14331433

14341434
global _SHUTTING_DOWN
14351435
_SHUTTING_DOWN = True
1436-
# Main thread
1437-
tlock = _main_thread._tstate_lock
1438-
# The main thread isn't finished yet, so its thread state lock can't have
1439-
# been released.
1440-
assert tlock is not None
1441-
assert tlock.locked()
1442-
tlock.release()
1443-
_main_thread._stop()
14441436

14451437
# Call registered threading atexit functions before threads are joined.
14461438
# Order is reversed, similar to atexit.
14471439
for atexit_call in reversed(_threading_atexits):
14481440
atexit_call()
14491441

1442+
# Main thread
1443+
if _main_thread.ident == get_ident():
1444+
tlock = _main_thread._tstate_lock
1445+
# The main thread isn't finished yet, so its thread state lock can't
1446+
# have been released.
1447+
assert tlock is not None
1448+
assert tlock.locked()
1449+
tlock.release()
1450+
_main_thread._stop()
1451+
else:
1452+
# bpo-1596321: _shutdown() must be called in the main thread.
1453+
# If the threading module was not imported by the main thread,
1454+
# _main_thread is the thread which imported the threading module.
1455+
# In this case, ignore _main_thread, similar behavior than for threads
1456+
# spawned by C libraries or using _thread.start_new_thread().
1457+
pass
1458+
14501459
# Join all non-deamon threads
14511460
while True:
14521461
with _shutdown_locks_lock:
Lines changed: 3 additions & 0 deletions
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)