Commits

Armin Rigo committed 00f0e57

minibuffer: weakref support, and keep alive the argument.

  • Participants
  • Parent commits c2d0ff4

Comments (0)

Files changed (3)

c/_cffi_backend.c

         return NULL;
     }
     /*WRITE(cd->c_data, size)*/
-    return minibuffer_new(cd->c_data, size);
+    return minibuffer_new(cd->c_data, size, (PyObject *)cd);
 }
 
 static PyObject *b_get_errno(PyObject *self, PyObject *noarg)
     PyObject_HEAD
     char      *mb_data;
     Py_ssize_t mb_size;
+    PyObject  *mb_keepalive;
+    PyObject  *mb_weakreflist;    /* weakref support */
 } MiniBufferObj;
 
 static Py_ssize_t mb_length(MiniBufferObj *self)
     (releasebufferproc)0,
 };
 
+static void
+mb_dealloc(MiniBufferObj *ob)
+{
+    PyObject_GC_UnTrack(ob);
+    if (ob->mb_weakreflist != NULL)
+        PyObject_ClearWeakRefs((PyObject *)ob);
+    Py_XDECREF(ob->mb_keepalive);
+    Py_TYPE(ob)->tp_free((PyObject *)ob);
+}
+
+static int
+mb_traverse(MiniBufferObj *ob, visitproc visit, void *arg)
+{
+    Py_VISIT(ob->mb_keepalive);
+    return 0;
+}
+
+static int
+mb_clear(MiniBufferObj *ob)
+{
+    Py_CLEAR(ob->mb_keepalive);
+    return 0;
+}
+
 #if PY_MAJOR_VERSION >= 3
 /* pfffffffffffff pages of copy-paste from listobject.c */
 static PyObject *mb_subscript(MiniBufferObj *self, PyObject *item)
 #endif
 
 #if PY_MAJOR_VERSION >= 3
-# define MINIBUF_TPFLAGS (Py_TPFLAGS_DEFAULT)
+# define MINIBUF_TPFLAGS 0
 #else
-# define MINIBUF_TPFLAGS (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GETCHARBUFFER | Py_TPFLAGS_HAVE_NEWBUFFER)
+# define MINIBUF_TPFLAGS (Py_TPFLAGS_HAVE_GETCHARBUFFER | Py_TPFLAGS_HAVE_NEWBUFFER)
 #endif
 
 static PyTypeObject MiniBuffer_Type = {
     "_cffi_backend.buffer",
     sizeof(MiniBufferObj),
     0,
-    (destructor)PyObject_Del,                   /* tp_dealloc */
+    (destructor)mb_dealloc,                     /* tp_dealloc */
     0,                                          /* tp_print */
     0,                                          /* tp_getattr */
     0,                                          /* tp_setattr */
     PyObject_GenericGetAttr,                    /* tp_getattro */
     0,                                          /* tp_setattro */
     &mb_as_buffer,                              /* tp_as_buffer */
-    MINIBUF_TPFLAGS,                            /* tp_flags */
+    (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
+        MINIBUF_TPFLAGS),                       /* tp_flags */
+    0,                                          /* tp_doc */
+    (traverseproc)mb_traverse,                  /* tp_traverse */
+    (inquiry)mb_clear,                          /* tp_clear */
+    0,                                          /* tp_richcompare */
+    offsetof(MiniBufferObj, mb_weakreflist),    /* tp_weaklistoffset */
 };
 
-static PyObject *minibuffer_new(char *data, Py_ssize_t size)
+static PyObject *minibuffer_new(char *data, Py_ssize_t size,
+                                PyObject *keepalive)
 {
-    MiniBufferObj *ob = PyObject_New(MiniBufferObj, &MiniBuffer_Type);
+    MiniBufferObj *ob = PyObject_GC_New(MiniBufferObj, &MiniBuffer_Type);
     if (ob != NULL) {
         ob->mb_data = data;
         ob->mb_size = size;
+        ob->mb_keepalive = keepalive; Py_INCREF(keepalive);
+        ob->mb_weakreflist = NULL;
+        PyObject_GC_Track(ob);
     }
     return (PyObject *)ob;
 }
     import _weakref
     BInt = new_primitive_type("int")
     BPtr = new_pointer_type(BInt)
-    _weakref.ref(BInt)
-    _weakref.ref(newp(BPtr, 42))
-    _weakref.ref(cast(BPtr, 42))
-    _weakref.ref(cast(BInt, 42))
+    rlist = [_weakref.ref(BInt),
+             _weakref.ref(newp(BPtr, 42)),
+             _weakref.ref(cast(BPtr, 42)),
+             _weakref.ref(cast(BInt, 42)),
+             _weakref.ref(buffer(newp(BPtr, 42))),
+             ]
+    for i in range(5):
+        import gc; gc.collect()
+        if [r() for r in rlist] == [None for r in rlist]:
+            break
 
 def test_no_inheritance():
     BInt = new_primitive_type("int")
     BCharP = new_pointer_type(new_primitive_type("char"))
     BCharArray = new_array_type(BCharP, None)
     py.test.raises(TypeError, newp, BCharArray, u+'foobar')
+
+def test_buffer_keepalive():
+    BCharP = new_pointer_type(new_primitive_type("char"))
+    BCharArray = new_array_type(BCharP, None)
+    buflist = []
+    for i in range(20):
+        c = newp(BCharArray, b"hi there %d" % i)
+        buflist.append(buffer(c))
+    import gc; gc.collect()
+    for i in range(20):
+        buf = buflist[i]
+        assert buf[:] == b"hi there %d\x00" % i