Skip to content

Instantly share code, notes, and snippets.

@aliles
Created July 14, 2012 12:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aliles/3110849 to your computer and use it in GitHub Desktop.
Save aliles/3110849 to your computer and use it in GitHub Desktop.
Wrapper for cffi cdata objects to enable coownership of memory.
"""Wrapper for cffi cdata objects to enable coownership of memory.
When a cdata object that owns memory is used to set a pointer in a C structure
owned by another cdata object, references to both cdata objects must be held to
prevent memory from being released early.
Coownership enables the C structure's cdata object to hold a reference to the
cdata owner the memory it has been initialised with. Preventing the memory from
being released until it itself is garbage collected.
"""
import functools
import types
import weakref
import cffi
class CdataOwner(object):
"""CData wrapper that adds coownership of cdata objects.
A reference to any object assigned to an attribute of the wrapped cdata
object is retained for as long as this object is alive. This is implemented
using a WeakKeyDictionary.
Any cdata attributes access through this wrapper will themselves be wrapped
before being returned.
NOTE: CDataOwner objects can not be passed directly to cffi foreign
functions. To access the wrapped cdata object, call the '__unwrap__()'
function.
"""
__REFS__ = weakref.WeakKeyDictionary()
@staticmethod
def add_coownership(ffi):
"""Add coownership to a cffi.FFI instance.
Coownership can be enabled by passing the new 'coown' keyword argument to
cffi.FFI().new().
Once initialisation is complete, the wrapping can be discarded by
calling '__unwrap__' on the wrapper object. This is safe to do because
coownership is referenced using the cdata object, not the wrapper.
"""
orig_new = ffi.new
def new(ffi, cdecl, init=None, coown=False):
obj = orig_new(cdecl, init)
if coown:
obj = CdataOwner(obj)
return obj
ffi.new = types.MethodType(new, ffi, cffi.FFI)
def __init__(self, this, root=None):
root = this if root is None else root
self.__dict__['__this__'] = this
self.__dict__['__root__'] = root
self.__dict__['__refs__'] = self.__REFS__.setdefault(root, {})
def __getattr__(self, name):
cdata = getattr(self.__this__, name)
return CdataOwner(cdata, root=self.__root__)
def __setattr__(self, name, value):
setattr(self.__this__, name, value)
self.__refs__[name] = value
def __call__(self, *args):
return self.__this__.__call__(*args)
def __getitem__(self, key):
return self.__this__.__getitem__(key)
def __repr__(self):
return self.__this__.__repr__()
def __setitem__(self, key, value):
return self.__this__.__setitem__(key, value)
def __str__(self):
return self.__this__.__str__()
def __unicode__(self):
return self.__this__.__unicode__()
def __unwrap__(self):
return self.__this__
if __name__ == '__main__':
ffi = cffi.FFI()
CdataOwner.add_coownership(ffi)
ffi.cdef("""
struct passwd {
char *pw_name;
char *pw_passwd;
...;
};
""")
pwd = ffi.verify("""
#include <sys/types.h>
#include <pwd.h>
""")
def create_passwd(name, passwd):
struct = ffi.new('struct passwd', coown=True)
struct.pw_name = ffi.new('char[]', name)
struct.pw_passwd = ffi.new('char[]', passwd)
return struct.__unwrap__()
if __name__ == '__main__':
passwd = create_passwd('me', 'mypassword')
print passwd.pw_name, passwd.pw_passwd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment