Skip to content

Commit 7da03e2

Browse files
Support Py_TPFLAGS_MANAGED_DICT without gc
1 parent 6dbabaf commit 7da03e2

File tree

7 files changed

+77
-7
lines changed

7 files changed

+77
-7
lines changed

Include/internal/pycore_object.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -936,10 +936,12 @@ extern int _PyType_CacheInitForSpecialization(PyHeapTypeObject *type,
936936
#ifdef Py_GIL_DISABLED
937937
# define MANAGED_DICT_OFFSET (((Py_ssize_t)sizeof(PyObject *))*-1)
938938
# define MANAGED_WEAKREF_OFFSET (((Py_ssize_t)sizeof(PyObject *))*-2)
939+
# define MANAGED_DICT_OFFSET_NO_GC (((Py_ssize_t)sizeof(PyObject *))*-1)
939940
# define MANAGED_WEAKREF_OFFSET_NO_GC (((Py_ssize_t)sizeof(PyObject *))*-2)
940941
#else
941942
# define MANAGED_DICT_OFFSET (((Py_ssize_t)sizeof(PyObject *))*-3)
942943
# define MANAGED_WEAKREF_OFFSET (((Py_ssize_t)sizeof(PyObject *))*-4)
944+
# define MANAGED_DICT_OFFSET_NO_GC (((Py_ssize_t)sizeof(PyObject *))*-1)
943945
# define MANAGED_WEAKREF_OFFSET_NO_GC (((Py_ssize_t)sizeof(PyObject *))*-2)
944946
#endif
945947

@@ -950,8 +952,13 @@ typedef union {
950952
static inline PyManagedDictPointer *
951953
_PyObject_ManagedDictPointer(PyObject *obj)
952954
{
953-
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
954-
return (PyManagedDictPointer *)((char *)obj + MANAGED_DICT_OFFSET);
955+
PyTypeObject *type = Py_TYPE(obj);
956+
assert(type->tp_flags & Py_TPFLAGS_MANAGED_DICT);
957+
Py_ssize_t offset = MANAGED_DICT_OFFSET;
958+
if (!_PyType_IS_GC(type)) {
959+
offset = MANAGED_DICT_OFFSET_NO_GC;
960+
}
961+
return (PyManagedDictPointer *)((char *)obj + offset);
955962
}
956963

957964
static inline PyDictObject *

Lib/test/test_capi/test_type.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,10 +276,15 @@ def test_extension_managed_dict_type(self):
276276
self.assertEqual(obj.__dict__, {'bar': 3})
277277
self.assertEqual(obj.bar, 3)
278278

279-
def test_type_have_weakref_and_no_gc(self):
279+
def test_type_have_managed_weakref_and_no_gc(self):
280280
ManagedWeakrefNoGCType = _testcapi.ManagedWeakrefNoGCType
281281
obj = ManagedWeakrefNoGCType()
282282
wr = weakref.ref(obj)
283283

284284
del obj # shouldn't segfault
285285
del wr
286+
287+
def test_type_have_managed_dict_and_no_gc(self):
288+
ManagedDictNoGCType = _testcapi.ManagedDictNoGCType
289+
obj = ManagedDictNoGCType()
290+
del obj # shouldn't segfault

Modules/_testcapimodule.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3252,6 +3252,30 @@ static PyType_Spec ManagedWeakrefNoGC_spec = {
32523252
.slots = ManagedWeakrefNoGC_slots,
32533253
};
32543254

3255+
typedef struct {
3256+
PyObject_HEAD
3257+
} ManagedDictNoGCObject;
3258+
3259+
static void
3260+
ManagedDictNoGC_dealloc(PyObject *self)
3261+
{
3262+
PyTypeObject *tp = Py_TYPE(self);
3263+
tp->tp_free(self);
3264+
Py_DECREF(tp);
3265+
}
3266+
3267+
static PyType_Slot ManagedDictNoGC_slots[] = {
3268+
{Py_tp_dealloc, ManagedDictNoGC_dealloc},
3269+
{0, 0}
3270+
};
3271+
3272+
static PyType_Spec ManagedDictNoGC_spec = {
3273+
.name = "_testcapi.ManagedDictNoGCType",
3274+
.basicsize = sizeof(ManagedDictNoGCObject),
3275+
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_MANAGED_DICT),
3276+
.slots = ManagedDictNoGC_slots,
3277+
};
3278+
32553279

32563280
static PyObject *
32573281
create_managed_dict_type(void)
@@ -3265,6 +3289,12 @@ create_managed_weakref_no_gc_type(void)
32653289
return PyType_FromSpec(&ManagedWeakrefNoGC_spec);
32663290
}
32673291

3292+
static PyObject *
3293+
create_managed_dict_no_gc_type(void)
3294+
{
3295+
return PyType_FromSpec(&ManagedDictNoGC_spec);
3296+
}
3297+
32683298
static int
32693299
_testcapi_exec(PyObject *m)
32703300
{
@@ -3402,6 +3432,13 @@ _testcapi_exec(PyObject *m)
34023432
return -1;
34033433
}
34043434

3435+
PyObject *managed_dict_no_gc_type = create_managed_dict_no_gc_type();
3436+
if (managed_dict_no_gc_type == NULL) {
3437+
return -1;
3438+
}
3439+
if (PyModule_Add(m, "ManagedDictNoGCType", managed_dict_no_gc_type) < 0) {
3440+
return -1;
3441+
}
34053442

34063443
/* Include tests from the _testcapi/ directory */
34073444
if (_PyTestCapi_Init_Vectorcall(m) < 0) {

Python/bytecodes.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3638,7 +3638,13 @@ dummy_func(
36383638
_LOAD_ATTR_NONDESCRIPTOR_NO_DICT;
36393639

36403640
op(_CHECK_ATTR_METHOD_LAZY_DICT, (dictoffset/1, owner -- owner)) {
3641-
char *ptr = ((char *)PyStackRef_AsPyObjectBorrow(owner)) + MANAGED_DICT_OFFSET + dictoffset;
3641+
PyObject *borrowed = PyStackRef_AsPyObjectBorrow(owner);
3642+
Py_ssize_t offset = MANAGED_DICT_OFFSET;
3643+
if (!PyType_IS_GC(Py_TYPE(borrowed))) {
3644+
offset = MANAGED_DICT_OFFSET_NO_GC;
3645+
}
3646+
3647+
char *ptr = ((char *)borrowed) + offset + dictoffset;
36423648
PyObject *dict = FT_ATOMIC_LOAD_PTR_ACQUIRE(*(PyObject **)ptr);
36433649
/* This object has a __dict__, just not yet created */
36443650
DEOPT_IF(dict != NULL);

Python/executor_cases.c.h

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/generated_cases.c.h

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/specialize.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1626,7 +1626,12 @@ specialize_attr_loadclassattr(PyObject *owner, _Py_CODEUNIT *instr,
16261626
else {
16271627
Py_ssize_t dictoffset;
16281628
if (tp_flags & Py_TPFLAGS_MANAGED_DICT) {
1629-
dictoffset = MANAGED_DICT_OFFSET;
1629+
if (tp_flags & Py_TPFLAGS_HAVE_GC) {
1630+
dictoffset = MANAGED_DICT_OFFSET;
1631+
}
1632+
else {
1633+
dictoffset = MANAGED_DICT_OFFSET_NO_GC;
1634+
}
16301635
}
16311636
else {
16321637
dictoffset = owner_cls->tp_dictoffset;

0 commit comments

Comments
 (0)