[pypy-commit] cffi default: Exception to the no-keepalive rule: add one keepalive for a case
arigo
noreply at buildbot.pypy.org
Fri Jun 29 10:49:59 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r560:6b874784716c
Date: 2012-06-29 10:24 +0200
http://bitbucket.org/cffi/cffi/changeset/6b874784716c/
Log: Exception to the no-keepalive rule: add one keepalive for a case
that is occasionally hard to work around.
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -41,7 +41,8 @@
#define CT_PRIMITIVE_FITS_LONG 2048
#define CT_IS_OPAQUE 4096
#define CT_IS_ENUM 8192
-#define CT_CUSTOM_FIELD_POS 16384
+#define CT_IS_PTR_TO_OWNED 16384
+#define CT_CUSTOM_FIELD_POS 32768
#define CT_PRIMITIVE_ANY (CT_PRIMITIVE_SIGNED | \
CT_PRIMITIVE_UNSIGNED | \
CT_PRIMITIVE_CHAR | \
@@ -126,6 +127,11 @@
} CDataObject_own_length;
typedef struct {
+ CDataObject_own_base head;
+ PyObject *structobj;
+} CDataObject_own_structptr;
+
+typedef struct {
ffi_cif cif;
/* the following information is used when doing the call:
- a buffer of size 'exchange_size' is malloced
@@ -1007,7 +1013,10 @@
if (cdb->weakreflist != NULL)
PyObject_ClearWeakRefs((PyObject *) cdb);
- if (cdb->head.c_type->ct_flags & CT_FUNCTIONPTR) {
+ if (cdb->head.c_type->ct_flags & CT_IS_PTR_TO_OWNED) {
+ Py_DECREF(((CDataObject_own_structptr *)cdb)->structobj);
+ }
+ else if (cdb->head.c_type->ct_flags & CT_FUNCTIONPTR) {
/* a callback */
ffi_closure *closure = (ffi_closure *)cdb->head.c_data;
PyObject *args = (PyObject *)(closure->user_data);
@@ -1275,15 +1284,33 @@
}
static PyObject *
-cdata_subscript(CDataObject *cd, PyObject *key)
+cdataowning_subscript(CDataObject *cd, PyObject *key)
{
char *c = _cdata_get_indexed_ptr(cd, key);
- CTypeDescrObject *ctitem = cd->c_type->ct_itemdescr;
/* use 'mp_subscript' instead of 'sq_item' because we don't want
negative indexes to be corrected automatically */
if (c == NULL)
return NULL;
- return convert_to_object(c, ctitem);
+
+ if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) {
+ PyObject *res = ((CDataObject_own_structptr *)cd)->structobj;
+ Py_INCREF(res);
+ return res;
+ }
+ else {
+ return convert_to_object(c, cd->c_type->ct_itemdescr);
+ }
+}
+
+static PyObject *
+cdata_subscript(CDataObject *cd, PyObject *key)
+{
+ char *c = _cdata_get_indexed_ptr(cd, key);
+ /* use 'mp_subscript' instead of 'sq_item' because we don't want
+ negative indexes to be corrected automatically */
+ if (c == NULL)
+ return NULL;
+ return convert_to_object(c, cd->c_type->ct_itemdescr);
}
static int
@@ -1603,6 +1630,12 @@
(objobjargproc)cdata_ass_sub, /*mp_ass_subscript*/
};
+static PyMappingMethods CDataOwn_as_mapping = {
+ (lenfunc)cdata_length, /*mp_length*/
+ (binaryfunc)cdataowning_subscript, /*mp_subscript*/
+ (objobjargproc)cdata_ass_sub, /*mp_ass_subscript*/
+};
+
static PyTypeObject CData_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_cffi_backend.CData",
@@ -1645,7 +1678,7 @@
(reprfunc)cdataowning_repr, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
- 0, /* tp_as_mapping */
+ &CDataOwn_as_mapping, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
@@ -1752,6 +1785,7 @@
static PyObject *b_newp(PyObject *self, PyObject *args)
{
CTypeDescrObject *ct, *ctitem;
+ CDataObject *cd;
CDataObject_own_base *cdb;
PyObject *init = Py_None;
Py_ssize_t dataoffset, datasize, explicitlength;
@@ -1812,23 +1846,48 @@
cdb = (CDataObject_own_base *)PyObject_Malloc(dataoffset + datasize);
if (PyObject_Init((PyObject *)cdb, &CDataOwning_Type) == NULL)
return NULL;
-
- Py_INCREF(ct);
- cdb->head.c_type = ct;
+ cdb->weakreflist = NULL;
cdb->head.c_data = ((char *)cdb) + dataoffset;
- cdb->weakreflist = NULL;
if (explicitlength >= 0)
((CDataObject_own_length*)cdb)->length = explicitlength;
- memset(cdb->head.c_data, 0, datasize);
- if (init != Py_None) {
- if (convert_from_object(cdb->head.c_data,
- (ct->ct_flags & CT_POINTER) ? ct->ct_itemdescr : ct, init) < 0) {
+ if (ct->ct_flags & CT_IS_PTR_TO_OWNED) {
+ /* common case of ptr-to-struct (or ptr-to-union): for this case
+ we build two objects instead of one, with the memory-owning
+ one being really the struct (or union) and the returned one
+ having a strong reference to it */
+ CDataObject_own_structptr *cdp;
+
+ ctitem = ct->ct_itemdescr;
+ Py_INCREF(ctitem);
+ cdb->head.c_type = ctitem;
+
+ cdp = (CDataObject_own_structptr *)
+ PyObject_Malloc(sizeof(CDataObject_own_structptr));
+ if (PyObject_Init((PyObject *)cdp, &CDataOwning_Type) == NULL) {
Py_DECREF(cdb);
return NULL;
}
+ cdp->head.weakreflist = NULL;
+ cdp->head.head.c_data = cdb->head.c_data;
+ cdp->structobj = (PyObject *)cdb; /* store away the only reference */
+ cd = &cdp->head.head;
}
- return (PyObject *)cdb;
+ else {
+ cd = &cdb->head;
+ }
+ Py_INCREF(ct);
+ cd->c_type = ct;
+
+ memset(cd->c_data, 0, datasize);
+ if (init != Py_None) {
+ if (convert_from_object(cd->c_data,
+ (ct->ct_flags & CT_POINTER) ? ct->ct_itemdescr : ct, init) < 0) {
+ Py_DECREF(cd);
+ return NULL;
+ }
+ }
+ return (PyObject *)cd;
}
static CDataObject *_new_casted_primitive(CTypeDescrObject *ct)
@@ -2294,6 +2353,8 @@
td->ct_size = sizeof(void *);
td->ct_flags = CT_POINTER;
+ if (ctitem->ct_flags & (CT_STRUCT|CT_UNION))
+ td->ct_flags |= CT_IS_PTR_TO_OWNED;
return (PyObject *)td;
}
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -1164,3 +1164,33 @@
newp(BStructPtr, [cast(BCharP, 0)])
py.test.raises(TypeError, newp, BStructPtr, [cast(BIntP, 0)])
py.test.raises(TypeError, newp, BStructPtr, [cast(BFunc2, 0)])
+
+def test_keepalive_struct():
+ # exception to the no-keepalive rule: p=newp(BStructPtr) returns a
+ # pointer owning the memory, and p[0] returns a pointer to the
+ # struct that *also* owns the memory
+ BStruct = new_struct_type("foo")
+ BStructPtr = new_pointer_type(BStruct)
+ complete_struct_or_union(BStruct, [('a1', new_primitive_type("int"), -1)])
+ p = newp(BStructPtr)
+ assert repr(p) == "<cdata 'struct foo *' owning 4 bytes>"
+ q = p[0]
+ assert repr(q) == "<cdata 'struct foo' owning 4 bytes>"
+ q.a1 = 123456
+ assert p.a1 == 123456
+ del p
+ import gc; gc.collect()
+ assert q.a1 == 123456
+ assert repr(q) == "<cdata 'struct foo' owning 4 bytes>"
+ assert q.a1 == 123456
+
+def test_nokeepalive_struct():
+ BStruct = new_struct_type("foo")
+ BStructPtr = new_pointer_type(BStruct)
+ BStructPtrPtr = new_pointer_type(BStructPtr)
+ complete_struct_or_union(BStruct, [('a1', new_primitive_type("int"), -1)])
+ p = newp(BStructPtr)
+ pp = newp(BStructPtrPtr)
+ pp[0] = p
+ s = pp[0][0]
+ assert repr(s).startswith("<cdata 'struct foo' 0x")
diff --git a/doc/source/index.rst b/doc/source/index.rst
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -393,6 +393,14 @@
ownership, so you must keep it alive. As soon as you forget it, then
the casted pointer will point to garbage.)
+.. versionchanged:: 0.2
+ Allocating a new struct also returns an owning pointer object.
+ But --- as an exception to the above rule --- dereferencing this
+ particular pointer object also returns an *owning* struct object.
+ This is done for cases where you really want to have a struct
+ object but don't have any convenient place to keep the original
+ pointer object alive.
+
The cdata objects support mostly the same operations as in C: you can
read or write from pointers, arrays and structures. Dereferencing a
pointer is done usually in C with the syntax ``*p``, which is not valid
More information about the pypy-commit
mailing list