Skip to content

Commit 5035cf0

Browse files
committed
Fix collection of stgdicts that got assigned to a type
1 parent eed157f commit 5035cf0

File tree

3 files changed

+43
-14
lines changed

3 files changed

+43
-14
lines changed

graalpython/com.oracle.graal.python.cext/modules/_ctypes/_ctypes.c

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,41 @@ bytes(cdata)
131131

132132
#include "pycore_long.h" // _PyLong_GetZero()
133133

134+
#if 1 // GraalPy change
135+
// Assign a stgdict to type's tp_dict slot. In GraalPy we do not track our
136+
// builtin dicts, and type dicts for managed classes actually have no tp_dict
137+
// with an extra native ref, they are kept alive by the type only and their
138+
// handles are collected by the GC or on context shutdown. So when we assign a
139+
// fresh stgdict to the type's tp_dict, we make it also have only
140+
// MANAGED_REFCOUNT and let it be collected when the type is collected.
141+
#define set_graalpy_tp_dict(type, stgdict) \
142+
{ \
143+
PyObject *__stgdict = (PyObject *)(stgdict); \
144+
PyTypeObject *__type = (PyTypeObject *)(type); \
145+
PyObject_GC_UnTrack(__stgdict); \
146+
if (points_to_py_handle_space(__type)) { \
147+
_PyObject_ASSERT_FAILED_MSG((PyObject*) __type, \
148+
"type dict has increased refcnt, " \
149+
"something changed in how GraalPy " \
150+
"marries classes to native structs"); \
151+
} \
152+
if (Py_REFCNT(__type->tp_dict) != MANAGED_REFCNT) { \
153+
_PyObject_ASSERT_FAILED_MSG((PyObject*) __type, \
154+
"type dict has increased refcnt, " \
155+
"something changed in how GraalPy " \
156+
"marries classes to native structs"); \
157+
} \
158+
if (Py_REFCNT(__stgdict) != MANAGED_REFCNT + 1) { \
159+
_PyObject_ASSERT_FAILED_MSG(__stgdict, \
160+
"new stgdict has incorrect refcnt, " \
161+
"something changed in GraalPy"); \
162+
} \
163+
__type->tp_dict = (PyObject *)__stgdict; \
164+
PyType_Modified(__type); \
165+
Py_DecRef(__stgdict); \
166+
}
167+
#endif
168+
134169
ctypes_state global_state;
135170

136171
PyObject *PyExc_ArgError = NULL;
@@ -550,8 +585,7 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt
550585
Py_DECREF((PyObject *)dict);
551586
return NULL;
552587
}
553-
result->tp_dict = (PyObject *)dict; // GraalPy change: do not use Py_SETREF
554-
PyType_Modified(result); // GraalPy change: sync tp_dict change to managed
588+
set_graalpy_tp_dict(result, dict); // GraalPy change: do not use Py_SETREF
555589
dict->format = _ctypes_alloc_format_string(NULL, "B");
556590
if (dict->format == NULL) {
557591
Py_DECREF(result);
@@ -1153,8 +1187,7 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
11531187
Py_DECREF((PyObject *)stgdict);
11541188
return NULL;
11551189
}
1156-
result->tp_dict = (PyObject *)stgdict; // GraalPy change: do not use Py_SETREF
1157-
PyType_Modified(result); // GraalPy change: sync tp_dict change to managed
1190+
set_graalpy_tp_dict(result, stgdict); // GraalPy change: do not use Py_SETREF
11581191

11591192
return (PyObject *)result;
11601193
}
@@ -1590,8 +1623,7 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
15901623
/* replace the class dict by our updated spam dict */
15911624
if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict))
15921625
goto error;
1593-
result->tp_dict = (PyObject *)stgdict; // GraalPy change: do not use Py_SETREF
1594-
PyType_Modified(result); // GraalPy change: sync tp_dict change to managed
1626+
set_graalpy_tp_dict(result, stgdict); // GraalPy change: do not use Py_SETREF
15951627
stgdict = NULL;
15961628

15971629
/* Special case for character arrays.
@@ -2005,8 +2037,7 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject
20052037
Py_DECREF((PyObject *)stgdict);
20062038
return NULL;
20072039
}
2008-
result->tp_dict = (PyObject *)stgdict; // GraalPy change: do not use Py_SETREF
2009-
PyType_Modified(result); // GraalPy change: sync tp_dict change to managed
2040+
set_graalpy_tp_dict(result, stgdict); // GraalPy change: do not use Py_SETREF
20102041

20112042
return (PyObject *)result;
20122043
}
@@ -2135,8 +2166,7 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
21352166
Py_DECREF((PyObject *)stgdict);
21362167
return NULL;
21372168
}
2138-
result->tp_dict = (PyObject *)stgdict; // GraalPy change: do not use Py_SETREF
2139-
PyType_Modified(result); // GraalPy change: sync tp_dict change to managed
2169+
set_graalpy_tp_dict(result, stgdict); // GraalPy change: do not use Py_SETREF
21402170

21412171
/* Install from_param class methods in ctypes base classes.
21422172
Overrides the PyCSimpleType_from_param generic method.
@@ -2578,8 +2608,7 @@ PyCFuncPtrType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
25782608
Py_DECREF((PyObject *)stgdict);
25792609
return NULL;
25802610
}
2581-
result->tp_dict = (PyObject *)stgdict; // GraalPy change: do not use Py_SETREF
2582-
PyType_Modified(result); // GraalPy change: sync tp_dict change to managed
2611+
set_graalpy_tp_dict(result, stgdict); // GraalPy change: do not use Py_SETREF
25832612

25842613
if (-1 == make_funcptrtype_dict(stgdict)) {
25852614
Py_DECREF(result);

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1121,7 +1121,7 @@ public void finalizeCApi() {
11211121
* lookup table and may otherwise be double-freed.
11221122
*/
11231123
CApiTransitions.freeNativeObjectStubs(handleContext);
1124-
CApiTransitions.freeClassReplacements(handleContext);
1124+
CApiTransitions.freeNativeReplacementStructs(handleContext);
11251125
CApiTransitions.freeNativeStorages(handleContext);
11261126
}
11271127
if (pyDateTimeCAPICapsule != null) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/CApiTransitions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ private static boolean tableEntryRemoved(HandleContext context, PythonNativeWrap
622622
return true;
623623
}
624624

625-
public static void freeClassReplacements(HandleContext handleContext) {
625+
public static void freeNativeReplacementStructs(HandleContext handleContext) {
626626
assert PythonContext.get(null).ownsGil();
627627
handleContext.nativeLookup.forEach((l, ref) -> {
628628
if (ref instanceof PythonObjectReference reference) {

0 commit comments

Comments
 (0)