Skip to content

Commit 42c0932

Browse files
Add support of weakrefs without gc
1 parent 504f0af commit 42c0932

File tree

4 files changed

+84
-16
lines changed

4 files changed

+84
-16
lines changed

Include/internal/pycore_object.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,7 @@ extern int _PyType_CacheInitForSpecialization(PyHeapTypeObject *type,
939939
#else
940940
# define MANAGED_DICT_OFFSET (((Py_ssize_t)sizeof(PyObject *))*-3)
941941
# define MANAGED_WEAKREF_OFFSET (((Py_ssize_t)sizeof(PyObject *))*-4)
942+
# define MANAGED_WEAKREF_OFFSET_NO_GC (((Py_ssize_t)sizeof(PyObject *))*-2)
942943
#endif
943944

944945
typedef union {

Lib/test/test_capi/test_type.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from test.support import import_helper, Py_GIL_DISABLED, refleak_helper
22
import unittest
3+
import weakref
34

45
_testcapi = import_helper.import_module('_testcapi')
56

@@ -274,3 +275,11 @@ def test_extension_managed_dict_type(self):
274275
obj.__dict__ = {'bar': 3}
275276
self.assertEqual(obj.__dict__, {'bar': 3})
276277
self.assertEqual(obj.bar, 3)
278+
279+
def test_type_have_weakref_and_no_gc(self):
280+
ManagedWeakrefNoGCType = _testcapi.ManagedWeakrefNoGCType
281+
obj = ManagedWeakrefNoGCType()
282+
wr = weakref.ref(obj)
283+
284+
del obj # shouldn't segfault
285+
del wr

Modules/_testcapimodule.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3227,12 +3227,44 @@ static PyType_Spec ManagedDict_spec = {
32273227
ManagedDict_slots
32283228
};
32293229

3230+
typedef struct {
3231+
PyObject_HEAD
3232+
} ManagedWeakrefNoGCObject;
3233+
3234+
static void
3235+
ManagedWeakrefNoGC_dealloc(PyObject *self)
3236+
{
3237+
PyObject_ClearWeakRefs(self);
3238+
PyTypeObject *tp = Py_TYPE(self);
3239+
tp->tp_free(self);
3240+
Py_DECREF(tp);
3241+
}
3242+
3243+
static PyType_Slot ManagedWeakrefNoGC_slots[] = {
3244+
{Py_tp_dealloc, ManagedWeakrefNoGC_dealloc},
3245+
{0, 0}
3246+
};
3247+
3248+
static PyType_Spec ManagedWeakrefNoGC_spec = {
3249+
.name = "_testcapi.ManagedWeakrefNoGCType",
3250+
.basicsize = sizeof(ManagedWeakrefNoGCObject),
3251+
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_MANAGED_WEAKREF),
3252+
.slots = ManagedWeakrefNoGC_slots,
3253+
};
3254+
3255+
32303256
static PyObject *
32313257
create_managed_dict_type(void)
32323258
{
32333259
return PyType_FromSpec(&ManagedDict_spec);
32343260
}
32353261

3262+
static PyObject *
3263+
create_managed_weakref_no_gc_type(void)
3264+
{
3265+
return PyType_FromSpec(&ManagedWeakrefNoGC_spec);
3266+
}
3267+
32363268
static int
32373269
_testcapi_exec(PyObject *m)
32383270
{
@@ -3362,6 +3394,15 @@ _testcapi_exec(PyObject *m)
33623394
return -1;
33633395
}
33643396

3397+
PyObject *managed_weakref_no_gc_type = create_managed_weakref_no_gc_type();
3398+
if (managed_weakref_no_gc_type == NULL) {
3399+
return -1;
3400+
}
3401+
if (PyModule_Add(m, "ManagedWeakrefNoGCType", managed_weakref_no_gc_type) < 0) {
3402+
return -1;
3403+
}
3404+
3405+
33653406
/* Include tests from the _testcapi/ directory */
33663407
if (_PyTestCapi_Init_Vectorcall(m) < 0) {
33673408
return -1;

Objects/typeobject.c

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4655,7 +4655,12 @@ type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type, PyObject *dict
46554655
if (ctx->add_weak) {
46564656
assert((type->tp_flags & Py_TPFLAGS_MANAGED_WEAKREF) == 0);
46574657
type_add_flags(type, Py_TPFLAGS_MANAGED_WEAKREF);
4658-
type->tp_weaklistoffset = MANAGED_WEAKREF_OFFSET;
4658+
if (_PyType_IS_GC(type)) {
4659+
type->tp_weaklistoffset = MANAGED_WEAKREF_OFFSET;
4660+
}
4661+
else {
4662+
type->tp_weaklistoffset = MANAGED_WEAKREF_OFFSET_NO_GC;
4663+
}
46594664
}
46604665
if (ctx->add_dict) {
46614666
assert((type->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0);
@@ -8500,6 +8505,13 @@ overrides_hash(PyTypeObject *type)
85008505
return r;
85018506
}
85028507

8508+
void
8509+
PyObject_NoGC_Preheader_Del(void *op)
8510+
{
8511+
size_t presize = _PyType_PreHeaderSize(Py_TYPE(op));
8512+
PyObject_Free(((char *)op) - presize);
8513+
}
8514+
85038515
static int
85048516
inherit_slots(PyTypeObject *type, PyTypeObject *base)
85058517
{
@@ -8677,7 +8689,21 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base)
86778689
if ((type->tp_flags & Py_TPFLAGS_HAVE_GC) ==
86788690
(base->tp_flags & Py_TPFLAGS_HAVE_GC)) {
86798691
/* They agree about gc. */
8680-
COPYSLOT(tp_free);
8692+
8693+
if ((type->tp_flags & Py_TPFLAGS_PREHEADER) &&
8694+
type->tp_free == NULL &&
8695+
base->tp_free == PyObject_Free) {
8696+
/* Because type has preheader fields, its
8697+
* objects will be allocated with those fields
8698+
* and it should be take in account when object
8699+
* is freed, so we use special tp_free.
8700+
*/
8701+
type->tp_free = PyObject_NoGC_Preheader_Del;
8702+
}
8703+
else {
8704+
COPYSLOT(tp_free);
8705+
}
8706+
86818707
}
86828708
else if ((type->tp_flags & Py_TPFLAGS_HAVE_GC) &&
86838709
type->tp_free == NULL &&
@@ -8893,13 +8919,6 @@ type_ready_preheader(PyTypeObject *type)
88938919
type->tp_name);
88948920
return -1;
88958921
}
8896-
if (!(type->tp_flags & Py_TPFLAGS_HAVE_GC)) {
8897-
PyErr_Format(PyExc_SystemError,
8898-
"type %s has the Py_TPFLAGS_MANAGED_DICT flag "
8899-
"but not Py_TPFLAGS_HAVE_GC flag",
8900-
type->tp_name);
8901-
return -1;
8902-
}
89038922
type->tp_dictoffset = -1;
89048923
}
89058924
if (type->tp_flags & Py_TPFLAGS_MANAGED_WEAKREF) {
@@ -8912,14 +8931,12 @@ type_ready_preheader(PyTypeObject *type)
89128931
type->tp_name);
89138932
return -1;
89148933
}
8915-
if (!(type->tp_flags & Py_TPFLAGS_HAVE_GC)) {
8916-
PyErr_Format(PyExc_SystemError,
8917-
"type %s has the Py_TPFLAGS_MANAGED_WEAKREF flag "
8918-
"but not Py_TPFLAGS_HAVE_GC flag",
8919-
type->tp_name);
8920-
return -1;
8934+
if (_PyType_IS_GC(type)) {
8935+
type->tp_weaklistoffset = MANAGED_WEAKREF_OFFSET;
8936+
}
8937+
else {
8938+
type->tp_weaklistoffset = MANAGED_WEAKREF_OFFSET_NO_GC;
89218939
}
8922-
type->tp_weaklistoffset = MANAGED_WEAKREF_OFFSET;
89238940
}
89248941
return 0;
89258942
}

0 commit comments

Comments
 (0)