Have cffi.buffer() objects hold a reference to another object.

Issue #56 resolved
Simon Sapin
created an issue

I just got bitten by this:

empty_pixels = ImageSurface('ARGB32', 100, 100).get_data()[:]
assert empty_pixels == b'\x00' * (4 * 100 * 100)

.get_data() return a buffer object from ffi.buffer() base on memory allocated by the surface. On CPython, by the time the buffer is sliced to get a byte string, the surface has been freed and I’m getting garbage from random memory.

I’d like ffi.buffer() to take an optional third argument, a Python object that is referenced by the returned buffer. This mechanism would help ensure that the memory pointed to by the buffer stays valid at least as long as the buffer itself.

Without help from CFFI I guess I could do this with a global WeakKeyDictionary but that feel like it shouldn’t be necessary.

Comments (8)

  1. Armin Rigo

    Fixed the TypeError.

    The ability to pass a 3rd argument to keep alive Python objects is part of the general issue of how long objects should stay alive. I did an intermediate solution (see a44545d70be4): if you call ffi.buffer(cdata), and cdata is an object that owns memory, then the memory owned will not be released before the returned buffer object is. Is that enough?

  2. Simon Sapin reporter

    This is a start, but not always enough. For example, cairo’s image surface allocate its own memory, so there is no cdata object that own the memory.

    What would work in this case is ffi.buffer(ffi.gc(cdata, destructor), size), if the buffer keeps the "gc" object alive and the destructor is not called while the buffer object is alive. Since the destruction can also hold Python references in addition to being callable, this would also cover my original request.

  3. Armin Rigo

    Uh, sorry, I was a bit confused. What I meant originally is that ffi.buffer(cdata) now returns a buffer object that has always got a reference to the object cdata. This in turn means that ffi.buffer(ffi.gc(...)) will keep a reference to the object returned by ffi.gc(), and so while the buffer object is alive, the destructor will not be called.

  4. Log in to comment