[gnome-builder] egg-state-machine: Weak ref the source objects
- From: Garrett Regier <gregier src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] egg-state-machine: Weak ref the source objects
- Date: Thu, 21 May 2015 06:58:40 +0000 (UTC)
commit 10f0a1ff6773ff6741100782fd0934172af9f2d3
Author: Garrett Regier <garrettregier gmail com>
Date: Wed May 20 20:51:59 2015 -0700
egg-state-machine: Weak ref the source objects
contrib/egg/egg-state-machine-private.h | 12 ++-
contrib/egg/egg-state-machine.c | 167 +++++++++++++++++++++++++++++++
tests/test-egg-state-machine.c | 90 +++++++++++++++++
3 files changed, 264 insertions(+), 5 deletions(-)
---
diff --git a/contrib/egg/egg-state-machine-private.h b/contrib/egg/egg-state-machine-private.h
index dec731b..72239cd 100644
--- a/contrib/egg/egg-state-machine-private.h
+++ b/contrib/egg/egg-state-machine-private.h
@@ -43,15 +43,17 @@ typedef struct
typedef struct
{
- gpointer object;
- gchar *property;
- GValue value;
+ EggStateMachine *state_machine;
+ gpointer object;
+ gchar *property;
+ GValue value;
} EggStateProperty;
typedef struct
{
- GtkWidget *widget;
- gchar *name;
+ EggStateMachine *state_machine;
+ GtkWidget *widget;
+ gchar *name;
} EggStateStyle;
G_END_DECLS
diff --git a/contrib/egg/egg-state-machine.c b/contrib/egg/egg-state-machine.c
index 0002f39..f545a27 100644
--- a/contrib/egg/egg-state-machine.c
+++ b/contrib/egg/egg-state-machine.c
@@ -45,6 +45,114 @@ enum {
static GParamSpec *gParamSpecs [LAST_PROP];
static void
+egg_state_machine__property_object_weak_notify (gpointer data,
+ GObject *where_object_was)
+{
+ EggStateProperty *state_prop = data;
+ EggStateMachine *self = state_prop->state_machine;
+ EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
+ GHashTableIter iter;
+ EggState *state;
+
+ g_assert (EGG_IS_STATE_MACHINE (self));
+ g_assert (where_object_was != NULL);
+
+ state_prop->object = NULL;
+
+ g_hash_table_iter_init (&iter, priv->states);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer)&state))
+ {
+ if (g_ptr_array_remove_fast (state->properties, state_prop))
+ return;
+ }
+
+ g_critical ("Failed to find property for %p", where_object_was);
+}
+
+static void
+egg_state_machine__style_object_weak_notify (gpointer data,
+ GObject *where_object_was)
+{
+ EggStateStyle *style_prop = data;
+ EggStateMachine *self = style_prop->state_machine;
+ EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
+ GHashTableIter iter;
+ EggState *state;
+
+ g_assert (EGG_IS_STATE_MACHINE (self));
+ g_assert (where_object_was != NULL);
+
+ style_prop->widget = NULL;
+
+ g_hash_table_iter_init (&iter, priv->states);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer)&state))
+ {
+ if (g_ptr_array_remove_fast (state->styles, style_prop))
+ return;
+ }
+
+ g_critical ("Failed to find style for %p", where_object_was);
+}
+
+static void
+egg_state_machine__binding_source_weak_notify (gpointer data,
+ GObject *where_object_was)
+{
+ EggStateMachine *self = data;
+ EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
+ GHashTableIter iter;
+ EggState *state;
+
+ g_assert (EGG_IS_STATE_MACHINE (self));
+ g_assert (where_object_was != NULL);
+
+ g_hash_table_iter_init (&iter, priv->states);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer)&state))
+ {
+ EggBindingSet *bindings;
+
+ bindings = g_hash_table_lookup (state->bindings, where_object_was);
+
+ if (bindings != NULL)
+ {
+ g_hash_table_remove (state->bindings, where_object_was);
+ return;
+ }
+ }
+
+ g_critical ("Failed to find bindings for %p", where_object_was);
+}
+
+static void
+egg_state_machine__signal_source_weak_notify (gpointer data,
+ GObject *where_object_was)
+{
+ EggStateMachine *self = data;
+ EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
+ GHashTableIter iter;
+ EggState *state;
+
+ g_assert (EGG_IS_STATE_MACHINE (self));
+ g_assert (where_object_was != NULL);
+
+ g_hash_table_iter_init (&iter, priv->states);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer)&state))
+ {
+ EggSignalGroup *signals;
+
+ signals = g_hash_table_lookup (state->signals, where_object_was);
+
+ if (signals != NULL)
+ {
+ g_hash_table_remove (state->signals, where_object_was);
+ return;
+ }
+ }
+
+ g_critical ("Failed to find signals for %p", where_object_was);
+}
+
+static void
egg_state_free (gpointer data)
{
EggState *state = data;
@@ -62,6 +170,14 @@ egg_state_property_free (gpointer data)
{
EggStateProperty *prop = data;
+ if (prop->object != NULL)
+ {
+ g_object_weak_unref (prop->object,
+ egg_state_machine__property_object_weak_notify,
+ prop);
+ prop->object = NULL;
+ }
+
g_free (prop->property);
g_value_unset (&prop->value);
g_slice_free (EggStateProperty, prop);
@@ -72,6 +188,14 @@ egg_state_style_free (gpointer data)
{
EggStateStyle *style = data;
+ if (style->widget != NULL)
+ {
+ g_object_weak_unref (G_OBJECT (style->widget),
+ egg_state_machine__style_object_weak_notify,
+ style);
+ style->widget = NULL;
+ }
+
g_free (style->name);
g_slice_free (EggStateStyle, style);
}
@@ -198,6 +322,31 @@ egg_state_machine_finalize (GObject *object)
{
EggStateMachine *self = (EggStateMachine *)object;
EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
+ GHashTableIter state_iter;
+ EggState *state;
+
+ g_hash_table_iter_init (&state_iter, priv->states);
+ while (g_hash_table_iter_next (&state_iter, NULL, (gpointer)&state))
+ {
+ GHashTableIter iter;
+ gpointer key;
+
+ g_hash_table_iter_init (&iter, state->bindings);
+ while (g_hash_table_iter_next (&iter, &key, NULL))
+ {
+ g_object_weak_unref (key,
+ egg_state_machine__binding_source_weak_notify,
+ self);
+ }
+
+ g_hash_table_iter_init (&iter, state->signals);
+ while (g_hash_table_iter_next (&iter, &key, NULL))
+ {
+ g_object_weak_unref (key,
+ egg_state_machine__signal_source_weak_notify,
+ self);
+ }
+ }
g_clear_pointer (&priv->states, g_hash_table_unref);
g_clear_pointer (&priv->state, g_free);
@@ -424,11 +573,16 @@ egg_state_machine_add_propertyv (EggStateMachine *self,
state_obj = egg_state_machine_get_state_obj (self, state);
state_prop = g_slice_new0 (EggStateProperty);
+ state_prop->state_machine = self;
state_prop->object = object;
state_prop->property = g_strdup (property);
g_value_init (&state_prop->value, G_VALUE_TYPE (value));
g_value_copy (value, &state_prop->value);
+ g_object_weak_ref (object,
+ egg_state_machine__property_object_weak_notify,
+ state_prop);
+
g_ptr_array_add (state_obj->properties, state_prop);
}
@@ -459,6 +613,10 @@ egg_state_machine_add_binding (EggStateMachine *self,
{
bindings = egg_binding_set_new ();
g_hash_table_insert (state_obj->bindings, source_object, bindings);
+
+ g_object_weak_ref (source_object,
+ egg_state_machine__binding_source_weak_notify,
+ self);
}
egg_binding_set_bind (bindings, source_property, target_object, target_property, flags);
@@ -481,9 +639,14 @@ egg_state_machine_add_style (EggStateMachine *self,
state_obj = egg_state_machine_get_state_obj (self, state);
style_obj = g_slice_new0 (EggStateStyle);
+ style_obj->state_machine = self;
style_obj->name = g_strdup (style);
style_obj->widget = widget;
+ g_object_weak_ref (G_OBJECT (widget),
+ egg_state_machine__style_object_weak_notify,
+ style_obj);
+
g_ptr_array_add (state_obj->styles, style_obj);
}
@@ -511,6 +674,10 @@ egg_state_machine_connect_object (EggStateMachine *self,
{
signals = egg_signal_group_new (G_OBJECT_TYPE (source));
g_hash_table_insert (state_obj->signals, source, signals);
+
+ g_object_weak_ref (source,
+ egg_state_machine__signal_source_weak_notify,
+ self);
}
egg_signal_group_connect_object (signals, detailed_signal, callback, user_data, flags);
diff --git a/tests/test-egg-state-machine.c b/tests/test-egg-state-machine.c
index 38d3c53..957c248 100644
--- a/tests/test-egg-state-machine.c
+++ b/tests/test-egg-state-machine.c
@@ -188,6 +188,18 @@ assert_prop_equal (gpointer obja,
g_value_unset (&vb);
}
+#if 0
+static gboolean
+has_style_class (GtkWidget *widget,
+ const gchar *class_name)
+{
+ GtkStyleContext *style_context;
+
+ style_context = gtk_widget_get_style_context (widget);
+ return gtk_style_context_has_class (style_context, class_name);
+}
+#endif
+
static void
test_state_machine_basic (void)
{
@@ -274,11 +286,89 @@ test_state_machine_basic (void)
g_clear_object (&action);
}
+#define assert_final_ref(o) \
+ G_STMT_START \
+ { \
+ GObject **object_ptr = (GObject **)o; \
+\
+ g_object_add_weak_pointer (*object_ptr, (gpointer *)object_ptr); \
+ g_object_unref (*object_ptr); \
+ g_assert_null (*object_ptr); \
+ } \
+ G_STMT_END
+
+
+/* This test exposed multiple bugs in GObject:
+ * https://bugzilla.gnome.org/show_bug.cgi?id=749659
+ * https://bugzilla.gnome.org/show_bug.cgi?id=749660
+ */
+#if 0
+static void
+test_state_machine_weak_ref_source (void)
+{
+ EggStateMachine *machine;
+ GSimpleAction *action;
+ TestObject *dummy;
+ TestObject *obj;
+ GtkWidget *widget;
+
+ machine = egg_state_machine_new ();
+
+ action = g_simple_action_new ("my-action", NULL);
+ dummy = g_object_new (TEST_TYPE_OBJECT, NULL);
+ obj = g_object_new (TEST_TYPE_OBJECT, NULL);
+ widget = g_object_ref_sink (gtk_event_box_new ());
+
+ g_simple_action_set_enabled (action, FALSE);
+
+ egg_state_machine_connect_object (machine, "state", obj, "frobnicate",
+ G_CALLBACK (obj1_frobnicate), dummy, G_CONNECT_SWAPPED);
+ egg_state_machine_add_binding (machine, "state", obj, "string", dummy, "string", 0);
+ egg_state_machine_add_property (machine, "state", action, "enabled", TRUE);
+ egg_state_machine_add_style (machine, "state", widget, "testing");
+
+ egg_state_machine_set_state (machine, "state");
+ g_assert_cmpstr (egg_state_machine_get_state (machine), ==, "state");
+
+ /* Check that everything is working */
+ g_signal_emit_by_name (obj, "frobnicate");
+ g_assert_cmpint (dummy->obj1_count, ==, 1);
+ g_object_set (obj, "string", "hello world", NULL);
+ assert_prop_equal (obj, dummy, "string");
+ g_assert_cmpint (g_action_get_enabled (G_ACTION (action)), ==, TRUE);
+ g_assert (has_style_class (widget, "testing"));
+
+ /* Destroy the source objects while still in the state */
+ assert_final_ref (&widget);
+ assert_final_ref (&obj);
+ assert_final_ref (&dummy);
+ assert_final_ref (&action);
+
+ /* Go back and forth between the states as this would cause
+ * a warning if the source objects did not have a weakref on them
+ */
+ egg_state_machine_set_state (machine, NULL);
+ g_assert_cmpstr (egg_state_machine_get_state (machine), ==, NULL);
+ egg_state_machine_set_state (machine, "state");
+ g_assert_cmpstr (egg_state_machine_get_state (machine), ==, "state");
+ egg_state_machine_set_state (machine, "empty");
+ g_assert_cmpstr (egg_state_machine_get_state (machine), ==, "empty");
+ egg_state_machine_set_state (machine, "state");
+ g_assert_cmpstr (egg_state_machine_get_state (machine), ==, "state");
+ egg_state_machine_set_state (machine, NULL);
+ g_assert_cmpstr (egg_state_machine_get_state (machine), ==, NULL);
+
+ assert_final_ref (&machine);
+}
+#endif
+
gint
main (gint argc,
gchar *argv[])
{
g_test_init (&argc, &argv, NULL);
+ gtk_init (&argc, &argv);
g_test_add_func ("/Egg/StateMachine/basic", test_state_machine_basic);
+ /*g_test_add_func ("/Egg/StateMachine/weak-ref-source", test_state_machine_weak_ref_source);*/
return g_test_run ();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]