Skip to content

Commit

Permalink
gh-103092: Isolate _ctypes, part 1 (#103893)
Browse files Browse the repository at this point in the history
Establish global state and port the following types to heap types:

- DictRemover_Type
- PyCArg_Type
- PyCThunk_Type
- PyCField_Type
- StructParam_Type
  • Loading branch information
erlend-aasland authored Apr 27, 2023
1 parent 63842bd commit e9c7772
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 201 deletions.
165 changes: 100 additions & 65 deletions Modules/_ctypes/_ctypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ bytes(cdata)

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

ctypes_state global_state;

PyObject *PyExc_ArgError = NULL;

/* This dict maps ctypes types to POINTER types */
Expand All @@ -150,13 +152,32 @@ typedef struct {
PyObject *dict;
} DictRemoverObject;

static int
_DictRemover_traverse(DictRemoverObject *self, visitproc visit, void *arg)
{
Py_VISIT(Py_TYPE(self));
Py_VISIT(self->key);
Py_VISIT(self->dict);
return 0;
}

static int
_DictRemover_clear(DictRemoverObject *self)
{
Py_CLEAR(self->key);
Py_CLEAR(self->dict);
return 0;
}

static void
_DictRemover_dealloc(PyObject *myself)
{
PyTypeObject *tp = Py_TYPE(myself);
DictRemoverObject *self = (DictRemoverObject *)myself;
Py_XDECREF(self->key);
Py_XDECREF(self->dict);
Py_TYPE(self)->tp_free(myself);
PyObject_GC_UnTrack(myself);
(void)_DictRemover_clear(self);
tp->tp_free(myself);
Py_DECREF(tp);
}

static PyObject *
Expand All @@ -173,47 +194,23 @@ _DictRemover_call(PyObject *myself, PyObject *args, PyObject *kw)
Py_RETURN_NONE;
}

static PyTypeObject DictRemover_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_ctypes.DictRemover", /* tp_name */
sizeof(DictRemoverObject), /* tp_basicsize */
0, /* tp_itemsize */
_DictRemover_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
_DictRemover_call, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
/* XXX should participate in GC? */
Py_TPFLAGS_DEFAULT, /* tp_flags */
PyDoc_STR("deletes a key from a dictionary"), /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
0, /* tp_free */
PyDoc_STRVAR(dictremover_doc, "deletes a key from a dictionary");

static PyType_Slot dictremover_slots[] = {
{Py_tp_dealloc, _DictRemover_dealloc},
{Py_tp_traverse, _DictRemover_traverse},
{Py_tp_clear, _DictRemover_clear},
{Py_tp_call, _DictRemover_call},
{Py_tp_doc, (void *)dictremover_doc},
{0, NULL},
};

static PyType_Spec dictremover_spec = {
.name = "_ctypes.DictRemover",
.basicsize = sizeof(DictRemoverObject),
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_IMMUTABLETYPE),
.slots = dictremover_slots,
};

int
Expand All @@ -224,7 +221,8 @@ PyDict_SetItemProxy(PyObject *dict, PyObject *key, PyObject *item)
PyObject *proxy;
int result;

obj = _PyObject_CallNoArgs((PyObject *)&DictRemover_Type);
ctypes_state *st = GLOBAL_STATE();
obj = _PyObject_CallNoArgs((PyObject *)st->DictRemover_Type);
if (obj == NULL)
return -1;

Expand Down Expand Up @@ -415,23 +413,45 @@ typedef struct {
PyObject *keep; // If set, a reference to the original CDataObject.
} StructParamObject;

static int
StructParam_traverse(StructParamObject *self, visitproc visit, void *arg)
{
Py_VISIT(Py_TYPE(self));
return 0;
}

static int
StructParam_clear(StructParamObject *self)
{
Py_CLEAR(self->keep);
return 0;
}

static void
StructParam_dealloc(PyObject *myself)
{
StructParamObject *self = (StructParamObject *)myself;
Py_XDECREF(self->keep);
PyTypeObject *tp = Py_TYPE(self);
PyObject_GC_UnTrack(myself);
(void)StructParam_clear(self);
PyMem_Free(self->ptr);
Py_TYPE(self)->tp_free(myself);
tp->tp_free(myself);
Py_DECREF(tp);
}

static PyType_Slot structparam_slots[] = {
{Py_tp_traverse, StructParam_traverse},
{Py_tp_clear, StructParam_clear},
{Py_tp_dealloc, StructParam_dealloc},
{0, NULL},
};

static PyTypeObject StructParam_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "_ctypes.StructParam_Type",
.tp_basicsize = sizeof(StructParamObject),
.tp_dealloc = StructParam_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
static PyType_Spec structparam_spec = {
.name = "_ctypes.StructParam_Type",
.basicsize = sizeof(StructParamObject),
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE |
Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_DISALLOW_INSTANTIATION),
.slots = structparam_slots,
};


Expand Down Expand Up @@ -460,7 +480,9 @@ StructUnionType_paramfunc(CDataObject *self)
/* Create a Python object which calls PyMem_Free(ptr) in
its deallocator. The object will be destroyed
at _ctypes_callproc() cleanup. */
obj = (&StructParam_Type)->tp_alloc(&StructParam_Type, 0);
ctypes_state *st = GLOBAL_STATE();
PyTypeObject *tp = st->StructParam_Type;
obj = tp->tp_alloc(tp, 0);
if (obj == NULL) {
PyMem_Free(ptr);
return NULL;
Expand Down Expand Up @@ -800,7 +822,8 @@ CDataType_from_param(PyObject *type, PyObject *value)
if (res) {
return Py_NewRef(value);
}
if (PyCArg_CheckExact(value)) {
ctypes_state *st = GLOBAL_STATE();
if (PyCArg_CheckExact(st, value)) {
PyCArgObject *p = (PyCArgObject *)value;
PyObject *ob = p->obj;
const char *ob_name;
Expand Down Expand Up @@ -1683,7 +1706,8 @@ c_wchar_p_from_param(PyObject *type, PyObject *value)
return Py_NewRef(value);
}
}
if (PyCArg_CheckExact(value)) {
ctypes_state *st = GLOBAL_STATE();
if (PyCArg_CheckExact(st, value)) {
/* byref(c_char(...)) */
PyCArgObject *a = (PyCArgObject *)value;
StgDictObject *dict = PyObject_stgdict(a->obj);
Expand Down Expand Up @@ -1746,7 +1770,8 @@ c_char_p_from_param(PyObject *type, PyObject *value)
return Py_NewRef(value);
}
}
if (PyCArg_CheckExact(value)) {
ctypes_state *st = GLOBAL_STATE();
if (PyCArg_CheckExact(st, value)) {
/* byref(c_char(...)) */
PyCArgObject *a = (PyCArgObject *)value;
StgDictObject *dict = PyObject_stgdict(a->obj);
Expand Down Expand Up @@ -1847,7 +1872,8 @@ c_void_p_from_param(PyObject *type, PyObject *value)
return Py_NewRef(value);
}
/* byref(...) */
if (PyCArg_CheckExact(value)) {
ctypes_state *st = GLOBAL_STATE();
if (PyCArg_CheckExact(st, value)) {
/* byref(c_xxx()) */
PyCArgObject *a = (PyCArgObject *)value;
if (a->tag == 'P') {
Expand Down Expand Up @@ -5635,12 +5661,22 @@ _ctypes_add_types(PyObject *mod)
} \
} while (0)

#define CREATE_TYPE(MOD, TP, SPEC) do { \
PyObject *type = PyType_FromMetaclass(NULL, MOD, SPEC, NULL); \
if (type == NULL) { \
return -1; \
} \
TP = (PyTypeObject *)type; \
} while (0)

ctypes_state *st = GLOBAL_STATE();

/* Note:
ob_type is the metatype (the 'type'), defaults to PyType_Type,
tp_base is the base type, defaults to 'object' aka PyBaseObject_Type.
*/
TYPE_READY(&PyCArg_Type);
TYPE_READY(&PyCThunk_Type);
CREATE_TYPE(mod, st->PyCArg_Type, &carg_spec);
CREATE_TYPE(mod, st->PyCThunk_Type, &cthunk_spec);
TYPE_READY(&PyCData_Type);
/* StgDict is derived from PyDict_Type */
TYPE_READY_BASE(&PyCStgDict_Type, &PyDict_Type);
Expand Down Expand Up @@ -5673,17 +5709,15 @@ _ctypes_add_types(PyObject *mod)
* Simple classes
*/

/* PyCField_Type is derived from PyBaseObject_Type */
TYPE_READY(&PyCField_Type);
CREATE_TYPE(mod, st->PyCField_Type, &cfield_spec);

/*************************************************
*
* Other stuff
*/

DictRemover_Type.tp_new = PyType_GenericNew;
TYPE_READY(&DictRemover_Type);
TYPE_READY(&StructParam_Type);
CREATE_TYPE(mod, st->DictRemover_Type, &dictremover_spec);
CREATE_TYPE(mod, st->StructParam_Type, &structparam_spec);

#ifdef MS_WIN32
TYPE_READY_BASE(&PyComError_Type, (PyTypeObject*)PyExc_Exception);
Expand All @@ -5692,6 +5726,7 @@ _ctypes_add_types(PyObject *mod)
#undef TYPE_READY
#undef TYPE_READY_BASE
#undef MOD_ADD_TYPE
#undef CREATE_TYPE
return 0;
}

Expand Down
81 changes: 36 additions & 45 deletions Modules/_ctypes/callbacks.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,11 @@

/**************************************************************/

static void
CThunkObject_dealloc(PyObject *myself)
{
CThunkObject *self = (CThunkObject *)myself;
PyObject_GC_UnTrack(self);
Py_XDECREF(self->converters);
Py_XDECREF(self->callable);
Py_XDECREF(self->restype);
if (self->pcl_write)
Py_ffi_closure_free(self->pcl_write);
PyObject_GC_Del(self);
}

static int
CThunkObject_traverse(PyObject *myself, visitproc visit, void *arg)
{
CThunkObject *self = (CThunkObject *)myself;
Py_VISIT(Py_TYPE(self));
Py_VISIT(self->converters);
Py_VISIT(self->callable);
Py_VISIT(self->restype);
Expand All @@ -61,36 +49,35 @@ CThunkObject_clear(PyObject *myself)
return 0;
}

PyTypeObject PyCThunk_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_ctypes.CThunkObject",
sizeof(CThunkObject), /* tp_basicsize */
sizeof(ffi_type), /* tp_itemsize */
CThunkObject_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
PyDoc_STR("CThunkObject"), /* tp_doc */
CThunkObject_traverse, /* tp_traverse */
CThunkObject_clear, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
static void
CThunkObject_dealloc(PyObject *myself)
{
CThunkObject *self = (CThunkObject *)myself;
PyTypeObject *tp = Py_TYPE(myself);
PyObject_GC_UnTrack(self);
(void)CThunkObject_clear(myself);
if (self->pcl_write) {
Py_ffi_closure_free(self->pcl_write);
}
PyObject_GC_Del(self);
Py_DECREF(tp);
}

static PyType_Slot cthunk_slots[] = {
{Py_tp_doc, (void *)PyDoc_STR("CThunkObject")},
{Py_tp_dealloc, CThunkObject_dealloc},
{Py_tp_traverse, CThunkObject_traverse},
{Py_tp_clear, CThunkObject_clear},
{0, NULL},
};

PyType_Spec cthunk_spec = {
.name = "_ctypes.CThunkObject",
.basicsize = sizeof(CThunkObject),
.itemsize = sizeof(ffi_type),
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION),
.slots = cthunk_slots,
};

/**************************************************************/
Expand Down Expand Up @@ -320,7 +307,8 @@ static CThunkObject* CThunkObject_new(Py_ssize_t nargs)
CThunkObject *p;
Py_ssize_t i;

p = PyObject_GC_NewVar(CThunkObject, &PyCThunk_Type, nargs);
ctypes_state *st = GLOBAL_STATE();
p = PyObject_GC_NewVar(CThunkObject, st->PyCThunk_Type, nargs);
if (p == NULL) {
return NULL;
}
Expand Down Expand Up @@ -357,7 +345,10 @@ CThunkObject *_ctypes_alloc_callback(PyObject *callable,
if (p == NULL)
return NULL;

assert(CThunk_CheckExact((PyObject *)p));
#ifdef Py_DEBUG
ctypes_state *st = GLOBAL_STATE();
assert(CThunk_CheckExact(st, (PyObject *)p));
#endif

p->pcl_write = Py_ffi_closure_alloc(sizeof(ffi_closure), &p->pcl_exec);
if (p->pcl_write == NULL) {
Expand Down
Loading

0 comments on commit e9c7772

Please sign in to comment.