[glib/halfline/debug-metrics: 11/22] Add g_source_set_dispose_function() for setting a dispose function for GSource




commit 1e739e133dd91aafcd33d1cb00966c26ffd619d0
Author: Sebastian Dröge <sebastian centricular com>
Date:   Sun Oct 20 11:10:21 2019 +0300

    Add g_source_set_dispose_function() for setting a dispose function for GSource
    
    This allows GSource implementors to safely clear any other references to
    the GSource while the GSource is still valid, unlike when doing the same
    from the finalize function.
    
    After the dispose function has run, it is valid for the reference count
    of the GSource to be > 0 again to allow the case where another thread in
    the mean-time got access to the GSource reference before the dispose
    function was called.
    
    This allows fixing a thread-safety issue in the GCancellable, GstBus and
    various other GSource implementations.

 glib/gmain.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 glib/gmain.h | 20 ++++++++++++++++++++
 2 files changed, 77 insertions(+)
---
diff --git a/glib/gmain.c b/glib/gmain.c
index e4381f629..2f4f578d1 100644
--- a/glib/gmain.c
+++ b/glib/gmain.c
@@ -357,6 +357,8 @@ struct _GSourcePrivate
    * let it remain empty on Windows) to avoid #ifdef all over the place.
    */
   GSList *fds;
+
+  GSourceDisposeFunc dispose;
 };
 
 typedef struct _GSourceIter
@@ -932,6 +934,39 @@ g_source_new (GSourceFuncs *source_funcs,
   return source;
 }
 
+/**
+ * g_source_set_dispose_function:
+ * @source: A #GSource to set the dispose function on
+ * @dispose: #GSourceDisposeFunc to set on the source
+ *
+ * Set @dispose as dispose function on @source. @dispose will be called once
+ * the reference count of @source reaches 0 but before any of the state of the
+ * source is freed, especially before the finalize function is called.
+ *
+ * This means that at this point @source is still a valid #GSource and it is
+ * allow for the reference count to increase again until @dispose returns.
+ *
+ * The dispose function can be used to clear any "weak" references to the
+ * @source in other data structures in a thread-safe way where it is possible
+ * for another thread to increase the reference count of @source again while
+ * it is being freed.
+ *
+ * The finalize function can not be used for this purpose as at that point
+ * @source is already partially freed and not valid anymore.
+ *
+ * This should only ever be called from #GSource implementations.
+ *
+ * Since: 2.64
+ **/
+void
+g_source_set_dispose_function (GSource            *source,
+                              GSourceDisposeFunc  dispose)
+{
+  g_return_if_fail (source != NULL);
+  g_return_if_fail (source->priv->dispose == NULL);
+  source->priv->dispose = dispose;
+}
+
 /* Holds context's lock */
 static void
 g_source_iter_init (GSourceIter  *iter,
@@ -2085,6 +2120,28 @@ g_source_unref_internal (GSource      *source,
   source->ref_count--;
   if (source->ref_count == 0)
     {
+      /* If there's a dispose function, call this first */
+      if (source->priv->dispose)
+        {
+          /* Temporarily increase the ref count again so that GSource methods
+           * can be called from dispose(). */
+          g_atomic_int_inc (&source->ref_count);
+          if (context)
+            UNLOCK_CONTEXT (context);
+          source->priv->dispose (source);
+          if (context)
+            LOCK_CONTEXT (context);
+
+          /* Now the reference count might be bigger than 0 again, in which
+           * case we simply return from here before freeing the source */
+          if (!g_atomic_int_dec_and_test (&source->ref_count))
+            {
+              if (!have_lock && context)
+                UNLOCK_CONTEXT (context);
+              return;
+            }
+        }
+
       TRACE (GLIB_SOURCE_BEFORE_FREE (source, context,
                                       source->source_funcs->finalize));
 
diff --git a/glib/gmain.h b/glib/gmain.h
index 76e2c14c1..365422bf0 100644
--- a/glib/gmain.h
+++ b/glib/gmain.h
@@ -182,6 +182,19 @@ typedef gboolean (*GSourceFunc)       (gpointer user_data);
 typedef void     (*GChildWatchFunc)   (GPid     pid,
                                        gint     status,
                                        gpointer user_data);
+
+
+/**
+ * GSourceDisposeFunc:
+ * @source: #GSource that is currently being disposed
+ *
+ * Dispose function for @source. See g_source_set_dispose_function() for
+ * details.
+ *
+ * Since: 2.64
+ */
+typedef void (*GSourceDisposeFunc)       (GSource *source);
+
 struct _GSource
 {
   /*< private >*/
@@ -431,6 +444,13 @@ GMainContext *g_main_loop_get_context (GMainLoop    *loop);
 GLIB_AVAILABLE_IN_ALL
 GSource *g_source_new             (GSourceFuncs   *source_funcs,
                                    guint           struct_size);
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+GLIB_AVAILABLE_IN_ALL
+void     g_source_set_dispose_function (GSource            *source,
+                                        GSourceDisposeFunc  dispose);
+G_GNUC_END_IGNORE_DEPRECATIONS
+
 GLIB_AVAILABLE_IN_ALL
 GSource *g_source_ref             (GSource        *source);
 GLIB_AVAILABLE_IN_ALL


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