[glib: 1/2] gsignal: Add a new GSignalFlag to mark the first run of an accumulator function




commit 9d1455444cdc1af7c867487385a3cb7b46bbfd08
Author: Sebastian Dröge <sebastian centricular com>
Date:   Thu Feb 16 10:09:27 2012 +0100

    gsignal: Add a new GSignalFlag to mark the first run of an accumulator function
    
    Also add a test for signal accumulators. There was none before, and this
    one now also covers the new flag.
    
    Fixes https://gitlab.gnome.org/GNOME/glib/issues/514

 gobject/gsignal.c       |  13 ++++--
 gobject/gsignal.h       |  13 ++++--
 gobject/tests/signals.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 121 insertions(+), 7 deletions(-)
---
diff --git a/gobject/gsignal.c b/gobject/gsignal.c
index 81d2834b9..c3b453de7 100644
--- a/gobject/gsignal.c
+++ b/gobject/gsignal.c
@@ -1717,6 +1717,7 @@ g_signal_newv (const gchar       *signal_name,
     g_return_val_if_fail (accumulator == NULL, 0);
   if (!accumulator)
     g_return_val_if_fail (accu_data == NULL, 0);
+  g_return_val_if_fail ((signal_flags & G_SIGNAL_ACCUMULATOR_FIRST_RUN) == 0, 0);
 
   if (!is_canonical (signal_name))
     {
@@ -3241,6 +3242,8 @@ accumulate (GSignalInvocationHint *ihint,
   continue_emission = accumulator->func (ihint, return_accu, handler_return, accumulator->data);
   g_value_reset (handler_return);
 
+  ihint->run_type &= ~G_SIGNAL_ACCUMULATOR_FIRST_RUN;
+
   return continue_emission;
 }
 
@@ -3380,7 +3383,7 @@ g_signal_emit_valist (gpointer instance,
          emission.instance = instance;
          emission.ihint.signal_id = signal_id;
          emission.ihint.detail = detail;
-         emission.ihint.run_type = run_type;
+         emission.ihint.run_type = run_type | G_SIGNAL_ACCUMULATOR_FIRST_RUN;
          emission.state = EMISSION_RUN;
          emission.chain_type = instance_type;
          emission_push (&emission);
@@ -3658,7 +3661,7 @@ signal_emit_unlocked_R (SignalNode   *node,
   if (handler_list)
     handler_ref (handler_list);
   
-  emission.ihint.run_type = G_SIGNAL_RUN_FIRST;
+  emission.ihint.run_type = G_SIGNAL_RUN_FIRST | G_SIGNAL_ACCUMULATOR_FIRST_RUN;
   
   if ((node->flags & G_SIGNAL_RUN_FIRST) && class_closure)
     {
@@ -3766,7 +3769,8 @@ signal_emit_unlocked_R (SignalNode   *node,
        goto EMIT_RESTART;
     }
   
-  emission.ihint.run_type = G_SIGNAL_RUN_LAST;
+  emission.ihint.run_type &= ~G_SIGNAL_RUN_FIRST;
+  emission.ihint.run_type |= G_SIGNAL_RUN_LAST;
   
   if ((node->flags & G_SIGNAL_RUN_LAST) && class_closure)
     {
@@ -3837,7 +3841,8 @@ signal_emit_unlocked_R (SignalNode   *node,
   
  EMIT_CLEANUP:
   
-  emission.ihint.run_type = G_SIGNAL_RUN_CLEANUP;
+  emission.ihint.run_type &= ~G_SIGNAL_RUN_LAST;
+  emission.ihint.run_type |= G_SIGNAL_RUN_CLEANUP;
   
   if ((node->flags & G_SIGNAL_RUN_CLEANUP) && class_closure)
     {
diff --git a/gobject/gsignal.h b/gobject/gsignal.h
index 5cc2b6dfa..536102dad 100644
--- a/gobject/gsignal.h
+++ b/gobject/gsignal.h
@@ -119,7 +119,10 @@ typedef gboolean (*GSignalAccumulator)     (GSignalInvocationHint *ihint,
  * @G_SIGNAL_DEPRECATED: The signal is deprecated and will be removed
  *   in a future version. A warning will be generated if it is connected while
  *   running with G_ENABLE_DIAGNOSTIC=1.  Since 2.32.
- * 
+ * @G_SIGNAL_ACCUMULATOR_FIRST_RUN: Only used in #GSignalAccumulator accumulator
+ *   functions for the #GSignalInvocationHint::run_type field to mark the first
+ *   call to the accumulator function for a signal emission.  Since 2.68.
+ *
  * The signal flags are used to specify a signal's behaviour, the overall
  * signal description outlines how especially the RUN flags control the
  * stages of a signal emission.
@@ -134,7 +137,9 @@ typedef enum
   G_SIGNAL_ACTION      = 1 << 5,
   G_SIGNAL_NO_HOOKS    = 1 << 6,
   G_SIGNAL_MUST_COLLECT = 1 << 7,
-  G_SIGNAL_DEPRECATED   = 1 << 8
+  G_SIGNAL_DEPRECATED   = 1 << 8,
+  /* normal signal flags until 1 << 16 */
+  G_SIGNAL_ACCUMULATOR_FIRST_RUN    = 1 << 17,
 } GSignalFlags;
 /**
  * G_SIGNAL_FLAGS_MASK:
@@ -215,7 +220,9 @@ typedef enum
  * @detail: The detail passed on for this emission
  * @run_type: The stage the signal emission is currently in, this
  *  field will contain one of %G_SIGNAL_RUN_FIRST,
- *  %G_SIGNAL_RUN_LAST or %G_SIGNAL_RUN_CLEANUP.
+ *  %G_SIGNAL_RUN_LAST or %G_SIGNAL_RUN_CLEANUP and %G_SIGNAL_ACCUMULATOR_FIRST_RUN.
+ *  %G_SIGNAL_ACCUMULATOR_FIRST_RUN is only set for the first run of the accumulator
+ *  function for a signal emission.
  * 
  * The #GSignalInvocationHint structure is used to pass on additional information
  * to callbacks during a signal emission.
diff --git a/gobject/tests/signals.c b/gobject/tests/signals.c
index 55d5207ca..37d06a237 100644
--- a/gobject/tests/signals.c
+++ b/gobject/tests/signals.c
@@ -185,6 +185,7 @@ struct _Test
 };
 
 static void all_types_handler (Test *test, int i, gboolean b, char c, guchar uc, guint ui, glong l, gulong 
ul, MyEnum e, MyFlags f, float fl, double db, char *str, GParamSpec *param, GBytes *bytes, gpointer ptr, Test 
*obj, GVariant *var, gint64 i64, guint64 ui64);
+static gboolean accumulator_sum (GSignalInvocationHint *ihint, GValue *return_accu, const GValue 
*handler_return, gpointer data);
 
 struct _TestClass
 {
@@ -238,6 +239,14 @@ test_class_init (TestClass *klass)
                 NULL,
                 G_TYPE_NONE,
                 0);
+  g_signal_new ("simple-accumulator",
+                G_TYPE_FROM_CLASS (klass),
+                G_SIGNAL_RUN_LAST,
+                0,
+                accumulator_sum, NULL,
+                NULL,
+                G_TYPE_INT,
+                0);
   g_signal_new ("generic-marshaller-1",
                 G_TYPE_FROM_CLASS (klass),
                 G_SIGNAL_RUN_LAST,
@@ -1127,6 +1136,97 @@ test_invocation_hint (void)
   g_object_unref (test);
 }
 
+static gboolean
+accumulator_sum (GSignalInvocationHint *ihint,
+                 GValue                *return_accu,
+                 const GValue          *handler_return,
+                 gpointer               data)
+{
+  gint acc = g_value_get_int (return_accu);
+  gint ret = g_value_get_int (handler_return);
+
+  g_assert_cmpint (ret, >, 0);
+
+  if (ihint->run_type & G_SIGNAL_ACCUMULATOR_FIRST_RUN)
+    {
+      g_assert_cmpint (acc, ==, 0);
+      g_assert_cmpint (ret, ==, 1);
+      g_assert_true (ihint->run_type & G_SIGNAL_RUN_FIRST);
+      g_assert_false (ihint->run_type & G_SIGNAL_RUN_LAST);
+    }
+  else if (ihint->run_type & G_SIGNAL_RUN_FIRST)
+    {
+      /* Only the first signal handler was called so far */
+      g_assert_cmpint (acc, ==, 1);
+      g_assert_cmpint (ret, ==, 2);
+      g_assert_false (ihint->run_type & G_SIGNAL_RUN_LAST);
+    }
+  else if (ihint->run_type & G_SIGNAL_RUN_LAST)
+    {
+      /* Only the first two signal handler were called so far */
+      g_assert_cmpint (acc, ==, 3);
+      g_assert_cmpint (ret, ==, 3);
+      g_assert_false (ihint->run_type & G_SIGNAL_RUN_FIRST);
+    }
+  else
+    {
+      g_assert_not_reached ();
+    }
+
+  g_value_set_int (return_accu, acc + ret);
+
+  /* Continue with the other signal handlers as long as the sum is < 6,
+   * i.e. don't run simple_accumulator_4_cb() */
+  return acc + ret < 6;
+}
+
+static gint
+simple_accumulator_1_cb (gpointer instance, gpointer data)
+{
+  return 1;
+}
+
+static gint
+simple_accumulator_2_cb (gpointer instance, gpointer data)
+{
+  return 2;
+}
+
+static gint
+simple_accumulator_3_cb (gpointer instance, gpointer data)
+{
+  return 3;
+}
+
+static gint
+simple_accumulator_4_cb (gpointer instance, gpointer data)
+{
+  return 4;
+}
+
+static void
+test_accumulator (void)
+{
+  GObject *test;
+  gint ret = -1;
+
+  test = g_object_new (test_get_type (), NULL);
+
+  /* Connect in reverse order to make sure that LAST signal handlers are
+   * called after FIRST signal handlers but signal handlers in each "group"
+   * are called in the order they were registered */
+  g_signal_connect_after (test, "simple-accumulator", G_CALLBACK (simple_accumulator_3_cb), NULL);
+  g_signal_connect_after (test, "simple-accumulator", G_CALLBACK (simple_accumulator_4_cb), NULL);
+  g_signal_connect (test, "simple-accumulator", G_CALLBACK (simple_accumulator_1_cb), NULL);
+  g_signal_connect (test, "simple-accumulator", G_CALLBACK (simple_accumulator_2_cb), NULL);
+  g_signal_emit_by_name (test, "simple-accumulator", &ret);
+
+  /* simple_accumulator_4_cb() is not run because accumulator is 6 */
+  g_assert_cmpint (ret, ==, 6);
+
+  g_object_unref (test);
+}
+
 static gboolean
 in_set (const gchar *s,
         const gchar *set[])
@@ -1153,6 +1253,7 @@ test_introspection (void)
     "simple",
     "simple-detailed",
     "simple-2",
+    "simple-accumulator",
     "generic-marshaller-1",
     "generic-marshaller-2",
     "generic-marshaller-enum-return-signed",
@@ -1578,6 +1679,7 @@ main (int argc,
   g_test_add_func ("/gobject/signals/custom-marshaller", test_custom_marshaller);
   g_test_add_func ("/gobject/signals/connect", test_connect);
   g_test_add_func ("/gobject/signals/emission-hook", test_emission_hook);
+  g_test_add_func ("/gobject/signals/accumulator", test_accumulator);
   g_test_add_func ("/gobject/signals/introspection", test_introspection);
   g_test_add_func ("/gobject/signals/block-handler", test_block_handler);
   g_test_add_func ("/gobject/signals/stop-emission", test_stop_emission);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]