@@ -59,6 +59,9 @@ typedef struct PySlot_Offset {
59
59
static void
60
60
slot_bf_releasebuffer (PyObject * self , Py_buffer * buffer );
61
61
62
+ static void
63
+ releasebuffer_call_python (PyObject * self , Py_buffer * buffer );
64
+
62
65
static PyObject *
63
66
slot_tp_new (PyTypeObject * type , PyObject * args , PyObject * kwds );
64
67
@@ -8097,6 +8100,10 @@ wrap_releasebuffer(PyObject *self, PyObject *args, void *wrapped)
8097
8100
return NULL ;
8098
8101
}
8099
8102
PyMemoryViewObject * mview = (PyMemoryViewObject * )arg ;
8103
+ if (mview -> view .obj == NULL ) {
8104
+ // Already released, ignore
8105
+ Py_RETURN_NONE ;
8106
+ }
8100
8107
if (mview -> view .obj != self ) {
8101
8108
PyErr_SetString (PyExc_ValueError ,
8102
8109
"memoryview's buffer is not this object" );
@@ -8985,9 +8992,10 @@ bufferwrapper_releasebuf(PyObject *self, Py_buffer *view)
8985
8992
Py_TYPE (bw -> mv )-> tp_as_buffer -> bf_releasebuffer (bw -> mv , view );
8986
8993
// We only need to call bf_releasebuffer if it's a Python function. If it's a C
8987
8994
// bf_releasebuf, it will be called when the memoryview is released.
8988
- if (Py_TYPE (bw -> obj )-> tp_as_buffer != NULL
8989
- && Py_TYPE (bw -> obj )-> tp_as_buffer -> bf_releasebuffer == slot_bf_releasebuffer ) {
8990
- Py_TYPE (bw -> obj )-> tp_as_buffer -> bf_releasebuffer (bw -> obj , view );
8995
+ if (((PyMemoryViewObject * )bw -> mv )-> view .obj != bw -> obj
8996
+ && Py_TYPE (bw -> obj )-> tp_as_buffer != NULL
8997
+ && Py_TYPE (bw -> obj )-> tp_as_buffer -> bf_releasebuffer == slot_bf_releasebuffer ) {
8998
+ releasebuffer_call_python (bw -> obj , view );
8991
8999
}
8992
9000
}
8993
9001
@@ -9052,8 +9060,51 @@ slot_bf_getbuffer(PyObject *self, Py_buffer *buffer, int flags)
9052
9060
return -1 ;
9053
9061
}
9054
9062
9063
+ static int
9064
+ releasebuffer_maybe_call_super (PyObject * self , Py_buffer * buffer )
9065
+ {
9066
+ PyTypeObject * self_type = Py_TYPE (self );
9067
+ PyObject * mro = lookup_tp_mro (self_type );
9068
+ if (mro == NULL ) {
9069
+ return -1 ;
9070
+ }
9071
+
9072
+ assert (PyTuple_Check (mro ));
9073
+ Py_ssize_t n = PyTuple_GET_SIZE (mro );
9074
+ Py_ssize_t i ;
9075
+
9076
+ /* No need to check the last one: it's gonna be skipped anyway. */
9077
+ for (i = 0 ; i + 1 < n ; i ++ ) {
9078
+ if ((PyObject * )(self_type ) == PyTuple_GET_ITEM (mro , i ))
9079
+ break ;
9080
+ }
9081
+ i ++ ; /* skip self_type */
9082
+ if (i >= n )
9083
+ return -1 ;
9084
+
9085
+ releasebufferproc base_releasebuffer = NULL ;
9086
+ for (; i < n ; i ++ ) {
9087
+ PyObject * obj = PyTuple_GET_ITEM (mro , i );
9088
+ if (!PyType_Check (obj )) {
9089
+ continue ;
9090
+ }
9091
+ PyTypeObject * base_type = (PyTypeObject * )obj ;
9092
+ if (base_type -> tp_as_buffer != NULL
9093
+ && base_type -> tp_as_buffer -> bf_releasebuffer != NULL
9094
+ && base_type -> tp_as_buffer -> bf_releasebuffer != slot_bf_releasebuffer ) {
9095
+ base_releasebuffer = base_type -> tp_as_buffer -> bf_releasebuffer ;
9096
+ break ;
9097
+ }
9098
+ }
9099
+
9100
+ if (base_releasebuffer != NULL ) {
9101
+ base_releasebuffer (self , buffer );
9102
+ }
9103
+ return 0 ;
9104
+ }
9105
+
9055
9106
static void
9056
- slot_bf_releasebuffer (PyObject * self , Py_buffer * buffer )
9107
+ releasebuffer_call_python (PyObject * self , Py_buffer * buffer )
9057
9108
{
9058
9109
PyObject * mv ;
9059
9110
if (Py_TYPE (buffer -> obj ) == & _PyBufferWrapper_Type ) {
@@ -9079,6 +9130,28 @@ slot_bf_releasebuffer(PyObject *self, Py_buffer *buffer)
9079
9130
}
9080
9131
}
9081
9132
9133
+ /*
9134
+ * bf_releasebuffer is very delicate, because we need to ensure that
9135
+ * C bf_releasebuffer slots are called correctly (or we'll leak memory),
9136
+ * but we cannot trust any __release_buffer__ implemented in Python to
9137
+ * do so correctly. Therefore, if a base class has a C bf_releasebuffer
9138
+ * slot, we call it directly here. That is safe because this function
9139
+ * only gets called from C callers of the bf_releasebuffer slot. Python
9140
+ * code that calls __release_buffer__ directly instead goes through
9141
+ * wrap_releasebuffer(), which doesn't call the bf_releasebuffer slot
9142
+ * directly but instead simply releases the associated memoryview.
9143
+ */
9144
+ static void
9145
+ slot_bf_releasebuffer (PyObject * self , Py_buffer * buffer )
9146
+ {
9147
+ if (releasebuffer_maybe_call_super (self , buffer ) < 0 ) {
9148
+ if (PyErr_Occurred ()) {
9149
+ PyErr_WriteUnraisable (self );
9150
+ }
9151
+ }
9152
+ releasebuffer_call_python (self , buffer );
9153
+ }
9154
+
9082
9155
static PyObject *
9083
9156
slot_am_await (PyObject * self )
9084
9157
{
0 commit comments