mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-07-24 19:07:52 +02:00
Add g_object_add/remove_toggle_ref() functions to get notification when a
2005-05-05 Owen Taylor <otaylor@redhat.com> * gobject.[ch] gobject.symbols: Add g_object_add/remove_toggle_ref() functions to get notification when a reference count is the last remaining reference; this enables better memory management for language bindings. (http://mail.gnome.org/archives/gtk-devel-list/2005-April/msg00095.html) 2005-05-05 Owen Taylor <otaylor@redhat.com> * glib/gdataset.[ch] glib/gdatasetprivate.h: Add g_datalist_set/unset_flags(), g_datalist_get_flags() functions to squeeze some bits into a GDataSet... this is needed for efficient implementation of toggle references in GObject. * tests/gobject/references.c tests/gobject/Makefile.am: Add a test case for weak and toggle references. * glib/gfileutils.[ch]: Rename g_file_replace() back to g_file_set_contents(). * glib/glib.symbols: Update. 2005-05-05 Owen Taylor <otaylor@redhat.com> * glib/Makefile.am glib/glib-sections.txt gobject/gobject-sections.txt: Update * gobject/tmpl/objects.sgml: Document toggle-references.
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
#include "gobject.h"
|
||||
#include "gobjectalias.h"
|
||||
#include <glib/gdatasetprivate.h>
|
||||
|
||||
/*
|
||||
* MT safe
|
||||
@@ -39,6 +40,10 @@
|
||||
#define PARAM_SPEC_PARAM_ID(pspec) ((pspec)->param_id)
|
||||
#define PARAM_SPEC_SET_PARAM_ID(pspec, id) ((pspec)->param_id = (id))
|
||||
|
||||
#define OBJECT_HAS_TOGGLE_REF_FLAG 0x1
|
||||
#define OBJECT_HAS_TOGGLE_REF(object) \
|
||||
((G_DATALIST_GET_FLAGS(&(object)->qdata) & OBJECT_HAS_TOGGLE_REF_FLAG) != 0)
|
||||
|
||||
|
||||
/* --- signals --- */
|
||||
enum {
|
||||
@@ -105,6 +110,7 @@ static void object_interface_check_properties (gpointer func_da
|
||||
/* --- variables --- */
|
||||
static GQuark quark_closure_array = 0;
|
||||
static GQuark quark_weak_refs = 0;
|
||||
static GQuark quark_toggle_refs = 0;
|
||||
static GParamSpecPool *pspec_pool = NULL;
|
||||
static GObjectNotifyContext property_notify_context = { 0, };
|
||||
static gulong gobject_signals[LAST_SIGNAL] = { 0, };
|
||||
@@ -241,6 +247,7 @@ g_object_do_class_init (GObjectClass *class)
|
||||
quark_closure_array = g_quark_from_static_string ("GObject-closure-array");
|
||||
|
||||
quark_weak_refs = g_quark_from_static_string ("GObject-weak-references");
|
||||
quark_toggle_refs = g_quark_from_static_string ("GObject-toggle-references");
|
||||
pspec_pool = g_param_spec_pool_new (TRUE);
|
||||
property_notify_context.quark_notify_queue = g_quark_from_static_string ("GObject-notify-queue");
|
||||
property_notify_context.dispatcher = g_object_notify_dispatcher;
|
||||
@@ -1519,10 +1526,8 @@ g_object_weak_unref (GObject *object,
|
||||
found_one = TRUE;
|
||||
wstack->n_weak_refs -= 1;
|
||||
if (i != wstack->n_weak_refs)
|
||||
{
|
||||
wstack->weak_refs[i].notify = wstack->weak_refs[wstack->n_weak_refs].notify;
|
||||
wstack->weak_refs[i].data = wstack->weak_refs[wstack->n_weak_refs].data;
|
||||
}
|
||||
wstack->weak_refs[i] = wstack->weak_refs[wstack->n_weak_refs];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1554,6 +1559,106 @@ g_object_remove_weak_pointer (GObject *object,
|
||||
weak_pointer_location);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GObject *object;
|
||||
guint n_toggle_refs;
|
||||
struct {
|
||||
GToggleNotify notify;
|
||||
gpointer data;
|
||||
} toggle_refs[1]; /* flexible array */
|
||||
} ToggleRefStack;
|
||||
|
||||
static void
|
||||
toggle_refs_notify (GObject *object,
|
||||
gboolean is_last_ref)
|
||||
{
|
||||
ToggleRefStack *tstack = g_datalist_id_get_data (&object->qdata, quark_toggle_refs);
|
||||
|
||||
/* Reentrancy here is not as tricky as it seems, because a toggle reference
|
||||
* will only be notified when there is exactly one of them.
|
||||
*/
|
||||
g_assert (tstack->n_toggle_refs == 1);
|
||||
tstack->toggle_refs[0].notify (tstack->toggle_refs[0].data, tstack->object, is_last_ref);
|
||||
}
|
||||
|
||||
void
|
||||
g_object_add_toggle_ref (GObject *object,
|
||||
GToggleNotify notify,
|
||||
gpointer data)
|
||||
{
|
||||
ToggleRefStack *tstack;
|
||||
guint i;
|
||||
|
||||
g_return_if_fail (G_IS_OBJECT (object));
|
||||
g_return_if_fail (notify != NULL);
|
||||
g_return_if_fail (object->ref_count >= 1);
|
||||
|
||||
g_object_ref (object);
|
||||
|
||||
tstack = g_datalist_id_remove_no_notify (&object->qdata, quark_toggle_refs);
|
||||
if (tstack)
|
||||
{
|
||||
i = tstack->n_toggle_refs++;
|
||||
/* allocate i = tstate->n_toggle_refs - 1 positions beyond the 1 declared
|
||||
* in tstate->toggle_refs */
|
||||
tstack = g_realloc (tstack, sizeof (*tstack) + sizeof (tstack->toggle_refs[0]) * i);
|
||||
}
|
||||
else
|
||||
{
|
||||
tstack = g_renew (ToggleRefStack, NULL, 1);
|
||||
tstack->object = object;
|
||||
tstack->n_toggle_refs = 1;
|
||||
i = 0;
|
||||
}
|
||||
|
||||
/* Set a flag for fast lookup after adding the first toggle reference */
|
||||
if (tstack->n_toggle_refs == 1)
|
||||
G_DATALIST_SET_FLAGS (&object->qdata, OBJECT_HAS_TOGGLE_REF_FLAG);
|
||||
|
||||
tstack->toggle_refs[i].notify = notify;
|
||||
tstack->toggle_refs[i].data = data;
|
||||
g_datalist_id_set_data_full (&object->qdata, quark_toggle_refs, tstack,
|
||||
(GDestroyNotify)g_free);
|
||||
}
|
||||
|
||||
void
|
||||
g_object_remove_toggle_ref (GObject *object,
|
||||
GToggleNotify notify,
|
||||
gpointer data)
|
||||
{
|
||||
ToggleRefStack *tstack;
|
||||
gboolean found_one = FALSE;
|
||||
|
||||
g_return_if_fail (G_IS_OBJECT (object));
|
||||
g_return_if_fail (notify != NULL);
|
||||
|
||||
tstack = g_datalist_id_get_data (&object->qdata, quark_toggle_refs);
|
||||
if (tstack)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < tstack->n_toggle_refs; i++)
|
||||
if (tstack->toggle_refs[i].notify == notify &&
|
||||
tstack->toggle_refs[i].data == data)
|
||||
{
|
||||
found_one = TRUE;
|
||||
tstack->n_toggle_refs -= 1;
|
||||
if (i != tstack->n_toggle_refs)
|
||||
tstack->toggle_refs[i] = tstack->toggle_refs[tstack->n_toggle_refs];
|
||||
|
||||
if (tstack->n_toggle_refs == 0)
|
||||
G_DATALIST_UNSET_FLAGS (&object->qdata, OBJECT_HAS_TOGGLE_REF_FLAG);
|
||||
|
||||
g_object_unref (object);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_one)
|
||||
g_warning ("%s: couldn't find toggle ref %p(%p)", G_STRFUNC, notify, data);
|
||||
}
|
||||
|
||||
gpointer
|
||||
g_object_ref (gpointer _object)
|
||||
{
|
||||
@@ -1568,6 +1673,8 @@ g_object_ref (gpointer _object)
|
||||
#endif /* G_ENABLE_DEBUG */
|
||||
|
||||
object->ref_count += 1;
|
||||
if (object->ref_count == 2 && OBJECT_HAS_TOGGLE_REF (object))
|
||||
toggle_refs_notify (object, FALSE);
|
||||
|
||||
return object;
|
||||
}
|
||||
@@ -1586,7 +1693,11 @@ g_object_unref (gpointer _object)
|
||||
#endif /* G_ENABLE_DEBUG */
|
||||
|
||||
if (object->ref_count > 1)
|
||||
object->ref_count -= 1;
|
||||
{
|
||||
object->ref_count -= 1;
|
||||
if (object->ref_count == 1 && OBJECT_HAS_TOGGLE_REF (object))
|
||||
toggle_refs_notify (object, TRUE);
|
||||
}
|
||||
else
|
||||
g_object_last_unref (object);
|
||||
}
|
||||
|
Reference in New Issue
Block a user