On Tue, 2005-04-26 at 01:17 +0200, Tim Janik wrote: > On Mon, 25 Apr 2005, Owen Taylor wrote: > > > Here's a patch implementing the proposed API (with the GDataset > > flags hack). A test case is included. > > > > As always, if the patch is approved, I'll move the docs to the tmpl > > files to match the rest of libgobject. > > without looking into the issue of toggle references per se, skimming > over the patch i do have two immediate comments on the datalist part: > > 1) you do: > GData *tmp = G_DATALIST_GET_POINTER (data); > modify (&tmp); // invokes user callbacks > G_DATALIST_SET_POINTER (data, tmp); > this breaks reentrancy for datalists, getters/setters/clearance > all have to work correctly in recursive call scenarios. > so you actually need to move the GET/SET-POINTER logic down into > the modify() functions of your patch. > 2) > +++ glib/gdataset.h 25 Apr 2005 22:57:13 -0000 > @@ -39,6 +39,15 @@ typedef void (*GDataForeachFu > > /* Keyed Data List > */ > +#define G_DATALIST_FLAGS_MASK 0x3 > + > +#define G_DATALIST_GET_FLAGS(datalist) \ > + ((gulong)*(datalist) & G_DATALIST_FLAGS_MASK) > +#define G_DATALIST_SET_FLAGS(datalist, flags) G_STMT_START { \ > + *datalist = (GData *)((flags) | \ > + ((gulong)*(datalist) & ~(gulong)G_DATALIST_FLAGS_MASK)); \ > +} G_STMT_END > + > not being able to predict future datalist modifications, flag uses, > notification requirements or alignment schemes, the GET_FLAGS and > SET_FLAGS macros should actually be turned into real functions on > the datalist. > i see no problem with leaving G_DATALIST_FLAGS_MASK as a macro, since > ensuring future modifications to it will only grow the number should > be easy to maintain and keeps backwards compatibility. OK, new version of the patch attached that fixes both. Well, what I did for 2) was to add public functions and a gdatasetprivate.h with the macros and use the macros from gobject.c. g_object_ref/unref very typically show up high on profiles and I'm reluctant to add another PLT function call in them when it is easily avoided. Premature optimization perhaps, but I think pretty harmless premature optimization ... the increase in complexity is not significant. Regards, Owen
? gobject/gobject.save ? tests/gobject/references Index: glib/Makefile.am =================================================================== RCS file: /cvs/gnome/glib/glib/Makefile.am,v retrieving revision 1.131 diff -u -p -u -r1.131 Makefile.am --- glib/Makefile.am 14 Mar 2005 04:26:57 -0000 1.131 +++ glib/Makefile.am 25 Apr 2005 23:42:08 -0000 @@ -70,6 +70,7 @@ libglib_2_0_la_SOURCES = \ gcompletion.c \ gconvert.c \ gdataset.c \ + gdatasetprivate.h \ gdate.c \ gdir.c \ gerror.c \ Index: glib/gdataset.c =================================================================== RCS file: /cvs/gnome/glib/glib/gdataset.c,v retrieving revision 1.22 diff -u -p -u -r1.22 gdataset.c --- glib/gdataset.c 14 Mar 2005 05:30:08 -0000 1.22 +++ glib/gdataset.c 25 Apr 2005 23:42:08 -0000 @@ -37,6 +37,7 @@ #include "glib.h" #include "galias.h" +#include "gdatasetprivate.h" /* --- defines --- */ @@ -91,7 +92,6 @@ static GHashTable *g_quark_ht = NULL; static gchar **g_quarks = NULL; static GQuark g_quark_seq_id = 0; - /* --- functions --- */ /* HOLDS: g_dataset_global_lock */ @@ -102,8 +102,8 @@ g_datalist_clear_i (GData **datalist) /* unlink *all* items before walking their destructors */ - list = *datalist; - *datalist = NULL; + list = G_DATALIST_GET_POINTER (datalist); + G_DATALIST_SET_POINTER (datalist, NULL); while (list) { @@ -139,7 +139,7 @@ g_datalist_clear (GData **datalist) if (!g_dataset_location_ht) g_data_initialize (); - while (*datalist) + while (G_DATALIST_GET_POINTER (datalist)) g_datalist_clear_i (datalist); G_UNLOCK (g_dataset_global); } @@ -210,7 +210,7 @@ g_data_set_internal (GData **datalist, { register GData *list; - list = *datalist; + list = G_DATALIST_GET_POINTER (datalist); if (!data) { register GData *prev; @@ -226,12 +226,12 @@ g_data_set_internal (GData **datalist, prev->next = list->next; else { - *datalist = list->next; + G_DATALIST_SET_POINTER (datalist, list->next); /* the dataset destruction *must* be done * prior to invokation of the data destroy function */ - if (!*datalist && dataset) + if (!list->next && dataset) g_dataset_destroy_internal (dataset); } @@ -309,11 +309,11 @@ g_data_set_internal (GData **datalist, } else list = g_chunk_new (GData, g_data_mem_chunk); - list->next = *datalist; + list->next = G_DATALIST_GET_POINTER (datalist); list->id = key_id; list->data = data; list->destroy_func = destroy_func; - *datalist = list; + G_DATALIST_SET_POINTER (datalist, list); } return NULL; @@ -459,7 +459,7 @@ g_datalist_id_get_data (GData **datalis { register GData *list; - for (list = *datalist; list; list = list->next) + for (list = G_DATALIST_GET_POINTER (datalist); list; list = list->next) if (list->id == key_id) return list->data; } @@ -509,7 +509,7 @@ g_datalist_foreach (GData **datalist, g_return_if_fail (datalist != NULL); g_return_if_fail (func != NULL); - for (list = *datalist; list; list = next) + for (list = G_DATALIST_GET_POINTER (datalist); list; list = next) { next = list->next; func (list->id, list->data, user_data); @@ -522,6 +522,51 @@ g_datalist_init (GData **datalist) g_return_if_fail (datalist != NULL); *datalist = NULL; +} + +/** + * g_datalist_set_flags: + * @datalist: pointer to the location that holds a list + * @flags: the flags to set. The values of the flags are + * restricted by %G_DATALIST_FLAGS_MASK (currently + * 3 ... so values from 0 to 3 are possible for @flags); + * a value for @flags that doesn't fit within the mask is + * an error. + * + * Stores flags values in a data list. This function is used + * to keep a small number of boolean flags in an object with + * a data list without using any additional space. It is + * not generally useful except in circumstances where space + * is very tight. (It is used in the base #GObject type, for + * example.) + * + * Return value: the flags of the datalist + **/ +void +g_datalist_set_flags (GData **datalist, + guint flags) +{ + g_return_if_fail (datalist != NULL); + g_return_if_fail ((flags & ~(guint)G_DATALIST_FLAGS_MASK) != 0); + + G_DATALIST_SET_FLAGS (datalist, flags); +} + +/** + * g_datalist_get_flags: + * @datalist: pointer to the location that holds a list + * + * Gets flags values packed in together with the datalist. + * SEe g_datalist_set_flags(). + * + * Return value: the flags of the datalist + **/ +guint +g_datalist_get_flags (GData **datalist) +{ + g_return_val_if_fail (datalist != NULL, 0); + + return G_DATALIST_GET_FLAGS (datalist); } /* HOLDS: g_dataset_global_lock */ Index: glib/gdataset.h =================================================================== RCS file: /cvs/gnome/glib/glib/gdataset.h,v retrieving revision 1.2 diff -u -p -u -r1.2 gdataset.h --- glib/gdataset.h 26 Jun 2001 16:01:14 -0000 1.2 +++ glib/gdataset.h 25 Apr 2005 23:42:08 -0000 @@ -39,19 +39,25 @@ typedef void (*GDataForeachFu /* Keyed Data List */ -void g_datalist_init (GData **datalist); -void g_datalist_clear (GData **datalist); -gpointer g_datalist_id_get_data (GData **datalist, - GQuark key_id); -void g_datalist_id_set_data_full (GData **datalist, - GQuark key_id, - gpointer data, - GDestroyNotify destroy_func); -gpointer g_datalist_id_remove_no_notify (GData **datalist, - GQuark key_id); -void g_datalist_foreach (GData **datalist, - GDataForeachFunc func, - gpointer user_data); +#define G_DATALIST_FLAGS_MASK 0x3 + +void g_datalist_init (GData **datalist); +void g_datalist_clear (GData **datalist); +gpointer g_datalist_id_get_data (GData **datalist, + GQuark key_id); +void g_datalist_id_set_data_full (GData **datalist, + GQuark key_id, + gpointer data, + GDestroyNotify destroy_func); +gpointer g_datalist_id_remove_no_notify (GData **datalist, + GQuark key_id); +void g_datalist_foreach (GData **datalist, + GDataForeachFunc func, + gpointer user_data); +void g_datalist_set_flags (GData **datalist, + guint flags); +guint g_datalist_get_flags (GData **datalist); + #define g_datalist_id_set_data(dl, q, d) \ g_datalist_id_set_data_full ((dl), (q), (d), NULL) #define g_datalist_id_remove_data(dl, q) \ Index: glib/gdatasetprivate.h =================================================================== RCS file: glib/gdatasetprivate.h diff -N glib/gdatasetprivate.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ glib/gdatasetprivate.h 25 Apr 2005 23:42:08 -0000 @@ -0,0 +1,55 @@ +/* GLIB - Library of useful routines for C programming + * gdataset-private.h: Internal macros for accessing dataset values + * Copyright (C) 2005 Red Hat + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __G_DATASETPRIVATE_H__ +#define __G_DATASETPRIVATE_H__ + +#include <glib/gdataset.h> + +G_BEGIN_DECLS + +#define G_DATALIST_GET_FLAGS(datalist) \ + ((gulong)*(datalist) & G_DATALIST_FLAGS_MASK) +#define G_DATALIST_SET_FLAGS(datalist, flags) G_STMT_START { \ + *datalist = (GData *)((flags) | \ + ((gulong)*(datalist) & ~(gulong)G_DATALIST_FLAGS_MASK)); \ +} G_STMT_END + +#define G_DATALIST_GET_POINTER(datalist) \ + ((GData *)((gulong)*(datalist) & ~(gulong)G_DATALIST_FLAGS_MASK)) +#define G_DATALIST_SET_POINTER(datalist,pointer) G_STMT_START { \ + *(datalist) = (GData *)(G_DATALIST_GET_FLAGS (datalist) | \ + (gulong)pointer); \ +} G_STMT_END + +G_END_DECLS + +#endif /* __G_DATASETPRIVATE_H__ */ + + + + Index: gobject/gobject.c =================================================================== RCS file: /cvs/gnome/glib/gobject/gobject.c,v retrieving revision 1.66 diff -u -p -u -r1.66 gobject.c --- gobject/gobject.c 14 Mar 2005 06:47:51 -0000 1.66 +++ gobject/gobject.c 25 Apr 2005 23:42:08 -0000 @@ -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_prope /* --- 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 *cl 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; @@ -1482,7 +1489,7 @@ g_object_weak_ref (GObject *object, if (wstack) { i = wstack->n_weak_refs++; - wstack = g_realloc (wstack, sizeof (*wstack) + sizeof (wstack->weak_refs[0]) * i); + wstack = g_realloc (wstack, sizeof (*wstack) + sizeof (wstack->weak_refs[0]) * (i - 1)); } else { @@ -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,133 @@ g_object_remove_weak_pointer (GObject * 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 *wstack = 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 (wstack->n_toggle_refs == 1); + wstack->toggle_refs[0].notify (wstack->toggle_refs[0].data, wstack->object, is_last_ref); +} + +/** + * g_object_add_toggle_ref: + * @object: a #GObject + * @notify: a function to call when this reference is the + * last reference to the object, or is no longer + * the last reference. + * @data: data to pass to @notify + * + * Increases the reference count of the object by one and + * sets a callback to be called when all other references + * to the object are dropped, or when this is already the + * last reference to the object and another reference is + * established. + * + * Multiple toggle references may be added to the same + * gobject, however if there are multiple toggle references + * to an object, none of them will ever be notified until + * all but one are removed. + */ +void +g_object_add_toggle_ref (GObject *object, + GToggleNotify notify, + gpointer data) +{ + ToggleRefStack *wstack; + 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); + + wstack = g_datalist_id_remove_no_notify (&object->qdata, quark_toggle_refs); + if (wstack) + { + i = wstack->n_toggle_refs++; + wstack = g_realloc (wstack, sizeof (*wstack) + sizeof (wstack->toggle_refs[0]) * (i - 1)); + } + else + { + wstack = g_renew (ToggleRefStack, NULL, 1); + wstack->object = object; + wstack->n_toggle_refs = 1; + i = 0; + } + + if (wstack->n_toggle_refs == 1) + G_DATALIST_SET_FLAGS(&object->qdata, OBJECT_HAS_TOGGLE_REF_FLAG); + + wstack->toggle_refs[i].notify = notify; + wstack->toggle_refs[i].data = data; + g_datalist_id_set_data_full (&object->qdata, quark_toggle_refs, wstack, + (GDestroyNotify)g_free); +} + +/** + * g_object_remove_toggle_ref: + * @object: a #GObject + * @notify: a function to call when this reference is the + * last reference to the object, or is no longer + * the last reference. + * @data: data to pass to @notify + * + * Removes a reference added with g_object_add_toggle_ref(). The + * reference count of the object is decreased by one. + */ +void +g_object_remove_toggle_ref (GObject *object, + GToggleNotify notify, + gpointer data) +{ + ToggleRefStack *wstack; + gboolean found_one = FALSE; + + g_return_if_fail (G_IS_OBJECT (object)); + g_return_if_fail (notify != NULL); + + wstack = g_datalist_id_get_data (&object->qdata, quark_toggle_refs); + if (wstack) + { + guint i; + + for (i = 0; i < wstack->n_toggle_refs; i++) + if (wstack->toggle_refs[i].notify == notify && + wstack->toggle_refs[i].data == data) + { + found_one = TRUE; + wstack->n_toggle_refs -= 1; + if (i != wstack->n_toggle_refs) + wstack->toggle_refs[i] = wstack->toggle_refs[wstack->n_toggle_refs]; + + if (wstack->n_toggle_refs == 0) + G_DATALIST_SET_FLAGS(&object->qdata, 0); + + 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 +1700,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 +1720,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); } Index: gobject/gobject.h =================================================================== RCS file: /cvs/gnome/glib/gobject/gobject.h,v retrieving revision 1.30 diff -u -p -u -r1.30 gobject.h --- gobject/gobject.h 8 Mar 2005 05:41:42 -0000 1.30 +++ gobject/gobject.h 25 Apr 2005 23:42:08 -0000 @@ -178,6 +178,31 @@ void g_object_add_weak_pointer gpointer *weak_pointer_location); void g_object_remove_weak_pointer (GObject *object, gpointer *weak_pointer_location); + +/** + * GToggleNotify: + * @data: Callback data passed to g_object_add_toggle_ref() + * @object: The object on which g_object_add_toggle_ref() was + * called. + * @is_last_ref: %TRUE if the toggle reference is now the + * last reference to the object. %FALSE if the toggle + * reference was the last reference and there are now other + * references. + * + * A callback function used for notification when the state + * of a toggle reference changes. See g_object_add_toggle_ref(). + */ +typedef void (*GToggleNotify) (gpointer data, + GObject *object, + gboolean is_last_ref); + +void g_object_add_toggle_ref (GObject *object, + GToggleNotify notify, + gpointer data); +void g_object_remove_toggle_ref (GObject *object, + GToggleNotify notify, + gpointer data); + gpointer g_object_get_qdata (GObject *object, GQuark quark); void g_object_set_qdata (GObject *object, Index: tests/gobject/Makefile.am =================================================================== RCS file: /cvs/gnome/glib/tests/gobject/Makefile.am,v retrieving revision 1.7 diff -u -p -u -r1.7 Makefile.am --- tests/gobject/Makefile.am 24 Oct 2003 03:41:22 -0000 1.7 +++ tests/gobject/Makefile.am 25 Apr 2005 23:42:09 -0000 @@ -52,7 +52,8 @@ test_programs = \ ifaceinit \ ifaceinherit \ ifaceproperties \ - override + override \ + references check_PROGRAMS = $(test_programs) Index: tests/gobject/references.c =================================================================== RCS file: tests/gobject/references.c diff -N tests/gobject/references.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tests/gobject/references.c 25 Apr 2005 23:42:09 -0000 @@ -0,0 +1,281 @@ +/* GObject - GLib Type, Object, Parameter and Signal Library + * Copyright (C) 2005 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "TestReferences" + +#undef G_DISABLE_ASSERT +#undef G_DISABLE_CHECKS +#undef G_DISABLE_CAST_CHECKS + +#include <glib-object.h> + +/* This test tests weak and toggle references + */ + +static GObject *global_object; + +static gboolean object_destroyed; +static gboolean weak_ref1_notified; +static gboolean weak_ref2_notified; +static gboolean toggle_ref1_weakened; +static gboolean toggle_ref1_strengthened; +static gboolean toggle_ref2_weakened; +static gboolean toggle_ref2_strengthened; +static gboolean toggle_ref3_weakened; +static gboolean toggle_ref3_strengthened; + +/* + * TestObject, a parent class for TestObject + */ +#define TEST_TYPE_OBJECT (test_object_get_type ()) +typedef struct _TestObject TestObject; +typedef struct _TestObjectClass TestObjectClass; + +struct _TestObject +{ + GObject parent_instance; +}; +struct _TestObjectClass +{ + GObjectClass parent_class; +}; + +G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT); + +static void +test_object_finalize (GObject *object) +{ + object_destroyed = TRUE; + + G_OBJECT_CLASS (test_object_parent_class)->finalize (object); +} + +static void +test_object_class_init (TestObjectClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->finalize = test_object_finalize; +} + +static void +test_object_init (TestObject *test_object) +{ +} + +static void +clear_flags (void) +{ + object_destroyed = FALSE; + weak_ref1_notified = FALSE; + weak_ref2_notified = FALSE; + toggle_ref1_weakened = FALSE; + toggle_ref1_strengthened = FALSE; + toggle_ref2_weakened = FALSE; + toggle_ref2_strengthened = FALSE; + toggle_ref3_weakened = FALSE; + toggle_ref3_strengthened = FALSE; +} + +static void +weak_ref1 (gpointer data, + GObject *object) +{ + g_assert (object == global_object); + g_assert (data == GUINT_TO_POINTER (42)); + + weak_ref1_notified = TRUE; +} + +static void +weak_ref2 (gpointer data, + GObject *object) +{ + g_assert (object == global_object); + g_assert (data == GUINT_TO_POINTER (24)); + + weak_ref2_notified = TRUE; +} + +static void +toggle_ref1 (gpointer data, + GObject *object, + gboolean is_last_ref) +{ + g_assert (object == global_object); + g_assert (data == GUINT_TO_POINTER (42)); + + if (is_last_ref) + toggle_ref1_weakened = TRUE; + else + toggle_ref1_strengthened = TRUE; +} + +static void +toggle_ref2 (gpointer data, + GObject *object, + gboolean is_last_ref) +{ + g_assert (object == global_object); + g_assert (data == GUINT_TO_POINTER (24)); + + if (is_last_ref) + toggle_ref2_weakened = TRUE; + else + toggle_ref2_strengthened = TRUE; +} + +static void +toggle_ref3 (gpointer data, + GObject *object, + gboolean is_last_ref) +{ + g_assert (object == global_object); + g_assert (data == GUINT_TO_POINTER (34)); + + if (is_last_ref) + { + toggle_ref3_weakened = TRUE; + g_object_remove_toggle_ref (object, toggle_ref3, GUINT_TO_POINTER (34)); + } + else + toggle_ref3_strengthened = TRUE; +} + +int +main (int argc, + char *argv[]) +{ + GObject *object; + + g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) | + G_LOG_LEVEL_WARNING | + G_LOG_LEVEL_CRITICAL); + g_type_init (); + + /* Test basic weak reference operation + */ + global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL); + + g_object_weak_ref (object, weak_ref1, GUINT_TO_POINTER (42)); + + clear_flags (); + g_object_unref (object); + g_assert (weak_ref1_notified == TRUE); + g_assert (object_destroyed == TRUE); + + /* Test two weak references at once + */ + global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL); + + g_object_weak_ref (object, weak_ref1, GUINT_TO_POINTER (42)); + g_object_weak_ref (object, weak_ref2, GUINT_TO_POINTER (24)); + + clear_flags (); + g_object_unref (object); + g_assert (weak_ref1_notified == TRUE); + g_assert (weak_ref2_notified == TRUE); + g_assert (object_destroyed == TRUE); + + /* Test remove weak references + */ + global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL); + + g_object_weak_ref (object, weak_ref1, GUINT_TO_POINTER (42)); + g_object_weak_ref (object, weak_ref2, GUINT_TO_POINTER (24)); + g_object_weak_unref (object, weak_ref1, GUINT_TO_POINTER (42)); + + clear_flags (); + g_object_unref (object); + g_assert (weak_ref1_notified == FALSE); + g_assert (weak_ref2_notified == TRUE); + g_assert (object_destroyed == TRUE); + + /* Test basic toggle reference operation + */ + global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL); + + g_object_add_toggle_ref (object, toggle_ref1, GUINT_TO_POINTER (42)); + + clear_flags (); + g_object_unref (object); + g_assert (toggle_ref1_weakened == TRUE); + g_assert (toggle_ref1_strengthened == FALSE); + g_assert (object_destroyed == FALSE); + + clear_flags (); + g_object_ref (object); + g_assert (toggle_ref1_weakened == FALSE); + g_assert (toggle_ref1_strengthened == TRUE); + g_assert (object_destroyed == FALSE); + + g_object_unref (object); + + clear_flags (); + g_object_remove_toggle_ref (object, toggle_ref1, GUINT_TO_POINTER (42)); + g_assert (toggle_ref1_weakened == FALSE); + g_assert (toggle_ref1_strengthened == FALSE); + g_assert (object_destroyed == TRUE); + + global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL); + + /* Test two toggle references at once + */ + g_object_add_toggle_ref (object, toggle_ref1, GUINT_TO_POINTER (42)); + g_object_add_toggle_ref (object, toggle_ref2, GUINT_TO_POINTER (24)); + + clear_flags (); + g_object_unref (object); + g_assert (toggle_ref1_weakened == FALSE); + g_assert (toggle_ref1_strengthened == FALSE); + g_assert (toggle_ref2_weakened == FALSE); + g_assert (toggle_ref2_strengthened == FALSE); + g_assert (object_destroyed == FALSE); + + clear_flags (); + g_object_remove_toggle_ref (object, toggle_ref1, GUINT_TO_POINTER (42)); + g_assert (toggle_ref1_weakened == FALSE); + g_assert (toggle_ref1_strengthened == FALSE); + g_assert (toggle_ref2_weakened == TRUE); + g_assert (toggle_ref2_strengthened == FALSE); + g_assert (object_destroyed == FALSE); + + clear_flags (); + g_object_remove_toggle_ref (object, toggle_ref2, GUINT_TO_POINTER (24)); + g_assert (toggle_ref1_weakened == FALSE); + g_assert (toggle_ref1_strengthened == FALSE); + g_assert (toggle_ref2_weakened == FALSE); + g_assert (toggle_ref2_strengthened == FALSE); + g_assert (object_destroyed == TRUE); + + /* Test a toggle reference that removes itself + */ + global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL); + + g_object_add_toggle_ref (object, toggle_ref3, GUINT_TO_POINTER (34)); + + clear_flags (); + g_object_unref (object); + g_assert (toggle_ref3_weakened == TRUE); + g_assert (toggle_ref3_strengthened == FALSE); + g_assert (object_destroyed == TRUE); + + return 0; +}
Attachment:
signature.asc
Description: This is a digitally signed message part