https://github.com/python/cpython/commit/b5bf8c80a921679b23548453565f6fd1f79901f2 commit: b5bf8c80a921679b23548453565f6fd1f79901f2 branch: main author: Eric Snow <ericsnowcurren...@gmail.com> committer: ericsnowcurrently <ericsnowcurren...@gmail.com> 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 -- python-checkins@python.org To unsubscribe send an email to python-checkins-le...@python.org https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: arch...@mail-archive.com