Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r2212:68008f1b02e3
Date: 2015-07-06 13:20 +0200
http://bitbucket.org/cffi/cffi/changeset/68008f1b02e3/

Log:    Found a simpler and more efficient way to implement any ffi.gc().

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -188,11 +188,13 @@
 static PyTypeObject CData_Type;
 static PyTypeObject CDataOwning_Type;
 static PyTypeObject CDataOwningGC_Type;
+static PyTypeObject CDataGCP_Type;
 
 #define CTypeDescr_Check(ob)  (Py_TYPE(ob) == &CTypeDescr_Type)
 #define CData_Check(ob)       (Py_TYPE(ob) == &CData_Type ||            \
                                Py_TYPE(ob) == &CDataOwning_Type ||      \
-                               Py_TYPE(ob) == &CDataOwningGC_Type)
+                               Py_TYPE(ob) == &CDataOwningGC_Type ||    \
+                               Py_TYPE(ob) == &CDataGCP_Type)
 #define CDataOwn_Check(ob)    (Py_TYPE(ob) == &CDataOwning_Type ||      \
                                Py_TYPE(ob) == &CDataOwningGC_Type)
 
@@ -235,6 +237,11 @@
 } CDataObject_owngc_frombuf;
 
 typedef struct {
+    CDataObject head;
+    PyObject *origobj, *destructor;
+} CDataObject_gcp;
+
+typedef struct {
     ffi_cif cif;
     /* the following information is used when doing the call:
        - a buffer of size 'exchange_size' is malloced
@@ -1625,6 +1632,35 @@
     return 0;
 }
 
+/* forward */
+static void _my_PyErr_WriteUnraisable(char *objdescr, PyObject *obj,
+                                      char *extra_error_line);
+
+static void cdatagcp_dealloc(CDataObject_gcp *cd)
+{
+    PyObject *result;
+    PyObject *destructor = cd->destructor;
+    PyObject *origobj = cd->origobj;
+    cdata_dealloc((CDataObject *)cd);
+
+    result = PyObject_CallFunctionObjArgs(destructor, origobj, NULL);
+    if (result != NULL) {
+        Py_DECREF(result);
+    }
+    else {
+        _my_PyErr_WriteUnraisable("From callback for ffi.gc ", origobj, NULL);
+    }
+    Py_DECREF(destructor);
+    Py_DECREF(origobj);
+}
+
+static int cdatagcp_traverse(CDataObject_gcp *cd, visitproc visit, void *arg)
+{
+    Py_VISIT(cd->destructor);
+    Py_VISIT(cd->origobj);
+    return 0;
+}
+
 static PyObject *cdata_float(CDataObject *cd);  /*forward*/
 
 static PyObject *convert_cdata_to_enum_string(CDataObject *cd, int both)
@@ -2729,6 +2765,41 @@
     &CDataOwning_Type,                          /* tp_base */
 };
 
+static PyTypeObject CDataGCP_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "_cffi_backend.CDataGCP",
+    sizeof(CDataObject_gcp),
+    0,
+    (destructor)cdatagcp_dealloc,               /* tp_dealloc */
+    0,                                          /* tp_print */
+    0,                                          /* tp_getattr */
+    0,                                          /* tp_setattr */
+    0,                                          /* tp_compare */
+    0,                                          /* tp_repr */
+    0,                                          /* tp_as_number */
+    0,                                          /* tp_as_sequence */
+    0,                                          /* tp_as_mapping */
+    0,                                          /* tp_hash */
+    0,                                          /* tp_call */
+    0,                                          /* tp_str */
+    0,                                          /* tp_getattro */
+    0,                                          /* tp_setattro */
+    0,                                          /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES  /* tp_flags */
+                       | Py_TPFLAGS_HAVE_GC,
+    0,                                          /* tp_doc */
+    (traverseproc)cdatagcp_traverse,            /* tp_traverse */
+    0,                                          /* tp_clear */
+    0,                                          /* tp_richcompare */
+    0,                                          /* tp_weaklistoffset */
+    0,                                          /* tp_iter */
+    0,                                          /* tp_iternext */
+    0,                                          /* tp_methods */
+    0,                                          /* tp_members */
+    0,                                          /* tp_getset */
+    &CData_Type,                                /* tp_base */
+};
+
 /************************************************************/
 
 typedef struct {
@@ -4709,7 +4780,8 @@
     return convert_from_object(result, ctype, pyobj);
 }
 
-static void _my_PyErr_WriteUnraisable(PyObject *obj, char *extra_error_line)
+static void _my_PyErr_WriteUnraisable(char *objdescr, PyObject *obj,
+                                      char *extra_error_line)
 {
     /* like PyErr_WriteUnraisable(), but write a full traceback */
     PyObject *f, *t, *v, *tb;
@@ -4726,7 +4798,7 @@
     f = PySys_GetObject("stderr");
     if (f != NULL) {
         if (obj != NULL) {
-            PyFile_WriteString("From cffi callback ", f);
+            PyFile_WriteString(objdescr, f);
             PyFile_WriteObject(obj, f, 0);
             PyFile_WriteString(":\n", f);
         }
@@ -4799,7 +4871,8 @@
     }
     onerror_cb = PyTuple_GET_ITEM(cb_args, 3);
     if (onerror_cb == Py_None) {
-        _my_PyErr_WriteUnraisable(py_ob, extra_error_line);
+        _my_PyErr_WriteUnraisable("From cffi callback ", py_ob,
+                                  extra_error_line);
     }
     else {
         PyObject *exc1, *val1, *tb1, *res1, *exc2, *val2, *tb2;
@@ -4824,11 +4897,12 @@
             /* double exception! print a double-traceback... */
             PyErr_Fetch(&exc2, &val2, &tb2);
             PyErr_Restore(exc1, val1, tb1);
-            _my_PyErr_WriteUnraisable(py_ob, extra_error_line);
+            _my_PyErr_WriteUnraisable("From cffi callback ", py_ob,
+                                      extra_error_line);
             PyErr_Restore(exc2, val2, tb2);
             extra_error_line = ("\nDuring the call to 'onerror', "
                                 "another exception occurred:\n\n");
-            _my_PyErr_WriteUnraisable(NULL, extra_error_line);
+            _my_PyErr_WriteUnraisable(NULL, NULL, extra_error_line);
         }
     }
     goto done;
@@ -5554,6 +5628,34 @@
                            (PyObject *)&CTypeDescr_Type);
 }
 
+static PyObject *b_gcp(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    CDataObject_gcp *cd;
+    CDataObject *origobj;
+    PyObject *destructor;
+    static char *keywords[] = {"cdata", "destructor", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O:gc", keywords,
+                                     &CData_Type, &origobj, &destructor))
+        return NULL;
+
+    cd = PyObject_GC_New(CDataObject_gcp, &CDataGCP_Type);
+    if (cd == NULL)
+        return NULL;
+
+    Py_INCREF(destructor);
+    Py_INCREF(origobj);
+    Py_INCREF(origobj->c_type);
+    cd->head.c_data = origobj->c_data;
+    cd->head.c_type = origobj->c_type;
+    cd->head.c_weakreflist = NULL;
+    cd->origobj = (PyObject *)origobj;
+    cd->destructor = destructor;
+
+    PyObject_GC_Track(cd);
+    return (PyObject *)cd;
+}
+
 /************************************************************/
 
 static char _testfunc0(char a, char b)
@@ -5859,6 +5961,7 @@
     {"newp_handle", b_newp_handle, METH_VARARGS},
     {"from_handle", b_from_handle, METH_O},
     {"from_buffer", b_from_buffer, METH_VARARGS},
+    {"gcp", (PyCFunction)b_gcp, METH_VARARGS | METH_KEYWORDS},
 #ifdef MS_WIN32
     {"getwinerror", (PyCFunction)b_getwinerror, METH_VARARGS | METH_KEYWORDS},
 #endif
@@ -6096,6 +6199,8 @@
         INITERROR;
     if (PyType_Ready(&CDataOwningGC_Type) < 0)
         INITERROR;
+    if (PyType_Ready(&CDataGCP_Type) < 0)
+        INITERROR;
     if (PyType_Ready(&CDataIter_Type) < 0)
         INITERROR;
     if (PyType_Ready(&MiniBuffer_Type) < 0)
diff --git a/c/cffi1_module.c b/c/cffi1_module.c
--- a/c/cffi1_module.c
+++ b/c/cffi1_module.c
@@ -13,7 +13,6 @@
 
 #include "ffi_obj.c"
 #include "cglob.c"
-#include "cgc.c"
 #include "lib_obj.c"
 #include "cdlopen.c"
 
diff --git a/c/cgc.c b/c/cgc.c
deleted file mode 100644
--- a/c/cgc.c
+++ /dev/null
@@ -1,122 +0,0 @@
-
-/* translated to C from cffi/gc_weakref.py */
-
-
-static PyObject *gc_wref_remove(PyObject *ffi_wref_tup, PyObject *key)
-{
-    FFIObject *ffi;
-    PyObject *indexobj, *destructor, *cdata, *freelist, *result;
-    Py_ssize_t index;
-
-    /* here, tup is a 4-tuple (ffi, destructor, cdata, index) */
-    if (!PyTuple_Check(ffi_wref_tup))
-        goto oops;    /* should never occur */
-
-    ffi = (FFIObject *)PyTuple_GET_ITEM(ffi_wref_tup, 0);
-    destructor = PyTuple_GET_ITEM(ffi_wref_tup, 1);
-    cdata = PyTuple_GET_ITEM(ffi_wref_tup, 2);
-    indexobj = PyTuple_GET_ITEM(ffi_wref_tup, 3);
-
-    index = PyInt_AsSsize_t(indexobj);
-    if (index < 0)
-        goto oops;    /* should never occur */
-
-    /* assert gc_wrefs[index] is key */
-    if (PyList_GET_ITEM(ffi->gc_wrefs, index) != key)
-        goto oops;    /* should never occur */
-
-    /* gc_wrefs[index] = freelist */
-    /* transfer ownership of 'freelist' to 'gc_wrefs[index]' */
-    freelist = ffi->gc_wrefs_freelist;
-    PyList_SET_ITEM(ffi->gc_wrefs, index, freelist);
-
-    /* freelist = index */
-    ffi->gc_wrefs_freelist = indexobj;
-    Py_INCREF(indexobj);
-
-    /* destructor(cdata) */
-    result = PyObject_CallFunctionObjArgs(destructor, cdata, NULL);
-
-    Py_DECREF(key);    /* free the reference that was in 'gc_wrefs[index]' */
-    return result;
-
- oops:
-    PyErr_SetString(PyExc_SystemError, "cgc: internal inconsistency");
-    /* random leaks may follow */
-    return NULL;
-}
-
-static PyMethodDef remove_callback = {
-    "gc_wref_remove", (PyCFunction)gc_wref_remove, METH_O
-};
-
-static PyObject *gc_weakrefs_build(FFIObject *ffi, CDataObject *cdata,
-                                   PyObject *destructor)
-{
-    PyObject *new_cdata, *ref = NULL, *tup = NULL, *remove_fn = NULL;
-    Py_ssize_t index;
-    PyObject *datalist;
-
-    if (ffi->gc_wrefs == NULL) {
-        /* initialize */
-        datalist = PyList_New(0);
-        if (datalist == NULL)
-            return NULL;
-        ffi->gc_wrefs = datalist;
-        assert(ffi->gc_wrefs_freelist == NULL);
-        ffi->gc_wrefs_freelist = Py_None;
-        Py_INCREF(Py_None);
-    }
-
-    /* new_cdata = self.ffi.cast(typeof(cdata), cdata) */
-    new_cdata = do_cast(cdata->c_type, (PyObject *)cdata);
-    if (new_cdata == NULL)
-        goto error;
-
-    /* if freelist is None: */
-    datalist = ffi->gc_wrefs;
-    if (ffi->gc_wrefs_freelist == Py_None) {
-        /* index = len(gc_wrefs) */
-        index = PyList_GET_SIZE(datalist);
-        /* gc_wrefs.append(None) */
-        if (PyList_Append(datalist, Py_None) < 0)
-            goto error;
-        tup = Py_BuildValue("OOOn", ffi, destructor, cdata, index);
-    }
-    else {
-        /* index = freelist */
-        index = PyInt_AsSsize_t(ffi->gc_wrefs_freelist);
-        if (index < 0)
-            goto error;   /* should not occur */
-        tup = PyTuple_Pack(4, ffi, destructor, cdata, ffi->gc_wrefs_freelist);
-    }
-    if (tup == NULL)
-        goto error;
-
-    remove_fn = PyCFunction_New(&remove_callback, tup);
-    if (remove_fn == NULL)
-        goto error;
-
-    ref = PyWeakref_NewRef(new_cdata, remove_fn);
-    if (ref == NULL)
-        goto error;
-
-    /* freelist = gc_wrefs[index] (which is None if we just did append(None)) 
*/
-    /* transfer ownership of 'datalist[index]' into gc_wrefs_freelist */
-    Py_DECREF(ffi->gc_wrefs_freelist);
-    ffi->gc_wrefs_freelist = PyList_GET_ITEM(datalist, index);
-    /* gc_wrefs[index] = ref */
-    /* transfer ownership of 'ref' into 'datalist[index]' */
-    PyList_SET_ITEM(datalist, index, ref);
-    Py_DECREF(remove_fn);
-    Py_DECREF(tup);
-
-    return new_cdata;
-
- error:
-    Py_XDECREF(new_cdata);
-    Py_XDECREF(ref);
-    Py_XDECREF(tup);
-    Py_XDECREF(remove_fn);
-    return NULL;
-}
diff --git a/c/ffi_obj.c b/c/ffi_obj.c
--- a/c/ffi_obj.c
+++ b/c/ffi_obj.c
@@ -664,21 +664,8 @@
 "Later, when this new cdata object is garbage-collected,\n"
 "'destructor(old_cdata_object)' will be called.");
 
-static PyObject *gc_weakrefs_build(FFIObject *ffi, CDataObject *cd,
-                                   PyObject *destructor);   /* forward */
-
-static PyObject *ffi_gc(FFIObject *self, PyObject *args, PyObject *kwds)
-{
-    CDataObject *cd;
-    PyObject *destructor;
-    static char *keywords[] = {"cdata", "destructor", NULL};
-
-    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O:gc", keywords,
-                                     &CData_Type, &cd, &destructor))
-        return NULL;
-
-    return gc_weakrefs_build(self, cd, destructor);
-}
+#define ffi_gc  b_gcp     /* ffi_gc() => b_gcp()
+                             from _cffi_backend.c */
 
 PyDoc_STRVAR(ffi_callback_doc,
 "Return a callback object or a decorator making such a callback object.\n"
diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -72,7 +72,6 @@
         self._cdefsources = []
         self._included_ffis = []
         self._windows_unicode = None
-        self._gcp = None
         if hasattr(backend, 'set_ffi'):
             backend.set_ffi(self)
         for name in backend.__dict__:
@@ -329,14 +328,13 @@
         data.  Later, when this new cdata object is garbage-collected,
         'destructor(old_cdata_object)' will be called.
         """
-        if self._gcp is not None:
-            return self._gcp(cdata, destructor)
-        if hasattr(self._backend, 'FFI'):
-            compiled_ffi = self._backend.FFI()
-            self._gcp = compiled_ffi.gc
-            return self._gcp(cdata, destructor)
+        try:
+            gcp = self._backend.gcp
+        except AttributeError:
+            pass
+        else:
+            return gcp(cdata, destructor)
         #
-        # the rest is for the ctypes backend only
         with self._lock:
             try:
                 gc_weakrefs = self.gc_weakrefs
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to