Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r2685:381f8b0c2ee0 Date: 2016-04-24 13:46 +0200 http://bitbucket.org/cffi/cffi/changeset/381f8b0c2ee0/
Log: merge heads diff --git a/AUTHORS b/AUTHORS --- a/AUTHORS +++ b/AUTHORS @@ -1,3 +1,8 @@ This package has been mostly done by Armin Rigo with help from Maciej Fijałkowski. The idea is heavily based (although not directly copied) from LuaJIT ffi by Mike Pall. + + +Other contributors: + + Google Inc. diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -6065,6 +6065,17 @@ &CData_Type, &origobj, &destructor)) return NULL; + if (destructor == Py_None) { + if (!PyObject_TypeCheck(origobj, &CDataGCP_Type)) { + PyErr_SetString(PyExc_TypeError, + "Can remove destructor only on a object " + "previously returned by ffi.gc()"); + return NULL; + } + Py_CLEAR(((CDataObject_gcp *)origobj)->destructor); + Py_RETURN_NONE; + } + cd = allocate_gcp_object(origobj, origobj->c_type, destructor); return (PyObject *)cd; } diff --git a/cffi/api.py b/cffi/api.py --- a/cffi/api.py +++ b/cffi/api.py @@ -397,20 +397,7 @@ data. Later, when this new cdata object is garbage-collected, 'destructor(old_cdata_object)' will be called. """ - try: - gcp = self._backend.gcp - except AttributeError: - pass - else: - return gcp(cdata, destructor) - # - with self._lock: - 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) + return self._backend.gcp(cdata, destructor) def _get_cached_btype(self, type): assert self._lock.acquire(False) is False diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py --- a/cffi/backend_ctypes.py +++ b/cffi/backend_ctypes.py @@ -993,6 +993,31 @@ assert onerror is None # XXX not implemented return BType(source, error) + def gcp(self, cdata, destructor): + BType = self.typeof(cdata) + + if destructor is None: + if not (hasattr(BType, '_gcp_type') and + BType._gcp_type is BType): + raise TypeError("Can remove destructor only on a object " + "previously returned by ffi.gc()") + cdata._destructor = None + return None + + try: + gcp_type = BType._gcp_type + except AttributeError: + class CTypesDataGcp(BType): + __slots__ = ['_orig', '_destructor'] + def __del__(self): + if self._destructor is not None: + self._destructor(self._orig) + gcp_type = BType._gcp_type = CTypesDataGcp + new_cdata = self.cast(gcp_type, cdata) + new_cdata._orig = cdata + new_cdata._destructor = destructor + return new_cdata + typeof = type def getcname(self, BType, replace_with): diff --git a/cffi/gc_weakref.py b/cffi/gc_weakref.py deleted file mode 100644 --- a/cffi/gc_weakref.py +++ /dev/null @@ -1,22 +0,0 @@ -from weakref import ref - - -class GcWeakrefs(object): - def __init__(self, ffi): - self.ffi = ffi - self.data = {} - - def build(self, cdata, destructor): - # make a new cdata of the same type as the original one - new_cdata = self.ffi.cast(self.ffi._backend.typeof(cdata), cdata) - # - def remove(key): - # careful, this function is not protected by any lock - old_key = self.data.pop(index) - assert old_key is key - destructor(cdata) - # - key = ref(new_cdata, remove) - index = object() - self.data[index] = key - return new_cdata diff --git a/doc/source/ref.rst b/doc/source/ref.rst --- a/doc/source/ref.rst +++ b/doc/source/ref.rst @@ -318,6 +318,11 @@ which means the destructor is called as soon as *this* exact returned object is garbage-collected. +**ffi.gc(ptr, None)**: removes the ownership on a object returned by a +regular call to ``ffi.gc``, and no destructor will be called when it +is garbage-collected. The object is modified in-place, and the +function returns ``None``. + Note that this should be avoided for large memory allocations or for limited resources. This is particularly true on PyPy: its GC does not know how much memory or how many resources the returned ``ptr`` diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst --- a/doc/source/whatsnew.rst +++ b/doc/source/whatsnew.rst @@ -3,6 +3,13 @@ ====================== +v1.next +======= + +* ``ffi.gc(p, None)`` removes the destructor on an object previously + created by another call to ``ffi.gc()`` + + v1.6 ==== diff --git a/testing/cffi0/backend_tests.py b/testing/cffi0/backend_tests.py --- a/testing/cffi0/backend_tests.py +++ b/testing/cffi0/backend_tests.py @@ -1522,21 +1522,30 @@ import gc; gc.collect(); gc.collect(); gc.collect() assert seen == [3] + def test_gc_disable(self): + ffi = FFI(backend=self.Backend()) + p = ffi.new("int *", 123) + py.test.raises(TypeError, ffi.gc, p, None) + seen = [] + q1 = ffi.gc(p, lambda p: seen.append(1)) + q2 = ffi.gc(q1, lambda p: seen.append(2)) + import gc; gc.collect() + assert seen == [] + assert ffi.gc(q1, None) is None + del q1, q2 + import gc; gc.collect(); gc.collect(); gc.collect() + assert seen == [2] + def test_gc_finite_list(self): ffi = FFI(backend=self.Backend()) - public = not hasattr(ffi._backend, 'gcp') p = ffi.new("int *", 123) keepalive = [] for i in range(10): keepalive.append(ffi.gc(p, lambda p: None)) - if public: - assert len(ffi.gc_weakrefs.data) == i + 1 del keepalive[:] import gc; gc.collect(); gc.collect() for i in range(10): keepalive.append(ffi.gc(p, lambda p: None)) - if public: - assert len(ffi.gc_weakrefs.data) == 10 def test_CData_CType(self): ffi = FFI(backend=self.Backend()) _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit