Skip to content

Commit b5bf8c8

Browse files
pythongh-132776: Minor Fixes for XIBufferViewType (pythongh-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
1 parent a4ea80d commit b5bf8c8

File tree

1 file changed

+72
-21
lines changed

1 file changed

+72
-21
lines changed

Modules/_interpretersmodule.c

+72-21
Original file line numberDiff line numberDiff line change
@@ -76,41 +76,60 @@ is_running_main(PyInterpreterState *interp)
7676
// XXX Release when the original interpreter is destroyed.
7777

7878
typedef struct {
79-
PyObject_HEAD
79+
PyObject base;
8080
Py_buffer *view;
8181
int64_t interpid;
8282
} XIBufferViewObject;
8383

8484
#define XIBufferViewObject_CAST(op) ((XIBufferViewObject *)(op))
8585

8686
static PyObject *
87-
xibufferview_from_xid(PyTypeObject *cls, _PyXIData_t *data)
87+
xibufferview_from_buffer(PyTypeObject *cls, Py_buffer *view, int64_t interpid)
8888
{
89-
assert(_PyXIData_DATA(data) != NULL);
90-
assert(_PyXIData_OBJ(data) == NULL);
91-
assert(_PyXIData_INTERPID(data) >= 0);
89+
assert(interpid >= 0);
90+
91+
Py_buffer *copied = PyMem_RawMalloc(sizeof(Py_buffer));
92+
if (copied == NULL) {
93+
return NULL;
94+
}
95+
/* This steals the view->obj reference */
96+
*copied = *view;
97+
9298
XIBufferViewObject *self = PyObject_Malloc(sizeof(XIBufferViewObject));
9399
if (self == NULL) {
100+
PyMem_RawFree(copied);
94101
return NULL;
95102
}
96-
PyObject_Init((PyObject *)self, cls);
97-
self->view = (Py_buffer *)_PyXIData_DATA(data);
98-
self->interpid = _PyXIData_INTERPID(data);
103+
PyObject_Init(&self->base, cls);
104+
*self = (XIBufferViewObject){
105+
.base = self->base,
106+
.view = copied,
107+
.interpid = interpid,
108+
};
99109
return (PyObject *)self;
100110
}
101111

102112
static void
103113
xibufferview_dealloc(PyObject *op)
104114
{
105115
XIBufferViewObject *self = XIBufferViewObject_CAST(op);
106-
PyInterpreterState *interp = _PyInterpreterState_LookUpID(self->interpid);
107-
/* If the interpreter is no longer alive then we have problems,
108-
since other objects may be using the buffer still. */
109-
assert(interp != NULL);
110116

111-
if (_PyBuffer_ReleaseInInterpreterAndRawFree(interp, self->view) < 0) {
112-
// XXX Emit a warning?
113-
PyErr_Clear();
117+
if (self->view != NULL) {
118+
PyInterpreterState *interp =
119+
_PyInterpreterState_LookUpID(self->interpid);
120+
if (interp == NULL) {
121+
/* The interpreter is no longer alive. */
122+
PyErr_Clear();
123+
PyMem_RawFree(self->view);
124+
}
125+
else {
126+
if (_PyBuffer_ReleaseInInterpreterAndRawFree(interp,
127+
self->view) < 0)
128+
{
129+
// XXX Emit a warning?
130+
PyErr_Clear();
131+
}
132+
}
114133
}
115134

116135
PyTypeObject *tp = Py_TYPE(self);
@@ -155,32 +174,64 @@ static PyType_Spec XIBufferViewType_spec = {
155174

156175
static PyTypeObject * _get_current_xibufferview_type(void);
157176

177+
178+
struct xibuffer {
179+
Py_buffer view;
180+
int used;
181+
};
182+
158183
static PyObject *
159184
_memoryview_from_xid(_PyXIData_t *data)
160185
{
186+
assert(_PyXIData_DATA(data) != NULL);
187+
assert(_PyXIData_OBJ(data) == NULL);
188+
assert(_PyXIData_INTERPID(data) >= 0);
189+
struct xibuffer *view = (struct xibuffer *)_PyXIData_DATA(data);
190+
assert(!view->used);
191+
161192
PyTypeObject *cls = _get_current_xibufferview_type();
162193
if (cls == NULL) {
163194
return NULL;
164195
}
165-
PyObject *obj = xibufferview_from_xid(cls, data);
196+
197+
PyObject *obj = xibufferview_from_buffer(
198+
cls, &view->view, _PyXIData_INTERPID(data));
166199
if (obj == NULL) {
167200
return NULL;
168201
}
169-
return PyMemoryView_FromObject(obj);
202+
PyObject *res = PyMemoryView_FromObject(obj);
203+
if (res == NULL) {
204+
Py_DECREF(obj);
205+
return NULL;
206+
}
207+
view->used = 1;
208+
return res;
209+
}
210+
211+
static void
212+
_pybuffer_shared_free(void* data)
213+
{
214+
struct xibuffer *view = (struct xibuffer *)data;
215+
if (!view->used) {
216+
PyBuffer_Release(&view->view);
217+
}
218+
PyMem_RawFree(data);
170219
}
171220

172221
static int
173-
_memoryview_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data)
222+
_pybuffer_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data)
174223
{
175-
Py_buffer *view = PyMem_RawMalloc(sizeof(Py_buffer));
224+
struct xibuffer *view = PyMem_RawMalloc(sizeof(struct xibuffer));
176225
if (view == NULL) {
177226
return -1;
178227
}
179-
if (PyObject_GetBuffer(obj, view, PyBUF_FULL_RO) < 0) {
228+
view->used = 0;
229+
if (PyObject_GetBuffer(obj, &view->view, PyBUF_FULL_RO) < 0) {
180230
PyMem_RawFree(view);
181231
return -1;
182232
}
183233
_PyXIData_Init(data, tstate->interp, view, NULL, _memoryview_from_xid);
234+
data->free = _pybuffer_shared_free;
184235
return 0;
185236
}
186237

@@ -201,7 +252,7 @@ register_memoryview_xid(PyObject *mod, PyTypeObject **p_state)
201252
*p_state = cls;
202253

203254
// Register XID for the builtin memoryview type.
204-
if (ensure_xid_class(&PyMemoryView_Type, _memoryview_shared) < 0) {
255+
if (ensure_xid_class(&PyMemoryView_Type, _pybuffer_shared) < 0) {
205256
return -1;
206257
}
207258
// We don't ever bother un-registering memoryview.

0 commit comments

Comments
 (0)