diff options
author | Sam Gross <colesbury@gmail.com> | 2024-04-29 14:36:02 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-29 14:36:02 -0400 |
commit | 7ccacb220d99662b626c8bc63b00a27eaf604f0c (patch) | |
tree | 231cb901ee62e523be237392b0f2b966aa8be128 /Lib/test | |
parent | 8d4b756fd31d4d91b55105b1241561e92cc571a3 (diff) | |
download | cpython-7ccacb220d99662b626c8bc63b00a27eaf604f0c.tar.gz cpython-7ccacb220d99662b626c8bc63b00a27eaf604f0c.zip |
gh-117783: Immortalize objects that use deferred reference counting (#118112)
Deferred reference counting is not fully implemented yet. As a temporary
measure, we immortalize objects that would use deferred reference
counting to avoid multi-threaded scaling bottlenecks.
This is only performed in the free-threaded build once the first
non-main thread is started. Additionally, some tests, including refleak
tests, suppress this behavior.
Diffstat (limited to 'Lib/test')
-rw-r--r-- | Lib/test/libregrtest/main.py | 8 | ||||
-rw-r--r-- | Lib/test/libregrtest/single.py | 5 | ||||
-rw-r--r-- | Lib/test/support/__init__.py | 19 | ||||
-rw-r--r-- | Lib/test/test_capi/test_watchers.py | 6 | ||||
-rw-r--r-- | Lib/test/test_code.py | 6 | ||||
-rw-r--r-- | Lib/test/test_functools.py | 1 | ||||
-rw-r--r-- | Lib/test/test_weakref.py | 5 |
7 files changed, 44 insertions, 6 deletions
diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 3c9d9620053..9e7a7d60880 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -7,7 +7,8 @@ import sysconfig import time import trace -from test.support import os_helper, MS_WINDOWS, flush_std_streams +from test.support import (os_helper, MS_WINDOWS, flush_std_streams, + suppress_immortalization) from .cmdline import _parse_args, Namespace from .findtests import findtests, split_test_packages, list_cases @@ -526,7 +527,10 @@ class Regrtest: if self.num_workers: self._run_tests_mp(runtests, self.num_workers) else: - self.run_tests_sequentially(runtests) + # gh-117783: don't immortalize deferred objects when tracking + # refleaks. Only releveant for the free-threaded build. + with suppress_immortalization(runtests.hunt_refleak): + self.run_tests_sequentially(runtests) coverage = self.results.get_coverage_results() self.display_result(runtests) diff --git a/Lib/test/libregrtest/single.py b/Lib/test/libregrtest/single.py index 235029d8620..fc2f2716ad4 100644 --- a/Lib/test/libregrtest/single.py +++ b/Lib/test/libregrtest/single.py @@ -303,7 +303,10 @@ def run_single_test(test_name: TestName, runtests: RunTests) -> TestResult: result = TestResult(test_name) pgo = runtests.pgo try: - _runtest(result, runtests) + # gh-117783: don't immortalize deferred objects when tracking + # refleaks. Only releveant for the free-threaded build. + with support.suppress_immortalization(runtests.hunt_refleak): + _runtest(result, runtests) except: if not pgo: msg = traceback.format_exc() diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 70d2610aa49..15f654302b1 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -516,6 +516,25 @@ def has_no_debug_ranges(): def requires_debug_ranges(reason='requires co_positions / debug_ranges'): return unittest.skipIf(has_no_debug_ranges(), reason) +@contextlib.contextmanager +def suppress_immortalization(suppress=True): + """Suppress immortalization of deferred objects.""" + try: + import _testinternalcapi + except ImportError: + yield + return + + if not suppress: + yield + return + + old_values = _testinternalcapi.set_immortalize_deferred(False) + try: + yield + finally: + _testinternalcapi.set_immortalize_deferred(*old_values) + MS_WINDOWS = (sys.platform == 'win32') # Is not actually used in tests, but is kept for compatibility. diff --git a/Lib/test/test_capi/test_watchers.py b/Lib/test/test_capi/test_watchers.py index 8e84d0077c7..90665a7561b 100644 --- a/Lib/test/test_capi/test_watchers.py +++ b/Lib/test/test_capi/test_watchers.py @@ -1,7 +1,9 @@ import unittest from contextlib import contextmanager, ExitStack -from test.support import catch_unraisable_exception, import_helper, gc_collect +from test.support import ( + catch_unraisable_exception, import_helper, + gc_collect, suppress_immortalization) # Skip this test if the _testcapi module isn't available. @@ -382,6 +384,7 @@ class TestCodeObjectWatchers(unittest.TestCase): self.assertEqual( exp_destroyed_1, _testcapi.get_code_watcher_num_destroyed_events(1)) + @suppress_immortalization() def test_code_object_events_dispatched(self): # verify that all counts are zero before any watchers are registered self.assert_event_counts(0, 0, 0, 0) @@ -428,6 +431,7 @@ class TestCodeObjectWatchers(unittest.TestCase): self.assertIsNone(cm.unraisable.object) self.assertEqual(str(cm.unraisable.exc_value), "boom!") + @suppress_immortalization() def test_dealloc_error(self): co = _testcapi.code_newempty("test_watchers", "dummy0", 0) with self.code_watcher(2): diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index fe8c672e71a..aa793f56225 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -141,7 +141,8 @@ except ImportError: ctypes = None from test.support import (cpython_only, check_impl_detail, requires_debug_ranges, - gc_collect, Py_GIL_DISABLED) + gc_collect, Py_GIL_DISABLED, + suppress_immortalization) from test.support.script_helper import assert_python_ok from test.support import threading_helper, import_helper from test.support.bytecode_helper import instructions_with_positions @@ -577,6 +578,7 @@ class CodeConstsTest(unittest.TestCase): class CodeWeakRefTest(unittest.TestCase): + @suppress_immortalization() def test_basic(self): # Create a code object in a clean environment so that we know we have # the only reference to it left. @@ -827,6 +829,7 @@ if check_impl_detail(cpython=True) and ctypes is not None: self.assertEqual(GetExtra(f.__code__, FREE_INDEX+100, ctypes.c_voidp(100)), 0) + @suppress_immortalization() def test_free_called(self): # Verify that the provided free function gets invoked # when the code object is cleaned up. @@ -854,6 +857,7 @@ if check_impl_detail(cpython=True) and ctypes is not None: del f @threading_helper.requires_working_threading() + @suppress_immortalization() def test_free_different_thread(self): # Freeing a code object on a different thread then # where the co_extra was set should be safe. diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index ec5f6af5e17..bb4c7cc8701 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -1833,6 +1833,7 @@ class TestLRU: return 1 self.assertEqual(f.cache_parameters(), {'maxsize': 1000, "typed": True}) + @support.suppress_immortalization() def test_lru_cache_weakrefable(self): @self.module.lru_cache def test_function(x): diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index a2f5b9b2902..df90647baee 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -13,7 +13,7 @@ import random import textwrap from test import support -from test.support import script_helper, ALWAYS_EQ +from test.support import script_helper, ALWAYS_EQ, suppress_immortalization from test.support import gc_collect from test.support import import_helper from test.support import threading_helper @@ -651,6 +651,7 @@ class ReferencesTestCase(TestBase): # deallocation of c2. del c2 + @suppress_immortalization() def test_callback_in_cycle(self): import gc @@ -743,6 +744,7 @@ class ReferencesTestCase(TestBase): del c1, c2, C, D gc.collect() + @suppress_immortalization() def test_callback_in_cycle_resurrection(self): import gc @@ -878,6 +880,7 @@ class ReferencesTestCase(TestBase): # No exception should be raised here gc.collect() + @suppress_immortalization() def test_classes(self): # Check that classes are weakrefable. class A(object): |