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