]> granicus.if.org Git - python/commitdiff
Merged revisions 62481 via svnmerge from
authorThomas Heller <theller@ctypes.org>
Thu, 24 Apr 2008 18:39:36 +0000 (18:39 +0000)
committerThomas Heller <theller@ctypes.org>
Thu, 24 Apr 2008 18:39:36 +0000 (18:39 +0000)
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r62481 | thomas.heller | 2008-04-24 20:14:19 +0200 (Do, 24 Apr 2008) | 3 lines

  Remove cyclic reference in CFuncPtr instances; see issue #2682.

  Backport candidate for the release25-maint branch.
........

Misc/NEWS
Modules/_ctypes/_ctypes.c
Modules/_ctypes/callbacks.c
Modules/_ctypes/ctypes.h

index 7a8744f421117fb4d6bc879c7159d93141e06007..0ac400dbcf77d3c2c506191c459c6c9a53e7c00b 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -32,6 +32,9 @@ Extension Modules
 Library
 -------
 
+- Issue #2682: ctypes callback functions now longer contain a cyclic
+  reference to themselves.
+
 - Issue #2058: Remove the buf attribute and add __slots__ to the TarInfo
   class in order to reduce tarfile's memory usage.
 
index 381d8018679ff60d87d466af478701940b2a8d43..9b01b80bea0c6497acf199656612d1989dfdcb40 100644 (file)
@@ -3052,7 +3052,7 @@ CFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
        CFuncPtrObject *self;
        PyObject *callable;
        StgDictObject *dict;
-       ffi_info *thunk;
+       CThunkObject *thunk;
 
        if (PyTuple_GET_SIZE(args) == 0)
                return GenericCData_new(type, args, kwds);
@@ -3108,11 +3108,6 @@ CFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
                return NULL;
        }
 
-       /*****************************************************************/
-       /* The thunk keeps unowned references to callable and dict->argtypes
-          so we have to keep them alive somewhere else: callable is kept in self,
-          dict->argtypes is in the type's stgdict.
-       */
        thunk = AllocFunctionCallback(callable,
                                      dict->argtypes,
                                      dict->restype,
@@ -3121,27 +3116,22 @@ CFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
                return NULL;
 
        self = (CFuncPtrObject *)GenericCData_new(type, args, kwds);
-       if (self == NULL)
+       if (self == NULL) {
+               Py_DECREF(thunk);
                return NULL;
+       }
 
        Py_INCREF(callable);
        self->callable = callable;
 
        self->thunk = thunk;
-       *(void **)self->b_ptr = *(void **)thunk;
-
-       /* We store ourself in self->b_objects[0], because the whole instance
-          must be kept alive if stored in a structure field, for example.
-          Cycle GC to the rescue! And we have a unittest proving that this works
-          correctly...
-       */
-
-       Py_INCREF((PyObject *)self); /* for KeepRef */
-       if (-1 == KeepRef((CDataObject *)self, 0, (PyObject *)self)) {
+       *(void **)self->b_ptr = (void *)thunk->pcl;
+       
+       Py_INCREF((PyObject *)thunk); /* for KeepRef */
+       if (-1 == KeepRef((CDataObject *)self, 0, (PyObject *)thunk)) {
                Py_DECREF((PyObject *)self);
                return NULL;
        }
-
        return (PyObject *)self;
 }
 
@@ -3590,6 +3580,7 @@ CFuncPtr_traverse(CFuncPtrObject *self, visitproc visit, void *arg)
        Py_VISIT(self->argtypes);
        Py_VISIT(self->converters);
        Py_VISIT(self->paramflags);
+       Py_VISIT(self->thunk);
        return CData_traverse((CDataObject *)self, visit, arg);
 }
 
@@ -3603,13 +3594,7 @@ CFuncPtr_clear(CFuncPtrObject *self)
        Py_CLEAR(self->argtypes);
        Py_CLEAR(self->converters);
        Py_CLEAR(self->paramflags);
-
-       if (self->thunk) {
-               FreeClosure(self->thunk->pcl);
-               PyMem_Free(self->thunk);
-               self->thunk = NULL;
-       }
-
+       Py_CLEAR(self->thunk);
        return CData_clear((CDataObject *)self);
 }
 
@@ -5011,6 +4996,9 @@ init_ctypes(void)
        if (PyType_Ready(&PyCArg_Type) < 0)
                return;
 
+       if (PyType_Ready(&CThunk_Type) < 0)
+               return;
+
        /* StgDict is derived from PyDict_Type */
        StgDict_Type.tp_base = &PyDict_Type;
        if (PyType_Ready(&StgDict_Type) < 0)
index 964af1bacc1f7c1a7e002d333c66b4eefa417979..45d190fa13f38245418428dc79fbd11d4fb7da06 100644 (file)
@@ -7,6 +7,73 @@
 #endif
 #include "ctypes.h"
 
+/**************************************************************/
+
+static CThunkObject_dealloc(PyObject *_self)
+{
+       CThunkObject *self = (CThunkObject *)_self;
+       Py_XDECREF(self->converters);
+       Py_XDECREF(self->callable);
+       Py_XDECREF(self->restype);
+       if (self->pcl)
+               FreeClosure(self->pcl);
+       PyObject_Del(self);
+}
+
+static int
+CThunkObject_traverse(PyObject *_self, visitproc visit, void *arg)
+{
+       CThunkObject *self = (CThunkObject *)_self;
+       Py_VISIT(self->converters);
+       Py_VISIT(self->callable);
+       Py_VISIT(self->restype);
+       return 0;
+}
+
+static int
+CThunkObject_clear(PyObject *_self)
+{
+       CThunkObject *self = (CThunkObject *)_self;
+       Py_CLEAR(self->converters);
+       Py_CLEAR(self->callable);
+       Py_CLEAR(self->restype);
+       return 0;
+}
+
+PyTypeObject CThunk_Type = {
+       PyVarObject_HEAD_INIT(NULL, 0)
+       "_ctypes.CThunkObject",
+       sizeof(CThunkObject),                   /* tp_basicsize */
+       sizeof(ffi_type),                       /* tp_itemsize */
+       CThunkObject_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,                     /* tp_flags */
+       "CThunkObject",                         /* tp_doc */
+       CThunkObject_traverse,                  /* tp_traverse */
+       CThunkObject_clear,                     /* tp_clear */
+       0,                                      /* tp_richcompare */
+       0,                                      /* tp_weaklistoffset */
+       0,                                      /* tp_iter */
+       0,                                      /* tp_iternext */
+       0,                                      /* tp_methods */
+       0,                                      /* tp_members */
+};
+
+/**************************************************************/
+
 static void
 PrintError(char *msg, ...)
 {
@@ -244,32 +311,56 @@ static void closure_fcn(ffi_cif *cif,
                        void **args,
                        void *userdata)
 {
-       ffi_info *p = userdata;
+       CThunkObject *p = (CThunkObject *)userdata;
 
        _CallPythonObject(resp,
-                         p->restype,
+                         p->ffi_restype,
                          p->setfunc,
                          p->callable,
                          p->converters,
                          args);
 }
 
-ffi_info *AllocFunctionCallback(PyObject *callable,
-                               PyObject *converters,
-                               PyObject *restype,
-                               int is_cdecl)
+static CThunkObject* CThunkObject_new(Py_ssize_t nArgs)
+{
+       CThunkObject *p;
+       int i;
+
+       p = PyObject_NewVar(CThunkObject, &CThunk_Type, nArgs);
+       if (p == NULL) {
+               PyErr_NoMemory();
+               return NULL;
+       }
+
+       p->pcl = NULL;
+       memset(&p->cif, 0, sizeof(p->cif));
+       p->converters = NULL;
+       p->callable = NULL;
+       p->setfunc = NULL;
+       p->ffi_restype = NULL;
+       
+       for (i = 0; i < nArgs + 1; ++i)
+               p->atypes[i] = NULL;
+       return p;
+}
+
+CThunkObject *AllocFunctionCallback(PyObject *callable,
+                                   PyObject *converters,
+                                   PyObject *restype,
+                                   int is_cdecl)
 {
        int result;
-       ffi_info *p;
+       CThunkObject *p;
        Py_ssize_t nArgs, i;
        ffi_abi cc;
 
        nArgs = PySequence_Size(converters);
-       p = (ffi_info *)PyMem_Malloc(sizeof(ffi_info) + sizeof(ffi_type) * (nArgs));
-       if (p == NULL) {
-               PyErr_NoMemory();
+       p = CThunkObject_new(nArgs);
+       if (p == NULL)
                return NULL;
-       }
+
+       assert(CThunk_CheckExact((PyObject *)p));
+
        p->pcl = MallocClosure();
        if (p->pcl == NULL) {
                PyErr_NoMemory();
@@ -285,9 +376,11 @@ ffi_info *AllocFunctionCallback(PyObject *callable,
        }
        p->atypes[i] = NULL;
 
+       Py_INCREF(restype);
+       p->restype = restype;
        if (restype == Py_None) {
                p->setfunc = NULL;
-               p->restype = &ffi_type_void;
+               p->ffi_restype = &ffi_type_void;
        } else {
                StgDictObject *dict = PyType_stgdict(restype);
                if (dict == NULL || dict->setfunc == NULL) {
@@ -296,7 +389,7 @@ ffi_info *AllocFunctionCallback(PyObject *callable,
                  goto error;
                }
                p->setfunc = dict->setfunc;
-               p->restype = &dict->ffi_type_pointer;
+               p->ffi_restype = &dict->ffi_type_pointer;
        }
 
        cc = FFI_DEFAULT_ABI;
@@ -320,16 +413,14 @@ ffi_info *AllocFunctionCallback(PyObject *callable,
                goto error;
        }
 
+       Py_INCREF(converters);
        p->converters = converters;
+       Py_INCREF(callable);
        p->callable = callable;
        return p;
 
   error:
-       if (p) {
-               if (p->pcl)
-                       FreeClosure(p->pcl);
-               PyMem_Free(p);
-       }
+       Py_XDECREF(p);
        return NULL;
 }
 
index 1390daf846991e626df5d1f8eb195de1e529849c..467ace8f2201ac8e88732779981e850ed63308cb 100644 (file)
@@ -53,14 +53,18 @@ struct tagCDataObject {
 };
 
 typedef struct {
+       PyObject_VAR_HEAD
        ffi_closure *pcl; /* the C callable */
        ffi_cif cif;
        PyObject *converters;
        PyObject *callable;
+       PyObject *restype;
        SETFUNC setfunc;
-       ffi_type *restype;
+       ffi_type *ffi_restype;
        ffi_type *atypes[1];
-} ffi_info;
+} CThunkObject;
+extern PyTypeObject CThunk_Type;
+#define CThunk_CheckExact(v)       ((v)->ob_type == &CThunk_Type)
 
 typedef struct {
        /* First part identical to tagCDataObject */
@@ -76,7 +80,7 @@ typedef struct {
        union value b_value;
        /* end of tagCDataObject, additional fields follow */
 
-       ffi_info *thunk;
+       CThunkObject *thunk;
        PyObject *callable;
 
        /* These two fields will override the ones in the type's stgdict if
@@ -147,10 +151,10 @@ extern void init_callbacks_in_module(PyObject *m);
 
 extern PyMethodDef module_methods[];
 
-extern ffi_info *AllocFunctionCallback(PyObject *callable,
-                                      PyObject *converters,
-                                      PyObject *restype,
-                                      int stdcall);
+extern CThunkObject *AllocFunctionCallback(PyObject *callable,
+                                          PyObject *converters,
+                                          PyObject *restype,
+                                          int stdcall);
 /* a table entry describing a predefined ctypes type */
 struct fielddesc {
        char code;