[glib] gmain: allow g_source_get_context() on destroyed sources



commit 26056558be4656ee6e891a4fae5d4198de7519cf
Author: Dan Winship <danw gnome org>
Date:   Mon Jul 30 08:06:57 2012 -0400

    gmain: allow g_source_get_context() on destroyed sources
    
    g_source_get_context() was checking that the source wasn't destroyed
    (since a source doesn't hold a ref on its context, and so
    source->context might point to garbage in that case). However, it's
    useful to be allowed to call g_source_get_context() on a source that
    is destroyed-but-currently-running.
    
    So instead, let g_source_get_context() return the context whenever
    it's non-NULL, and clear the source->context of any sources that are
    still in a context's sources list when the context is freed. Since
    sources are only removed from the list when the source is freed (not
    when it is destroyed), this means that now whenever a source has a
    non-NULL context pointer, then that pointer is valid.
    
    This also means that g_source_get_time() will now return-if-fail
    rather than crashing if it is called on a source whose context has
    been destroyed.
    
    Add tests to glib/tests/mainloop to verify that g_source_get_context()
    and g_source_get_time() work on destroyed sources.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=661767

 glib/gmain.c          |   15 ++++++++++++---
 glib/tests/mainloop.c |   14 ++++++++++++--
 2 files changed, 24 insertions(+), 5 deletions(-)
---
diff --git a/glib/gmain.c b/glib/gmain.c
index d2f42b0..ae815fe 100644
--- a/glib/gmain.c
+++ b/glib/gmain.c
@@ -512,7 +512,10 @@ g_main_context_unref (GMainContext *context)
 
   g_source_iter_init (&iter, context, TRUE);
   while (g_source_iter_next (&iter, &source))
-    g_source_destroy_internal (source, context, FALSE);
+    {
+      source->context = NULL;
+      g_source_destroy_internal (source, context, FALSE);
+    }
 
   g_mutex_clear (&context->mutex);
 
@@ -1203,7 +1206,13 @@ g_source_get_id (GSource *source)
  * @source: a #GSource
  * 
  * Gets the #GMainContext with which the source is associated.
- * Calling this function on a destroyed source is an error.
+ *
+ * You can call this on a source that has been destroyed, provided
+ * that the #GMainContext it was attached to still exists (in which
+ * case it will return that #GMainContext). In particular, you can
+ * always call this function on the source returned from
+ * g_main_current_source(). But calling this function on a source
+ * whose #GMainContext has been destroyed is an error.
  * 
  * Return value: (transfer none) (allow-none): the #GMainContext with which the
  *               source is associated, or %NULL if the context has not
@@ -1212,7 +1221,7 @@ g_source_get_id (GSource *source)
 GMainContext *
 g_source_get_context (GSource *source)
 {
-  g_return_val_if_fail (!SOURCE_DESTROYED (source), NULL);
+  g_return_val_if_fail (source->context != NULL || !SOURCE_DESTROYED (source), NULL);
 
   return source->context;
 }
diff --git a/glib/tests/mainloop.c b/glib/tests/mainloop.c
index d8207b9..e162202 100644
--- a/glib/tests/mainloop.c
+++ b/glib/tests/mainloop.c
@@ -77,7 +77,6 @@ test_maincontext_basic (void)
   g_assert (g_main_context_find_source_by_funcs_user_data (ctx, &funcs, NULL) == NULL);
 
   id = g_source_attach (source, ctx);
-  g_source_unref (source);
   g_assert_cmpint (g_source_get_id (source), ==, id);
   g_assert (g_main_context_find_source_by_id (ctx, id) == source);
 
@@ -85,6 +84,10 @@ test_maincontext_basic (void)
   g_assert_cmpint (g_source_get_priority (source), ==, G_PRIORITY_HIGH);
 
   g_source_destroy (source);
+  g_assert (g_source_get_context (source) == ctx);
+  g_assert (g_main_context_find_source_by_id (ctx, id) == NULL);
+
+  g_source_unref (source);
   g_main_context_unref (ctx);
 
   ctx = g_main_context_default ();
@@ -602,7 +605,7 @@ timeout2_callback (gpointer user_data)
 {
   TimeTestData *data = user_data;
   GSource *source;
-  gint64 time2;
+  gint64 time2, time3;
 
   source = g_main_current_source ();
   g_assert (source == data->timeout2);
@@ -615,6 +618,13 @@ timeout2_callback (gpointer user_data)
   time2 = g_source_get_time (source);
   g_assert_cmpint (data->time1, ==, time2);
 
+  /* The source should still have a valid time even after being
+   * destroyed, since it's currently running.
+   */
+  g_source_destroy (source);
+  time3 = g_source_get_time (source);
+  g_assert_cmpint (time2, ==, time3);
+
   return FALSE;
 }
 



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