mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
gh-105716: Support Background Threads in Subinterpreters Consistently (gh-109921)
The existence of background threads running on a subinterpreter was preventing interpreters from getting properly destroyed, as well as impacting the ability to run the interpreter again. It also affected how we wait for non-daemon threads to finish. We add PyInterpreterState.threads.main, with some internal C-API functions.
This commit is contained in:
parent
a040a32ea2
commit
1dd9dee45d
11 changed files with 258 additions and 46 deletions
|
@ -26,6 +26,11 @@ from unittest import mock
|
|||
from test import lock_tests
|
||||
from test import support
|
||||
|
||||
try:
|
||||
from test.support import interpreters
|
||||
except ModuleNotFoundError:
|
||||
interpreters = None
|
||||
|
||||
threading_helper.requires_working_threading(module=True)
|
||||
|
||||
# Between fork() and exec(), only async-safe functions are allowed (issues
|
||||
|
@ -52,6 +57,12 @@ def skip_unless_reliable_fork(test):
|
|||
return test
|
||||
|
||||
|
||||
def requires_subinterpreters(meth):
|
||||
"""Decorator to skip a test if subinterpreters are not supported."""
|
||||
return unittest.skipIf(interpreters is None,
|
||||
'subinterpreters required')(meth)
|
||||
|
||||
|
||||
def restore_default_excepthook(testcase):
|
||||
testcase.addCleanup(setattr, threading, 'excepthook', threading.excepthook)
|
||||
threading.excepthook = threading.__excepthook__
|
||||
|
@ -1311,6 +1322,44 @@ class SubinterpThreadingTests(BaseTestCase):
|
|||
# The thread was joined properly.
|
||||
self.assertEqual(os.read(r, 1), b"x")
|
||||
|
||||
@requires_subinterpreters
|
||||
def test_threads_join_with_no_main(self):
|
||||
r_interp, w_interp = self.pipe()
|
||||
|
||||
INTERP = b'I'
|
||||
FINI = b'F'
|
||||
DONE = b'D'
|
||||
|
||||
interp = interpreters.create()
|
||||
interp.run(f"""if True:
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
|
||||
done = False
|
||||
|
||||
def notify_fini():
|
||||
global done
|
||||
done = True
|
||||
os.write({w_interp}, {FINI!r})
|
||||
t.join()
|
||||
threading._register_atexit(notify_fini)
|
||||
|
||||
def task():
|
||||
while not done:
|
||||
time.sleep(0.1)
|
||||
os.write({w_interp}, {DONE!r})
|
||||
t = threading.Thread(target=task)
|
||||
t.start()
|
||||
|
||||
os.write({w_interp}, {INTERP!r})
|
||||
""")
|
||||
interp.close()
|
||||
|
||||
self.assertEqual(os.read(r_interp, 1), INTERP)
|
||||
self.assertEqual(os.read(r_interp, 1), FINI)
|
||||
self.assertEqual(os.read(r_interp, 1), DONE)
|
||||
|
||||
@cpython_only
|
||||
def test_daemon_threads_fatal_error(self):
|
||||
subinterp_code = f"""if 1:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue