[glib: 4/5] GMainContext - Add tests for !1353 and related situations



commit aaa7640f7cebbdbe4b65bdd56fdb7e35fac7ea2b
Author: Sebastian Dröge <sebastian centricular com>
Date:   Mon Feb 3 18:00:12 2020 +0200

    GMainContext - Add tests for !1353 and related situations

 glib/tests/mainloop.c | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 174 insertions(+)
---
diff --git a/glib/tests/mainloop.c b/glib/tests/mainloop.c
index a6dcc794f..1368e9bc0 100644
--- a/glib/tests/mainloop.c
+++ b/glib/tests/mainloop.c
@@ -1832,14 +1832,188 @@ test_maincontext_source_finalization (void)
   g_assert_true (source_finalize_called);
 }
 
+/* GSource implementation which optionally keeps a strong reference to another
+ * GSource until finalization, when it destroys and unrefs the other source.
+ */
+typedef struct {
+  GSource source;
+
+  GSource *other_source;
+} SourceWithSource;
+
+static void
+finalize_source_with_source (GSource *source)
+{
+  SourceWithSource *s = (SourceWithSource *) source;
+
+  if (s->other_source)
+    {
+      g_source_destroy (s->other_source);
+      g_source_unref (s->other_source);
+      s->other_source = NULL;
+    }
+}
+
+static GSourceFuncs source_with_source_funcs = {
+  NULL,
+  NULL,
+  NULL,
+  finalize_source_with_source
+};
+
+static void
+test_maincontext_source_finalization_from_source (gconstpointer user_data)
+{
+  GMainContext *c = g_main_context_new ();
+  GSource *s1, *s2;
+
+  g_test_summary ("Tests if freeing a GSource as part of another GSource "
+                  "during main context destruction works.");
+  g_test_bug ("https://gitlab.gnome.org/GNOME/glib/merge_requests/1353";);
+
+  s1 = g_source_new (&source_with_source_funcs, sizeof (SourceWithSource));
+  s2 = g_source_new (&source_with_source_funcs, sizeof (SourceWithSource));
+  ((SourceWithSource *)s1)->other_source = g_source_ref (s2);
+
+  /* Attach sources in a different order so they end up being destroyed
+   * in a different order by the main context */
+  if (GPOINTER_TO_INT (user_data) < 5)
+    {
+      g_source_attach (s1, c);
+      g_source_attach (s2, c);
+    }
+  else
+    {
+      g_source_attach (s2, c);
+      g_source_attach (s1, c);
+    }
+
+  /* Test a few different permutations here */
+  if (GPOINTER_TO_INT (user_data) % 5 == 0)
+    {
+      /* Get rid of our references so that destroying the context
+       * will unref them immediately */
+      g_source_unref (s1);
+      g_source_unref (s2);
+      g_main_context_unref (c);
+    }
+  else if (GPOINTER_TO_INT (user_data) % 5 == 1)
+    {
+      /* Destroy and free the sources before the context */
+      g_source_destroy (s1);
+      g_source_unref (s1);
+      g_source_destroy (s2);
+      g_source_unref (s2);
+      g_main_context_unref (c);
+    }
+  else if (GPOINTER_TO_INT (user_data) % 5 == 2)
+    {
+      /* Destroy and free the sources before the context */
+      g_source_destroy (s2);
+      g_source_unref (s2);
+      g_source_destroy (s1);
+      g_source_unref (s1);
+      g_main_context_unref (c);
+    }
+  else if (GPOINTER_TO_INT (user_data) % 5 == 3)
+    {
+      /* Destroy and free the context before the sources */
+      g_main_context_unref (c);
+      g_source_unref (s2);
+      g_source_unref (s1);
+    }
+  else if (GPOINTER_TO_INT (user_data) % 5 == 4)
+    {
+      /* Destroy and free the context before the sources */
+      g_main_context_unref (c);
+      g_source_unref (s1);
+      g_source_unref (s2);
+    }
+}
+
+static gboolean
+dispatch_source_with_source (GSource *source, GSourceFunc callback, gpointer user_data)
+{
+  return G_SOURCE_REMOVE;
+}
+
+static GSourceFuncs source_with_source_funcs_dispatch = {
+  NULL,
+  NULL,
+  dispatch_source_with_source,
+  finalize_source_with_source
+};
+
+static void
+test_maincontext_source_finalization_from_dispatch (gconstpointer user_data)
+{
+  GMainContext *c = g_main_context_new ();
+  GSource *s1, *s2;
+
+  g_test_summary ("Tests if freeing a GSource as part of another GSource "
+                  "during main context iteration works.");
+
+  s1 = g_source_new (&source_with_source_funcs_dispatch, sizeof (SourceWithSource));
+  s2 = g_source_new (&source_with_source_funcs_dispatch, sizeof (SourceWithSource));
+  ((SourceWithSource *)s1)->other_source = g_source_ref (s2);
+
+  g_source_attach (s1, c);
+  g_source_attach (s2, c);
+
+  if (GPOINTER_TO_INT (user_data) == 0)
+    {
+      /* This finalizes s1 as part of the iteration, which then destroys and
+       * frees s2 too */
+      g_source_set_ready_time (s1, 0);
+    }
+  else if (GPOINTER_TO_INT (user_data) == 1)
+    {
+      /* This destroys s2 as part of the iteration but does not free it as
+       * it's still referenced by s1 */
+      g_source_set_ready_time (s2, 0);
+    }
+  else if (GPOINTER_TO_INT (user_data) == 2)
+    {
+      /* This destroys both s1 and s2 as part of the iteration */
+      g_source_set_ready_time (s1, 0);
+      g_source_set_ready_time (s2, 0);
+    }
+
+  /* Get rid of our references so only the main context has one now */
+  g_source_unref (s1);
+  g_source_unref (s2);
+
+  /* Iterate as long as there are sources to dispatch */
+  while (g_main_context_iteration (c, FALSE))
+    {
+      /* Do nothing here */
+    }
+
+  g_main_context_unref (c);
+}
+
 int
 main (int argc, char *argv[])
 {
+  gint i;
+
   g_test_init (&argc, &argv, NULL);
   g_test_bug_base ("http://bugzilla.gnome.org/";);
 
   g_test_add_func ("/maincontext/basic", test_maincontext_basic);
   g_test_add_func ("/maincontext/source_finalization", test_maincontext_source_finalization);
+  for (i = 0; i < 10; i++)
+    {
+      gchar *name = g_strdup_printf ("/maincontext/source_finalization_from_source/%d", i);
+      g_test_add_data_func (name, GINT_TO_POINTER (i), test_maincontext_source_finalization_from_source);
+      g_free (name);
+    }
+  for (i = 0; i < 3; i++)
+    {
+      gchar *name = g_strdup_printf ("/maincontext/source_finalization_from_dispatch/%d", i);
+      g_test_add_data_func (name, GINT_TO_POINTER (i), test_maincontext_source_finalization_from_dispatch);
+      g_free (name);
+    }
   g_test_add_func ("/mainloop/basic", test_mainloop_basic);
   g_test_add_func ("/mainloop/timeouts", test_timeouts);
   g_test_add_func ("/mainloop/priorities", test_priorities);


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