aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/test
diff options
context:
space:
mode:
authorSam Gross <colesbury@gmail.com>2024-04-29 14:36:02 -0400
committerGitHub <noreply@github.com>2024-04-29 14:36:02 -0400
commit7ccacb220d99662b626c8bc63b00a27eaf604f0c (patch)
tree231cb901ee62e523be237392b0f2b966aa8be128 /Lib/test
parent8d4b756fd31d4d91b55105b1241561e92cc571a3 (diff)
downloadcpython-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.py8
-rw-r--r--Lib/test/libregrtest/single.py5
-rw-r--r--Lib/test/support/__init__.py19
-rw-r--r--Lib/test/test_capi/test_watchers.py6
-rw-r--r--Lib/test/test_code.py6
-rw-r--r--Lib/test/test_functools.py1
-rw-r--r--Lib/test/test_weakref.py5
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):