Skip to content

Commit

Permalink
bpo-1596321: Fix threading._shutdown() for the main thread (pythonGH-…
Browse files Browse the repository at this point in the history
…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>
  • Loading branch information
vstinner authored and miss-islington committed Sep 27, 2021
1 parent 9e209d4 commit e3b5a5f
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 8 deletions.
33 changes: 33 additions & 0 deletions Lib/test/test_threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -928,6 +928,39 @@ def test_debug_deprecation(self):
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):

Expand Down
25 changes: 17 additions & 8 deletions Lib/threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -1523,20 +1523,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:
Expand Down
Original file line number Diff line number Diff line change
@@ -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.

0 comments on commit e3b5a5f

Please sign in to comment.