gh-84436: Implement Immortal Objects (gh-19474)

This is the implementation of PEP683

Motivation:

The PR introduces the ability to immortalize instances in CPython which bypasses reference counting. Tagging objects as immortal allows up to skip certain operations when we know that the object will be around for the entire execution of the runtime.

Note that this by itself will bring a performance regression to the runtime due to the extra reference count checks. However, this brings the ability of having truly immutable objects that are useful in other contexts such as immutable data sharing between sub-interpreters.
This commit is contained in:
Eddie Elizondo 2023-04-22 15:39:37 -04:00 committed by GitHub
parent 916de04fd1
commit ea2c001650
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 483 additions and 171 deletions

View file

@ -1,27 +1,31 @@
import sys
import types
import unittest
# Note: This test file can't import `unittest` since the runtime can't
# currently guarantee that it will not leak memory. Doing so will mark
# the test as passing but with reference leaks. This can safely import
# the `unittest` library once there's a strict guarantee of no leaks
# during runtime shutdown.
# bpo-46417: Test that structseq types used by the sys module are still
# valid when Py_Finalize()/Py_Initialize() are called multiple times.
class TestStructSeq(unittest.TestCase):
class TestStructSeq:
# test PyTypeObject members
def check_structseq(self, obj_type):
def _check_structseq(self, obj_type):
# ob_refcnt
self.assertGreaterEqual(sys.getrefcount(obj_type), 1)
assert sys.getrefcount(obj_type) > 1
# tp_base
self.assertTrue(issubclass(obj_type, tuple))
assert issubclass(obj_type, tuple)
# tp_bases
self.assertEqual(obj_type.__bases__, (tuple,))
assert obj_type.__bases__ == (tuple,)
# tp_dict
self.assertIsInstance(obj_type.__dict__, types.MappingProxyType)
assert isinstance(obj_type.__dict__, types.MappingProxyType)
# tp_mro
self.assertEqual(obj_type.__mro__, (obj_type, tuple, object))
assert obj_type.__mro__ == (obj_type, tuple, object)
# tp_name
self.assertIsInstance(type.__name__, str)
assert isinstance(type.__name__, str)
# tp_subclasses
self.assertEqual(obj_type.__subclasses__(), [])
assert obj_type.__subclasses__() == []
def test_sys_attrs(self):
for attr_name in (
@ -32,23 +36,23 @@ class TestStructSeq(unittest.TestCase):
'thread_info', # ThreadInfoType
'version_info', # VersionInfoType
):
with self.subTest(attr=attr_name):
attr = getattr(sys, attr_name)
self.check_structseq(type(attr))
attr = getattr(sys, attr_name)
self._check_structseq(type(attr))
def test_sys_funcs(self):
func_names = ['get_asyncgen_hooks'] # AsyncGenHooksType
if hasattr(sys, 'getwindowsversion'):
func_names.append('getwindowsversion') # WindowsVersionType
for func_name in func_names:
with self.subTest(func=func_name):
func = getattr(sys, func_name)
obj = func()
self.check_structseq(type(obj))
func = getattr(sys, func_name)
obj = func()
self._check_structseq(type(obj))
try:
unittest.main()
tests = TestStructSeq()
tests.test_sys_attrs()
tests.test_sys_funcs()
except SystemExit as exc:
if exc.args[0] != 0:
raise