summaryrefslogtreecommitdiff
path: root/gi/pygi-boxed.c
diff options
context:
space:
mode:
authorSimon Feltman <sfeltman@src.gnome.org>2014-07-29 19:29:28 -0700
committerSimon Feltman <sfeltman@src.gnome.org>2014-08-17 22:31:19 -0700
commit85175047e66dfc0c0263eac91d8056a95d0a60a0 (patch)
tree3eb6ea6620a1d69f514778321e69d6224ac38347 /gi/pygi-boxed.c
parent62aed0977090f7099a5e538209f7c680ea22fe12 (diff)
downloadpygobject-85175047e66dfc0c0263eac91d8056a95d0a60a0.tar.gz
Refactor boxed wrapper memory management strategy
Change pygi_boxed_new() to accept "copy_boxed" instead of "free_on_dealloc". This changes memory management so the PyGIBoxed wrapper owns the boxed pointer given to it. Use __del__ instead of dealloc for freeing the boxed memory. This is needed for edge cases where objects like GSource can trigger the finalized callback during de-alloc, resulting in the PyObjects references counts being manipulated and triggering a re-entrant de-alloc. Add hack to keep Gtk.TreeIter.do_iter_next/previous implementations working which rely on pass-by-reference. See also: https://bugzilla.gnome.org/show_bug.cgi?id=734465 https://bugzilla.gnome.org/show_bug.cgi?id=722899 https://bugzilla.gnome.org/show_bug.cgi?id=726999
Diffstat (limited to 'gi/pygi-boxed.c')
-rw-r--r--gi/pygi-boxed.c54
1 files changed, 43 insertions, 11 deletions
diff --git a/gi/pygi-boxed.c b/gi/pygi-boxed.c
index 4fd90d2d..557584b5 100644
--- a/gi/pygi-boxed.c
+++ b/gi/pygi-boxed.c
@@ -28,18 +28,26 @@
static void
_boxed_dealloc (PyGIBoxed *self)
{
+ Py_TYPE (self)->tp_free ((PyObject *)self);
+}
+
+static PyObject *
+boxed_del (PyGIBoxed *self)
+{
GType g_type;
+ gpointer boxed = pyg_boxed_get_ptr (self);
- if ( ( (PyGBoxed *) self)->free_on_dealloc) {
+ if ( ( (PyGBoxed *) self)->free_on_dealloc && boxed != NULL) {
if (self->slice_allocated) {
- g_slice_free1 (self->size, pyg_boxed_get_ptr (self));
+ g_slice_free1 (self->size, boxed);
} else {
g_type = pyg_type_from_object ( (PyObject *) self);
- g_boxed_free (g_type, pyg_boxed_get_ptr (self));
+ g_boxed_free (g_type, boxed);
}
}
+ pyg_boxed_set_ptr (self, NULL);
- Py_TYPE (self)->tp_free ((PyObject *)self);
+ Py_RETURN_NONE;
}
void *
@@ -103,7 +111,7 @@ _boxed_new (PyTypeObject *type,
goto out;
}
- self = (PyGIBoxed *) _pygi_boxed_new (type, boxed, TRUE, size);
+ self = (PyGIBoxed *) _pygi_boxed_new (type, boxed, FALSE, size);
if (self == NULL) {
g_slice_free1 (size, boxed);
goto out;
@@ -136,30 +144,48 @@ _boxed_init (PyObject *self,
PYGLIB_DEFINE_TYPE("gi.Boxed", PyGIBoxed_Type, PyGIBoxed);
PyObject *
-_pygi_boxed_new (PyTypeObject *type,
+_pygi_boxed_new (PyTypeObject *pytype,
gpointer boxed,
- gboolean free_on_dealloc,
+ gboolean copy_boxed,
gsize allocated_slice)
{
PyGIBoxed *self;
+ GType gtype;
if (!boxed) {
Py_RETURN_NONE;
}
- if (!PyType_IsSubtype (type, &PyGIBoxed_Type)) {
+ if (!PyType_IsSubtype (pytype, &PyGIBoxed_Type)) {
PyErr_SetString (PyExc_TypeError, "must be a subtype of gi.Boxed");
return NULL;
}
- self = (PyGIBoxed *) type->tp_alloc (type, 0);
+ gtype = pyg_type_from_object ((PyObject *)pytype);
+
+ /* Boxed objects with slice allocation means they come from caller allocated
+ * out arguments. In this case copy_boxed does not make sense because we
+ * already own the slice allocated memory and we should be receiving full
+ * ownership transfer. */
+ if (copy_boxed) {
+ g_assert (allocated_slice == 0);
+ boxed = g_boxed_copy (gtype, boxed);
+ }
+
+ self = (PyGIBoxed *) pytype->tp_alloc (pytype, 0);
if (self == NULL) {
return NULL;
}
- ( (PyGBoxed *) self)->gtype = pyg_type_from_object ( (PyObject *) type);
- ( (PyGBoxed *) self)->free_on_dealloc = free_on_dealloc;
+ /* We always free on dealloc because we always own the memory due to:
+ * 1) copy_boxed == TRUE
+ * 2) allocated_slice > 0
+ * 3) otherwise the mode is assumed "transfer everything".
+ */
+ ((PyGBoxed *)self)->free_on_dealloc = TRUE;
+ ((PyGBoxed *)self)->gtype = gtype;
pyg_boxed_set_ptr (self, boxed);
+
if (allocated_slice > 0) {
self->size = allocated_slice;
self->slice_allocated = TRUE;
@@ -182,6 +208,11 @@ static PyGetSetDef pygi_boxed_getsets[] = {
{ NULL, 0, 0 }
};
+static PyMethodDef boxed_methods[] = {
+ { "__del__", (PyCFunction)boxed_del, METH_NOARGS },
+ { NULL, NULL, 0 }
+};
+
void
_pygi_boxed_register_types (PyObject *m)
{
@@ -192,6 +223,7 @@ _pygi_boxed_register_types (PyObject *m)
PyGIBoxed_Type.tp_dealloc = (destructor) _boxed_dealloc;
PyGIBoxed_Type.tp_flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE);
PyGIBoxed_Type.tp_getset = pygi_boxed_getsets;
+ PyGIBoxed_Type.tp_methods = boxed_methods;
if (PyType_Ready (&PyGIBoxed_Type))
return;