Author: Armin Rigo <[email protected]>
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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit