Skip to content

Commit 2de048c

Browse files
authored
pythongh-115999: Specialize loading attributes from modules in free-threaded builds (python#127711)
We use the same approach that was used for specialization of LOAD_GLOBAL in free-threaded builds: _CHECK_ATTR_MODULE is renamed to _CHECK_ATTR_MODULE_PUSH_KEYS; it pushes the keys object for the following _LOAD_ATTR_MODULE_FROM_KEYS (nee _LOAD_ATTR_MODULE). This arrangement avoids having to recheck the keys version. _LOAD_ATTR_MODULE is renamed to _LOAD_ATTR_MODULE_FROM_KEYS; it loads the value from the keys object pushed by the preceding _CHECK_ATTR_MODULE_PUSH_KEYS at the cached index.
1 parent 292067f commit 2de048c

15 files changed

+437
-179
lines changed

Include/internal/pycore_opcode_metadata.h

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_uop_ids.h

+71-70
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_uop_metadata.h

+10-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_capi/test_misc.py

+38-9
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
# Skip this test if the _testcapi module isn't available.
4949
_testcapi = import_helper.import_module('_testcapi')
5050

51+
from _testcapi import HeapCTypeSubclass, HeapCTypeSubclassWithFinalizer
52+
5153
import _testlimitedcapi
5254
import _testinternalcapi
5355

@@ -653,9 +655,9 @@ def test_c_subclass_of_heap_ctype_with_tpdealloc_decrefs_once(self):
653655
self.assertEqual(type_refcnt - 1, sys.getrefcount(_testcapi.HeapCTypeSubclass))
654656

655657
def test_c_subclass_of_heap_ctype_with_del_modifying_dunder_class_only_decrefs_once(self):
656-
subclass_instance = _testcapi.HeapCTypeSubclassWithFinalizer()
657-
type_refcnt = sys.getrefcount(_testcapi.HeapCTypeSubclassWithFinalizer)
658-
new_type_refcnt = sys.getrefcount(_testcapi.HeapCTypeSubclass)
658+
subclass_instance = HeapCTypeSubclassWithFinalizer()
659+
type_refcnt = sys.getrefcount(HeapCTypeSubclassWithFinalizer)
660+
new_type_refcnt = sys.getrefcount(HeapCTypeSubclass)
659661

660662
# Test that subclass instance was fully created
661663
self.assertEqual(subclass_instance.value, 10)
@@ -665,19 +667,46 @@ def test_c_subclass_of_heap_ctype_with_del_modifying_dunder_class_only_decrefs_o
665667
del subclass_instance
666668

667669
# Test that setting __class__ modified the reference counts of the types
670+
#
671+
# This is highly sensitive to implementation details and may break in the future.
672+
#
673+
# We expect the refcount on the old type, HeapCTypeSubclassWithFinalizer, to
674+
# remain the same: the finalizer gets a strong reference (+1) when it gets the
675+
# type from the module and setting __class__ decrements the refcount (-1).
676+
#
677+
# We expect the refcount on the new type, HeapCTypeSubclass, to increase by 2:
678+
# the finalizer get a strong reference (+1) when it gets the type from the
679+
# module and setting __class__ increments the refcount (+1).
680+
expected_type_refcnt = type_refcnt
681+
expected_new_type_refcnt = new_type_refcnt + 2
682+
683+
if not Py_GIL_DISABLED:
684+
# In default builds the result returned from sys.getrefcount
685+
# includes a temporary reference that is created by the interpreter
686+
# when it pushes its argument on the operand stack. This temporary
687+
# reference is not included in the result returned by Py_REFCNT, which
688+
# is used in the finalizer.
689+
#
690+
# In free-threaded builds the result returned from sys.getrefcount
691+
# does not include the temporary reference. Types use deferred
692+
# refcounting and the interpreter will not create a new reference
693+
# for deferred values on the operand stack.
694+
expected_type_refcnt -= 1
695+
expected_new_type_refcnt -= 1
696+
668697
if support.Py_DEBUG:
669698
# gh-89373: In debug mode, _Py_Dealloc() keeps a strong reference
670699
# to the type while calling tp_dealloc()
671-
self.assertEqual(type_refcnt, _testcapi.HeapCTypeSubclassWithFinalizer.refcnt_in_del)
672-
else:
673-
self.assertEqual(type_refcnt - 1, _testcapi.HeapCTypeSubclassWithFinalizer.refcnt_in_del)
674-
self.assertEqual(new_type_refcnt + 1, _testcapi.HeapCTypeSubclass.refcnt_in_del)
700+
expected_type_refcnt += 1
701+
702+
self.assertEqual(expected_type_refcnt, HeapCTypeSubclassWithFinalizer.refcnt_in_del)
703+
self.assertEqual(expected_new_type_refcnt, HeapCTypeSubclass.refcnt_in_del)
675704

676705
# Test that the original type already has decreased its refcnt
677-
self.assertEqual(type_refcnt - 1, sys.getrefcount(_testcapi.HeapCTypeSubclassWithFinalizer))
706+
self.assertEqual(type_refcnt - 1, sys.getrefcount(HeapCTypeSubclassWithFinalizer))
678707

679708
# Test that subtype_dealloc decref the newly assigned __class__ only once
680-
self.assertEqual(new_type_refcnt, sys.getrefcount(_testcapi.HeapCTypeSubclass))
709+
self.assertEqual(new_type_refcnt, sys.getrefcount(HeapCTypeSubclass))
681710

682711
def test_heaptype_with_setattro(self):
683712
obj = _testcapi.HeapCTypeSetattr()

0 commit comments

Comments
 (0)