Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r793:a8efbdc7c1cc Date: 2012-08-09 11:34 +0200 http://bitbucket.org/cffi/cffi/changeset/a8efbdc7c1cc/
Log: Finally found out the "right" way to implement ffi.gc(), in just a few lines of Python code using weakrefs with callbacks. diff --git a/cffi/api.py b/cffi/api.py --- a/cffi/api.py +++ b/cffi/api.py @@ -234,6 +234,18 @@ replace_with = ' ' + replace_with return self._backend.getcname(cdecl, replace_with) + def gc(self, cdata, destructor): + """Return a new cdata object that points to the same + data. Later, when this new cdata object is garbage-collected, + 'destructor(old_cdata_object)' will be called. + """ + try: + gc_weakrefs = self.gc_weakrefs + except AttributeError: + from .gc_weakref import GcWeakrefs + gc_weakrefs = self.gc_weakrefs = GcWeakrefs(self) + return gc_weakrefs.build(cdata, destructor) + def _get_cached_btype(self, type): try: BType = self._cached_btypes[type] diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py --- a/cffi/backend_ctypes.py +++ b/cffi/backend_ctypes.py @@ -2,7 +2,7 @@ from . import model class CTypesData(object): - __slots__ = [] + __slots__ = ['__weakref__'] def __init__(self, *args): raise TypeError("cannot instantiate %r" % (self.__class__,)) diff --git a/cffi/gc_weakref.py b/cffi/gc_weakref.py new file mode 100644 --- /dev/null +++ b/cffi/gc_weakref.py @@ -0,0 +1,19 @@ +from weakref import ref + + +class GcWeakrefs(object): + # code copied and adapted from WeakKeyDictionary. + + def __init__(self, ffi): + self.ffi = ffi + self.data = data = {} + def remove(k): + destructor, cdata = data.pop(k) + destructor(cdata) + self.remove = remove + + def build(self, cdata, destructor): + # make a new cdata of the same type as the original one + new_cdata = self.ffi.cast(self.ffi.typeof(cdata), cdata) + self.data[ref(new_cdata, self.remove)] = destructor, cdata + return new_cdata diff --git a/doc/source/index.rst b/doc/source/index.rst --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -914,6 +914,14 @@ ``ffi.getcname(ffi.typeof(x), "*")`` returns the string representation of the C type "pointer to the same type than x". +``ffi.gc(cdata, destructor)``: return a new cdata object that points to the +same data. Later, when this new cdata object is garbage-collected, +``destructor(old_cdata_object)`` will be called. Example of usage: +``ptr = ffi.gc(lib.malloc(42), lib.free)``. *New in version 0.3* (together +with the fact that any cdata object can be weakly referenced). + +.. "versionadded:: 0.3" --- inlined in the previous paragraph + Unimplemented features ---------------------- diff --git a/testing/backend_tests.py b/testing/backend_tests.py --- a/testing/backend_tests.py +++ b/testing/backend_tests.py @@ -1279,3 +1279,18 @@ q = ffi.cast("int[3]", p) assert q[0] == -5 assert repr(q).startswith("<cdata 'int[3]' 0x") + + def test_gc(self): + ffi = FFI(backend=self.Backend()) + p = ffi.new("int *", 123) + seen = [] + def destructor(p1): + assert p1 is p + assert p1[0] == 123 + seen.append(1) + q = ffi.gc(p, destructor) + import gc; gc.collect() + assert seen == [] + del q + import gc; gc.collect(); gc.collect(); gc.collect() + assert seen == [1] _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit