Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-1596321: Fix threading._shutdown() for the main thread #28549

Merged
merged 1 commit into from
Sep 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -1504,20 +1504,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()
gpshead marked this conversation as resolved.
Show resolved Hide resolved
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.