https://github.com/python/cpython/commit/b5bf8c80a921679b23548453565f6fd1f79901f2
commit: b5bf8c80a921679b23548453565f6fd1f79901f2
branch: main
author: Eric Snow <[email protected]>
committer: ericsnowcurrently <[email protected]>
date: 2025-04-22T17:37:20-06:00
summary:
gh-132776: Minor Fixes for XIBufferViewType (gh-132779)
This change covers the following:
* dealloc: no cleanup if no buffer set
* dealloc: handle already-destroyed interpreter correctly
* handle errors in _memoryview_from_xid() correctly
* clean up the buffer if the xidata is never used
files:
M Modules/_interpretersmodule.c
diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c
index c444c4c32c71e3..d9c2d2bfd081f7 100644
--- a/Modules/_interpretersmodule.c
+++ b/Modules/_interpretersmodule.c
@@ -76,7 +76,7 @@ is_running_main(PyInterpreterState *interp)
// XXX Release when the original interpreter is destroyed.
typedef struct {
- PyObject_HEAD
+ PyObject base;
Py_buffer *view;
int64_t interpid;
} XIBufferViewObject;
@@ -84,18 +84,28 @@ typedef struct {
#define XIBufferViewObject_CAST(op) ((XIBufferViewObject *)(op))
static PyObject *
-xibufferview_from_xid(PyTypeObject *cls, _PyXIData_t *data)
+xibufferview_from_buffer(PyTypeObject *cls, Py_buffer *view, int64_t interpid)
{
- assert(_PyXIData_DATA(data) != NULL);
- assert(_PyXIData_OBJ(data) == NULL);
- assert(_PyXIData_INTERPID(data) >= 0);
+ assert(interpid >= 0);
+
+ Py_buffer *copied = PyMem_RawMalloc(sizeof(Py_buffer));
+ if (copied == NULL) {
+ return NULL;
+ }
+ /* This steals the view->obj reference */
+ *copied = *view;
+
XIBufferViewObject *self = PyObject_Malloc(sizeof(XIBufferViewObject));
if (self == NULL) {
+ PyMem_RawFree(copied);
return NULL;
}
- PyObject_Init((PyObject *)self, cls);
- self->view = (Py_buffer *)_PyXIData_DATA(data);
- self->interpid = _PyXIData_INTERPID(data);
+ PyObject_Init(&self->base, cls);
+ *self = (XIBufferViewObject){
+ .base = self->base,
+ .view = copied,
+ .interpid = interpid,
+ };
return (PyObject *)self;
}
@@ -103,14 +113,23 @@ static void
xibufferview_dealloc(PyObject *op)
{
XIBufferViewObject *self = XIBufferViewObject_CAST(op);
- PyInterpreterState *interp = _PyInterpreterState_LookUpID(self->interpid);
- /* If the interpreter is no longer alive then we have problems,
- since other objects may be using the buffer still. */
- assert(interp != NULL);
- if (_PyBuffer_ReleaseInInterpreterAndRawFree(interp, self->view) < 0) {
- // XXX Emit a warning?
- PyErr_Clear();
+ if (self->view != NULL) {
+ PyInterpreterState *interp =
+ _PyInterpreterState_LookUpID(self->interpid);
+ if (interp == NULL) {
+ /* The interpreter is no longer alive. */
+ PyErr_Clear();
+ PyMem_RawFree(self->view);
+ }
+ else {
+ if (_PyBuffer_ReleaseInInterpreterAndRawFree(interp,
+ self->view) < 0)
+ {
+ // XXX Emit a warning?
+ PyErr_Clear();
+ }
+ }
}
PyTypeObject *tp = Py_TYPE(self);
@@ -155,32 +174,64 @@ static PyType_Spec XIBufferViewType_spec = {
static PyTypeObject * _get_current_xibufferview_type(void);
+
+struct xibuffer {
+ Py_buffer view;
+ int used;
+};
+
static PyObject *
_memoryview_from_xid(_PyXIData_t *data)
{
+ assert(_PyXIData_DATA(data) != NULL);
+ assert(_PyXIData_OBJ(data) == NULL);
+ assert(_PyXIData_INTERPID(data) >= 0);
+ struct xibuffer *view = (struct xibuffer *)_PyXIData_DATA(data);
+ assert(!view->used);
+
PyTypeObject *cls = _get_current_xibufferview_type();
if (cls == NULL) {
return NULL;
}
- PyObject *obj = xibufferview_from_xid(cls, data);
+
+ PyObject *obj = xibufferview_from_buffer(
+ cls, &view->view, _PyXIData_INTERPID(data));
if (obj == NULL) {
return NULL;
}
- return PyMemoryView_FromObject(obj);
+ PyObject *res = PyMemoryView_FromObject(obj);
+ if (res == NULL) {
+ Py_DECREF(obj);
+ return NULL;
+ }
+ view->used = 1;
+ return res;
+}
+
+static void
+_pybuffer_shared_free(void* data)
+{
+ struct xibuffer *view = (struct xibuffer *)data;
+ if (!view->used) {
+ PyBuffer_Release(&view->view);
+ }
+ PyMem_RawFree(data);
}
static int
-_memoryview_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data)
+_pybuffer_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data)
{
- Py_buffer *view = PyMem_RawMalloc(sizeof(Py_buffer));
+ struct xibuffer *view = PyMem_RawMalloc(sizeof(struct xibuffer));
if (view == NULL) {
return -1;
}
- if (PyObject_GetBuffer(obj, view, PyBUF_FULL_RO) < 0) {
+ view->used = 0;
+ if (PyObject_GetBuffer(obj, &view->view, PyBUF_FULL_RO) < 0) {
PyMem_RawFree(view);
return -1;
}
_PyXIData_Init(data, tstate->interp, view, NULL, _memoryview_from_xid);
+ data->free = _pybuffer_shared_free;
return 0;
}
@@ -201,7 +252,7 @@ register_memoryview_xid(PyObject *mod, PyTypeObject
**p_state)
*p_state = cls;
// Register XID for the builtin memoryview type.
- if (ensure_xid_class(&PyMemoryView_Type, _memoryview_shared) < 0) {
+ if (ensure_xid_class(&PyMemoryView_Type, _pybuffer_shared) < 0) {
return -1;
}
// We don't ever bother un-registering memoryview.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]