[gsignal] Fix closure invalidation
authorRyan Lortie <desrt@desrt.ca>
Mon, 8 Oct 2012 22:20:24 +0000 (18:20 -0400)
committerRyan Lortie <desrt@desrt.ca>
Tue, 9 Oct 2012 15:40:45 +0000 (11:40 -0400)
If the closure is invalidated we drop the ref on the signal handler
node, but if the signal is currently being dispatched, the ref could be
held elsewhere.

Flag that we no longer have an outstanding invalidation handler so that
we don't try to unregister ourselves when the other ref drops.

Add a testcase that catches this situation.

gobject/tests/signals.c

index f9e0da4..019d9e4 100644 (file)
@@ -109,6 +109,14 @@ test_class_init (TestClass *klass)
 
   klass->all_types = all_types_handler;
 
+  g_signal_new ("simple",
+                G_TYPE_FROM_CLASS (klass),
+                G_SIGNAL_RUN_LAST,
+                0,
+                NULL, NULL,
+                NULL,
+                G_TYPE_NONE,
+                0);
   g_signal_new ("generic-marshaller-1",
                 G_TYPE_FROM_CLASS (klass),
                 G_SIGNAL_RUN_LAST,
@@ -800,6 +808,34 @@ test_connect (void)
   g_object_unref (test);
 }
 
+static void
+simple_handler1 (GObject *sender,
+                 GObject *target)
+{
+  g_object_unref (target);
+}
+
+static void
+simple_handler2 (GObject *sender,
+                 GObject *target)
+{
+  g_object_unref (target);
+}
+
+static void
+test_destroy_target_object (void)
+{
+  Test *sender, *target1, *target2;
+
+  sender = g_object_new (test_get_type (), NULL);
+  target1 = g_object_new (test_get_type (), NULL);
+  target2 = g_object_new (test_get_type (), NULL);
+  g_signal_connect_object (sender, "simple", G_CALLBACK (simple_handler1), target1, 0);
+  g_signal_connect_object (sender, "simple", G_CALLBACK (simple_handler2), target2, 0);
+  g_signal_emit_by_name (sender, "simple");
+  g_object_unref (sender);
+}
+
 /* --- */
 
 int
@@ -812,6 +848,7 @@ main (int argc,
 
   g_test_add_func ("/gobject/signals/all-types", test_all_types);
   g_test_add_func ("/gobject/signals/variant", test_variant_signal);
+  g_test_add_func ("/gobject/signals/destroy-target-object", test_destroy_target_object);
   g_test_add_func ("/gobject/signals/generic-marshaller-1", test_generic_marshaller_signal_1);
   g_test_add_func ("/gobject/signals/generic-marshaller-2", test_generic_marshaller_signal_2);
   g_test_add_func ("/gobject/signals/generic-marshaller-enum-return-signed", test_generic_marshaller_signal_enum_return_signed);