Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c6b09eb

Browse files
committed
#3712: The memoryview object had a reference leak and didn't support cyclic garbage collection.
Reviewed by Benjamin Peterson.
1 parent d26782e commit c6b09eb

3 files changed

Lines changed: 101 additions & 34 deletions

File tree

Lib/test/test_memoryview.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import unittest
77
import test.support
88
import sys
9+
import gc
10+
import weakref
911

1012

1113
class CommonMemoryTests:
@@ -157,6 +159,36 @@ def test_attributes_writable(self):
157159
m = self.check_attributes_with_type(bytearray)
158160
self.assertEquals(m.readonly, False)
159161

162+
def test_getbuffer(self):
163+
# Test PyObject_GetBuffer() on a memoryview object.
164+
b = self.base_object
165+
oldrefcount = sys.getrefcount(b)
166+
m = self._view(b)
167+
oldviewrefcount = sys.getrefcount(m)
168+
s = str(m, "utf-8")
169+
self._check_contents(b, s.encode("utf-8"))
170+
self.assertEquals(sys.getrefcount(m), oldviewrefcount)
171+
m = None
172+
self.assertEquals(sys.getrefcount(b), oldrefcount)
173+
174+
def test_gc(self):
175+
class MyBytes(bytes):
176+
pass
177+
class MyObject:
178+
pass
179+
180+
# Create a reference cycle through a memoryview object
181+
b = MyBytes(b'abc')
182+
m = self._view(b)
183+
o = MyObject()
184+
b.m = m
185+
b.o = o
186+
wr = weakref.ref(o)
187+
b = m = o = None
188+
# The cycle must be broken
189+
gc.collect()
190+
self.assert_(wr() is None, wr())
191+
160192

161193
class MemoryviewTest(unittest.TestCase, CommonMemoryTests):
162194

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ What's New in Python 3.0 release candidate 1
1212
Core and Builtins
1313
-----------------
1414

15+
- Issue #3712: The memoryview object had a reference leak and didn't support
16+
cyclic garbage collection.
17+
1518
- Issue #3668: Fix a memory leak with the "s*" argument parser in
1619
PyArg_ParseTuple and friends, which occurred when the argument for "s*"
1720
was correctly parsed but parsing of subsequent arguments failed.

Objects/memoryobject.c

Lines changed: 66 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,34 @@
33

44
#include "Python.h"
55

6+
static void
7+
dup_buffer(Py_buffer *dest, Py_buffer *src)
8+
{
9+
*dest = *src;
10+
if (src->shape == &(src->len))
11+
dest->shape = &(dest->len);
12+
if (src->strides == &(src->itemsize))
13+
dest->strides = &(dest->itemsize);
14+
}
15+
616
static int
717
memory_getbuf(PyMemoryViewObject *self, Py_buffer *view, int flags)
818
{
9-
if (view != NULL) {
10-
if (self->view.obj)
11-
Py_INCREF(self->view.obj);
12-
*view = self->view;
13-
}
14-
if (self->view.obj == NULL)
15-
return 0;
16-
return self->view.obj->ob_type->tp_as_buffer->bf_getbuffer(
17-
self->view.obj, NULL, PyBUF_FULL);
19+
int res = 0;
20+
/* XXX for whatever reason fixing the flags seems necessary */
21+
if (self->view.readonly)
22+
flags &= ~PyBUF_WRITABLE;
23+
if (self->view.obj != NULL)
24+
res = PyObject_GetBuffer(self->view.obj, view, flags);
25+
if (view)
26+
dup_buffer(view, &self->view);
27+
return res;
1828
}
1929

2030
static void
2131
memory_releasebuf(PyMemoryViewObject *self, Py_buffer *view)
2232
{
23-
PyBuffer_Release(&self->view);
33+
PyBuffer_Release(view);
2434
}
2535

2636
PyDoc_STRVAR(memory_doc,
@@ -33,18 +43,15 @@ PyMemoryView_FromBuffer(Py_buffer *info)
3343
{
3444
PyMemoryViewObject *mview;
3545

36-
mview = (PyMemoryViewObject *)PyObject_New(PyMemoryViewObject,
37-
&PyMemoryView_Type);
38-
if (mview == NULL) return NULL;
46+
mview = (PyMemoryViewObject *)
47+
PyObject_GC_New(PyMemoryViewObject, &PyMemoryView_Type);
48+
if (mview == NULL)
49+
return NULL;
3950
mview->base = NULL;
40-
/* XXX there should be an API to duplicate a buffer object */
41-
mview->view = *info;
42-
if (info->shape == &(info->len))
43-
mview->view.shape = &(mview->view.len);
44-
if (info->strides == &(info->itemsize))
45-
mview->view.strides = &(mview->view.itemsize);
51+
dup_buffer(&mview->view, info);
4652
/* NOTE: mview->view.obj should already have been incref'ed as
4753
part of PyBuffer_FillInfo(). */
54+
_PyObject_GC_TRACK(mview);
4855
return (PyObject *)mview;
4956
}
5057

@@ -60,9 +67,10 @@ PyMemoryView_FromObject(PyObject *base)
6067
return NULL;
6168
}
6269

63-
mview = (PyMemoryViewObject *)PyObject_New(PyMemoryViewObject,
64-
&PyMemoryView_Type);
65-
if (mview == NULL) return NULL;
70+
mview = (PyMemoryViewObject *)
71+
PyObject_GC_New(PyMemoryViewObject, &PyMemoryView_Type);
72+
if (mview == NULL)
73+
return NULL;
6674

6775
mview->base = NULL;
6876
if (PyObject_GetBuffer(base, &(mview->view), PyBUF_FULL_RO) < 0) {
@@ -72,6 +80,7 @@ PyMemoryView_FromObject(PyObject *base)
7280

7381
mview->base = base;
7482
Py_INCREF(base);
83+
_PyObject_GC_TRACK(mview);
7584
return (PyObject *)mview;
7685
}
7786

@@ -233,8 +242,9 @@ PyMemoryView_GetContiguous(PyObject *obj, int buffertype, char fort)
233242
return NULL;
234243
}
235244

236-
mem = PyObject_New(PyMemoryViewObject, &PyMemoryView_Type);
237-
if (mem == NULL) return NULL;
245+
mem = PyObject_GC_New(PyMemoryViewObject, &PyMemoryView_Type);
246+
if (mem == NULL)
247+
return NULL;
238248

239249
view = &mem->view;
240250
flags = PyBUF_FULL_RO;
@@ -245,27 +255,28 @@ PyMemoryView_GetContiguous(PyObject *obj, int buffertype, char fort)
245255
}
246256

247257
if (PyObject_GetBuffer(obj, view, flags) != 0) {
248-
PyObject_DEL(mem);
258+
Py_DECREF(mem);
249259
return NULL;
250260
}
251261

252262
if (PyBuffer_IsContiguous(view, fort)) {
253263
/* no copy needed */
254264
Py_INCREF(obj);
255265
mem->base = obj;
266+
_PyObject_GC_TRACK(mem);
256267
return (PyObject *)mem;
257268
}
258269
/* otherwise a copy is needed */
259270
if (buffertype == PyBUF_WRITE) {
260-
PyObject_DEL(mem);
271+
Py_DECREF(mem);
261272
PyErr_SetString(PyExc_BufferError,
262273
"writable contiguous buffer requested "
263274
"for a non-contiguousobject.");
264275
return NULL;
265276
}
266277
bytes = PyBytes_FromStringAndSize(NULL, view->len);
267278
if (bytes == NULL) {
268-
PyBuffer_Release(view);
279+
Py_DECREF(mem);
269280
return NULL;
270281
}
271282
dest = PyBytes_AS_STRING(bytes);
@@ -280,7 +291,7 @@ PyMemoryView_GetContiguous(PyObject *obj, int buffertype, char fort)
280291
else {
281292
if (_indirect_copy_nd(dest, view, fort) < 0) {
282293
Py_DECREF(bytes);
283-
PyBuffer_Release(view);
294+
Py_DECREF(mem);
284295
return NULL;
285296
}
286297
}
@@ -290,15 +301,16 @@ PyMemoryView_GetContiguous(PyObject *obj, int buffertype, char fort)
290301
mem->base = PyTuple_Pack(2, obj, bytes);
291302
Py_DECREF(bytes);
292303
if (mem->base == NULL) {
293-
PyBuffer_Release(view);
304+
Py_DECREF(mem);
294305
return NULL;
295306
}
296307
}
297308
else {
298-
PyBuffer_Release(view);
309+
PyBuffer_Release(view); /* XXX ? */
299310
/* steal the reference */
300311
mem->base = bytes;
301312
}
313+
_PyObject_GC_TRACK(mem);
302314
return (PyObject *)mem;
303315
}
304316

@@ -444,6 +456,7 @@ static PyMethodDef memory_methods[] = {
444456
static void
445457
memory_dealloc(PyMemoryViewObject *self)
446458
{
459+
_PyObject_GC_UNTRACK(self);
447460
if (self->view.obj != NULL) {
448461
if (self->base && PyTuple_Check(self->base)) {
449462
/* Special case when first element is generic object
@@ -468,7 +481,7 @@ memory_dealloc(PyMemoryViewObject *self)
468481
}
469482
Py_CLEAR(self->base);
470483
}
471-
PyObject_DEL(self);
484+
PyObject_GC_Del(self);
472485
}
473486

474487
static PyObject *
@@ -749,6 +762,25 @@ memory_richcompare(PyObject *v, PyObject *w, int op)
749762
}
750763

751764

765+
static int
766+
memory_traverse(PyMemoryViewObject *self, visitproc visit, void *arg)
767+
{
768+
if (self->base != NULL)
769+
Py_VISIT(self->base);
770+
if (self->view.obj != NULL)
771+
Py_VISIT(self->view.obj);
772+
return 0;
773+
}
774+
775+
static int
776+
memory_clear(PyMemoryViewObject *self)
777+
{
778+
Py_CLEAR(self->base);
779+
PyBuffer_Release(&self->view);
780+
return 0;
781+
}
782+
783+
752784
/* As mapping */
753785
static PyMappingMethods memory_as_mapping = {
754786
(lenfunc)memory_length, /*mp_length*/
@@ -785,10 +817,10 @@ PyTypeObject PyMemoryView_Type = {
785817
PyObject_GenericGetAttr, /* tp_getattro */
786818
0, /* tp_setattro */
787819
&memory_as_buffer, /* tp_as_buffer */
788-
Py_TPFLAGS_DEFAULT, /* tp_flags */
820+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
789821
memory_doc, /* tp_doc */
790-
0, /* tp_traverse */
791-
0, /* tp_clear */
822+
(traverseproc)memory_traverse, /* tp_traverse */
823+
(inquiry)memory_clear, /* tp_clear */
792824
memory_richcompare, /* tp_richcompare */
793825
0, /* tp_weaklistoffset */
794826
0, /* tp_iter */

0 commit comments

Comments
 (0)