[gnome-builder] egg-state-machine: Weak ref the source objects



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]