[pypy-commit] cffi default: Make the type objects mortal

arigo noreply at buildbot.pypy.org
Wed Sep 30 17:20:40 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r2296:9ad33a44f60f
Date: 2015-09-30 17:17 +0200
http://bitbucket.org/cffi/cffi/changeset/9ad33a44f60f/

Log:	Make the type objects mortal

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -155,6 +155,9 @@
 
     PyObject *ct_weakreflist;    /* weakref support */
 
+    PyObject *ct_unique_key;    /* key in unique_cache (a string, but not
+                                   human-readable) */
+
     Py_ssize_t ct_size;     /* size of instances, or -1 if unknown */
     Py_ssize_t ct_length;   /* length of arrays, or -1 if unknown;
                                or alignment of primitive and struct types;
@@ -286,6 +289,7 @@
 typedef PyObject *const cffi_allocator_t[3];
 static cffi_allocator_t default_allocator = { NULL, NULL, NULL };
 static PyObject *FFIError;
+static PyObject *unique_cache;
 
 /************************************************************/
 
@@ -301,6 +305,7 @@
     ct->ct_itemdescr = NULL;
     ct->ct_stuff = NULL;
     ct->ct_weakreflist = NULL;
+    ct->ct_unique_key = NULL;
     PyObject_GC_Track(ct);
     return ct;
 }
@@ -343,6 +348,15 @@
     PyObject_GC_UnTrack(ct);
     if (ct->ct_weakreflist != NULL)
         PyObject_ClearWeakRefs((PyObject *) ct);
+
+    if (ct->ct_unique_key != NULL) {
+        /* revive dead object temporarily for DelItem */
+        Py_REFCNT(ct) = 43;
+        PyDict_DelItem(unique_cache, ct->ct_unique_key);
+        assert(Py_REFCNT(ct) == 42);
+        Py_REFCNT(ct) = 0;
+        Py_DECREF(ct->ct_unique_key);
+    }
     Py_XDECREF(ct->ct_itemdescr);
     Py_XDECREF(ct->ct_stuff);
     if (ct->ct_flags & CT_FUNCTIONPTR)
@@ -3612,8 +3626,6 @@
 
 /************************************************************/
 
-static PyObject *unique_cache;
-
 static PyObject *get_unique_type(CTypeDescrObject *x,
                                  const void *unique_key[], long keylength)
 {
@@ -3633,7 +3645,6 @@
     */
     PyObject *key, *y;
     const void **pkey;
-    int err;
 
     key = PyBytes_FromStringAndSize(NULL, keylength * sizeof(void *));
     if (key == NULL)
@@ -3649,11 +3660,14 @@
         Py_DECREF(x);
         return y;
     }
-    err = PyDict_SetItem(unique_cache, key, (PyObject *)x);
-    Py_DECREF(key);
-    if (err < 0)
+    if (PyDict_SetItem(unique_cache, key, (PyObject *)x) < 0) {
+        Py_DECREF(key);
         goto error;
-
+    }
+
+    assert(x->ct_unique_key == NULL);
+    x->ct_unique_key = key; /* the key will be freed in ctypedescr_dealloc() */
+    Py_DECREF(x);          /* the 'value' in unique_cache doesn't count as 1 */
     return (PyObject *)x;
 
  error:
diff --git a/testing/cffi1/test_ffi_obj.py b/testing/cffi1/test_ffi_obj.py
--- a/testing/cffi1/test_ffi_obj.py
+++ b/testing/cffi1/test_ffi_obj.py
@@ -29,6 +29,32 @@
     assert ffi.typeof("int[][10]") is ffi.typeof("int[][10]")
     assert ffi.typeof("int(*)()") is ffi.typeof("int(*)()")
 
+def test_ffi_type_not_immortal():
+    import weakref, gc
+    ffi = _cffi1_backend.FFI()
+    t1 = ffi.typeof("int **")
+    t2 = ffi.typeof("int *")
+    w1 = weakref.ref(t1)
+    w2 = weakref.ref(t2)
+    del t1, ffi
+    gc.collect()
+    assert w1() is None
+    assert w2() is t2
+    ffi = _cffi1_backend.FFI()
+    assert ffi.typeof(ffi.new("int **")[0]) is t2
+    #
+    ffi = _cffi1_backend.FFI()
+    t1 = ffi.typeof("int ***")
+    t2 = ffi.typeof("int **")
+    w1 = weakref.ref(t1)
+    w2 = weakref.ref(t2)
+    del t2, ffi
+    gc.collect()
+    assert w1() is t1
+    assert w2() is not None   # kept alive by t1
+    ffi = _cffi1_backend.FFI()
+    assert ffi.typeof("int * *") is t1.item
+
 def test_ffi_cache_type_globally():
     ffi1 = _cffi1_backend.FFI()
     ffi2 = _cffi1_backend.FFI()


More information about the pypy-commit mailing list