aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/test/test_threading.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test/test_threading.py')
-rw-r--r--Lib/test/test_threading.py112
1 files changed, 99 insertions, 13 deletions
diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py
index 814c00ca0fd..00a3037c3e1 100644
--- a/Lib/test/test_threading.py
+++ b/Lib/test/test_threading.py
@@ -5,7 +5,7 @@ Tests for the threading module.
import test.support
from test.support import threading_helper, requires_subprocess, requires_gil_enabled
from test.support import verbose, cpython_only, os_helper
-from test.support.import_helper import import_module
+from test.support.import_helper import ensure_lazy_imports, import_module
from test.support.script_helper import assert_python_ok, assert_python_failure
from test.support import force_not_colorized
@@ -28,7 +28,7 @@ from test import lock_tests
from test import support
try:
- from test.support import interpreters
+ from concurrent import interpreters
except ImportError:
interpreters = None
@@ -121,6 +121,10 @@ class ThreadTests(BaseTestCase):
maxDiff = 9999
@cpython_only
+ def test_lazy_import(self):
+ ensure_lazy_imports("threading", {"functools", "warnings"})
+
+ @cpython_only
def test_name(self):
def func(): pass
@@ -526,7 +530,8 @@ class ThreadTests(BaseTestCase):
finally:
sys.setswitchinterval(old_interval)
- def test_join_from_multiple_threads(self):
+ @support.bigmemtest(size=20, memuse=72*2**20, dry_run=False)
+ def test_join_from_multiple_threads(self, size):
# Thread.join() should be thread-safe
errors = []
@@ -1242,13 +1247,68 @@ class ThreadTests(BaseTestCase):
self.assertEqual(err, b"")
self.assertIn(b"all clear", out)
+ @support.subTests('lock_class_name', ['Lock', 'RLock'])
+ def test_acquire_daemon_thread_lock_in_finalization(self, lock_class_name):
+ # gh-123940: Py_Finalize() prevents other threads from running Python
+ # code (and so, releasing locks), so acquiring a locked lock can not
+ # succeed.
+ # We raise an exception rather than hang.
+ code = textwrap.dedent(f"""
+ import threading
+ import time
+
+ thread_started_event = threading.Event()
+
+ lock = threading.{lock_class_name}()
+ def loop():
+ if {lock_class_name!r} == 'RLock':
+ lock.acquire()
+ with lock:
+ thread_started_event.set()
+ while True:
+ time.sleep(1)
+
+ uncontested_lock = threading.{lock_class_name}()
+
+ class Cycle:
+ def __init__(self):
+ self.self_ref = self
+ self.thr = threading.Thread(
+ target=loop, daemon=True)
+ self.thr.start()
+ thread_started_event.wait()
+
+ def __del__(self):
+ assert self.thr.is_alive()
+
+ # We *can* acquire an unlocked lock
+ uncontested_lock.acquire()
+ if {lock_class_name!r} == 'RLock':
+ uncontested_lock.acquire()
+
+ # Acquiring a locked one fails
+ try:
+ lock.acquire()
+ except PythonFinalizationError:
+ assert self.thr.is_alive()
+ print('got the correct exception!')
+
+ # Cycle holds a reference to itself, which ensures it is
+ # cleaned up during the GC that runs after daemon threads
+ # have been forced to exit during finalization.
+ Cycle()
+ """)
+ rc, out, err = assert_python_ok("-c", code)
+ self.assertEqual(err, b"")
+ self.assertIn(b"got the correct exception", out)
+
def test_start_new_thread_failed(self):
# gh-109746: if Python fails to start newly created thread
# due to failure of underlying PyThread_start_new_thread() call,
# its state should be removed from interpreter' thread states list
# to avoid its double cleanup
try:
- from resource import setrlimit, RLIMIT_NPROC
+ from resource import setrlimit, RLIMIT_NPROC # noqa: F401
except ImportError as err:
self.skipTest(err) # RLIMIT_NPROC is specific to Linux and BSD
code = """if 1:
@@ -1279,12 +1339,6 @@ class ThreadTests(BaseTestCase):
@cpython_only
def test_finalize_daemon_thread_hang(self):
- if support.check_sanitizer(thread=True, memory=True):
- # the thread running `time.sleep(100)` below will still be alive
- # at process exit
- self.skipTest(
- "https://github.com/python/cpython/issues/124878 - Known"
- " race condition that TSAN identifies.")
# gh-87135: tests that daemon threads hang during finalization
script = textwrap.dedent('''
import os
@@ -1347,6 +1401,35 @@ class ThreadTests(BaseTestCase):
''')
assert_python_ok("-c", script)
+ @skip_unless_reliable_fork
+ @unittest.skipUnless(hasattr(threading, 'get_native_id'), "test needs threading.get_native_id()")
+ def test_native_id_after_fork(self):
+ script = """if True:
+ import threading
+ import os
+ from test import support
+
+ parent_thread_native_id = threading.current_thread().native_id
+ print(parent_thread_native_id, flush=True)
+ assert parent_thread_native_id == threading.get_native_id()
+ childpid = os.fork()
+ if childpid == 0:
+ print(threading.current_thread().native_id, flush=True)
+ assert threading.current_thread().native_id == threading.get_native_id()
+ else:
+ try:
+ assert parent_thread_native_id == threading.current_thread().native_id
+ assert parent_thread_native_id == threading.get_native_id()
+ finally:
+ support.wait_process(childpid, exitcode=0)
+ """
+ rc, out, err = assert_python_ok('-c', script)
+ self.assertEqual(rc, 0)
+ self.assertEqual(err, b"")
+ native_ids = out.strip().splitlines()
+ self.assertEqual(len(native_ids), 2)
+ self.assertNotEqual(native_ids[0], native_ids[1])
+
class ThreadJoinOnShutdown(BaseTestCase):
def _run_and_join(self, script):
@@ -1427,7 +1510,8 @@ class ThreadJoinOnShutdown(BaseTestCase):
self._run_and_join(script)
@unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug")
- def test_4_daemon_threads(self):
+ @support.bigmemtest(size=40, memuse=70*2**20, dry_run=False)
+ def test_4_daemon_threads(self, size):
# Check that a daemon thread cannot crash the interpreter on shutdown
# by manipulating internal structures that are being disposed of in
# the main thread.
@@ -2131,8 +2215,7 @@ class CRLockTests(lock_tests.RLockTests):
]
for args, kwargs in arg_types:
with self.subTest(args=args, kwargs=kwargs):
- with self.assertWarns(DeprecationWarning):
- threading.RLock(*args, **kwargs)
+ self.assertRaises(TypeError, threading.RLock, *args, **kwargs)
# Subtypes with custom `__init__` are allowed (but, not recommended):
class CustomRLock(self.locktype):
@@ -2150,6 +2233,9 @@ class ConditionAsRLockTests(lock_tests.RLockTests):
# Condition uses an RLock by default and exports its API.
locktype = staticmethod(threading.Condition)
+ def test_constructor_noargs(self):
+ self.skipTest("Condition allows positional arguments")
+
def test_recursion_count(self):
self.skipTest("Condition does not expose _recursion_count()")