Created
July 14, 2012 12:01
-
-
Save aliles/3110849 to your computer and use it in GitHub Desktop.
Wrapper for cffi cdata objects to enable coownership of memory.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""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