[glib/tls: 3/7] gmain: add g_source_add_child_source and g_source_remove_child_source



commit baf6330e9c19cdc0ccf09c1305e3f339fe64bbe2
Author: Dan Winship <danw gnome org>
Date:   Sat Nov 6 10:11:15 2010 -0400

    gmain: add g_source_add_child_source and g_source_remove_child_source
    
    This adds "child source" support to GSource. A child source behaves
    basically like a GPollFD; when you add a source to a context, all of
    its child sources are added with the same priority; when you destroy a
    source, all of its child sources are destroyed; and when a child
    source triggers, its parent source's dispatch function is run as well.
    
    Use cases include:
    
        - adding a GTimeoutSource to another source to cause the source to
          automatically trigger after a certain timeout.
    
        - wrapping an existing source type with a new type that has
          a "better" callback signature (eg, including a source
          object).
    
        - creating a source that triggers based on different conditions
          at different times.

 docs/reference/glib/glib-sections.txt |    2 +
 glib/glib.symbols                     |    2 +
 glib/gmain.c                          |  261 +++++++++++++++++++++++++++------
 glib/gmain.h                          |   17 ++-
 4 files changed, 233 insertions(+), 49 deletions(-)
---
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index dca0a98..1a9ad47 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -472,6 +472,8 @@ g_main_context_get_poll_func
 GPollFunc
 g_main_context_add_poll
 g_main_context_remove_poll
+g_main_context_add_child_source
+g_main_context_remove_child_source
 g_main_depth
 g_main_current_source
 g_main_set_poll_func
diff --git a/glib/glib.symbols b/glib/glib.symbols
index 73ec856..f7cc7d8 100644
--- a/glib/glib.symbols
+++ b/glib/glib.symbols
@@ -722,6 +722,7 @@ g_main_loop_quit
 g_main_loop_ref
 g_main_loop_run
 g_main_loop_unref
+g_source_add_child_source
 g_source_add_poll
 g_source_attach
 g_source_destroy
@@ -739,6 +740,7 @@ g_source_ref
 g_source_remove
 g_source_remove_by_funcs_user_data
 g_source_remove_by_user_data
+g_source_remove_child_source
 g_source_remove_poll
 g_source_set_callback
 g_source_set_callback_indirect
diff --git a/glib/gmain.c b/glib/gmain.c
index 05b0813..833ac76 100644
--- a/glib/gmain.c
+++ b/glib/gmain.c
@@ -313,6 +313,12 @@ struct _GPollRec
   gint priority;
 };
 
+struct _GSourcePrivate
+{
+  GSList *child_sources;
+  GSource *parent_source;
+};
+
 #ifdef G_THREADS_ENABLED
 #define LOCK_CONTEXT(context) g_static_mutex_lock (&context->mutex)
 #define UNLOCK_CONTEXT(context) g_static_mutex_unlock (&context->mutex)
@@ -344,6 +350,9 @@ static void g_source_unref_internal             (GSource      *source,
 static void g_source_destroy_internal           (GSource      *source,
 						 GMainContext *context,
 						 gboolean      have_lock);
+static void g_source_set_priority_unlocked      (GSource      *source,
+						 GMainContext *context,
+						 gint          priority);
 static void g_main_context_poll                 (GMainContext *context,
 						 gint          timeout,
 						 gint          priority,
@@ -848,12 +857,21 @@ g_source_list_add (GSource      *source,
 {
   GSource *tmp_source, *last_source;
   
-  last_source = NULL;
-  tmp_source = context->source_list;
-  while (tmp_source && tmp_source->priority <= source->priority)
+  if (source->priv && source->priv->parent_source)
+    {
+      /* Put the source immediately before its parent */
+      tmp_source = source->priv->parent_source;
+      last_source = source->priv->parent_source->prev;
+    }
+  else
     {
-      last_source = tmp_source;
-      tmp_source = tmp_source->next;
+      last_source = NULL;
+      tmp_source = context->source_list;
+      while (tmp_source && tmp_source->priority <= source->priority)
+	{
+	  last_source = tmp_source;
+	  tmp_source = tmp_source->next;
+	}
     }
 
   source->next = tmp_source;
@@ -885,6 +903,39 @@ g_source_list_remove (GSource      *source,
   source->next = NULL;
 }
 
+static guint
+g_source_attach_unlocked (GSource      *source,
+			  GMainContext *context)
+{
+  guint result = 0;
+  GSList *tmp_list;
+
+  source->context = context;
+  result = source->source_id = context->next_id++;
+
+  source->ref_count++;
+  g_source_list_add (source, context);
+
+  tmp_list = source->poll_fds;
+  while (tmp_list)
+    {
+      g_main_context_add_poll_unlocked (context, source->priority, tmp_list->data);
+      tmp_list = tmp_list->next;
+    }
+
+  if (source->priv)
+    {
+      tmp_list = source->priv->child_sources;
+      while (tmp_list)
+	{
+	  g_source_attach_unlocked (tmp_list->data, context);
+	  tmp_list = tmp_list->next;
+	}
+    }
+
+  return result;
+}
+
 /**
  * g_source_attach:
  * @source: a #GSource
@@ -901,7 +952,6 @@ g_source_attach (GSource      *source,
 		 GMainContext *context)
 {
   guint result = 0;
-  GSList *tmp_list;
 
   g_return_val_if_fail (source->context == NULL, 0);
   g_return_val_if_fail (!SOURCE_DESTROYED (source), 0);
@@ -911,18 +961,7 @@ g_source_attach (GSource      *source,
 
   LOCK_CONTEXT (context);
 
-  source->context = context;
-  result = source->source_id = context->next_id++;
-
-  source->ref_count++;
-  g_source_list_add (source, context);
-
-  tmp_list = source->poll_fds;
-  while (tmp_list)
-    {
-      g_main_context_add_poll_unlocked (context, source->priority, tmp_list->data);
-      tmp_list = tmp_list->next;
-    }
+  result = g_source_attach_unlocked (source, context);
 
 #ifdef G_THREADS_ENABLED
   /* Now wake up the main loop if it is waiting in the poll() */
@@ -972,6 +1011,24 @@ g_source_destroy_internal (GSource      *source,
 	      tmp_list = tmp_list->next;
 	    }
 	}
+
+      if (source->priv && source->priv->child_sources)
+	{
+	  /* This is safe because even if a child_source finalizer or
+	   * closure notify tried to modify source->priv->child_sources
+	   * from outside the lock, it would fail since
+	   * SOURCE_DESTROYED(source) is now TRUE.
+	   */
+	  tmp_list = source->priv->child_sources;
+	  while (tmp_list)
+	    {
+	      g_source_destroy_internal (tmp_list->data, context, TRUE);
+	      g_source_unref_internal (tmp_list->data, context, TRUE);
+	      tmp_list = tmp_list->next;
+	    }
+	  g_slist_free (source->priv->child_sources);
+	  source->priv->child_sources = NULL;
+	}
 	  
       g_source_unref_internal (source, context, TRUE);
     }
@@ -1119,6 +1176,90 @@ g_source_remove_poll (GSource *source,
 }
 
 /**
+ * g_source_add_child_source:
+ * @source:a #GSource
+ * @child_source: a second #GSource that @source should "poll"
+ *
+ * Adds @child_source to @source as a "polled" source; when @source is
+ * added to a #GMainContext, @child_source will be automatically added
+ * with the same priority, when @child_source is triggered, it will
+ * cause @source to dispatch (and won't call @child_source's own
+ * callback), and when @source is destroyed, it will destroy
+ * @child_source as well. (@source will also still be dispatched if
+ * its own prepare/check functions indicate that it is ready.)
+ *
+ * @source will hold a reference on @child_source while @child_source
+ * is attached to it.
+ **/
+void
+g_source_add_child_source (GSource *source,
+			   GSource *child_source)
+{
+  GMainContext *context;
+
+  g_return_if_fail (source != NULL);
+  g_return_if_fail (child_source != NULL);
+  g_return_if_fail (!SOURCE_DESTROYED (source));
+  g_return_if_fail (!SOURCE_DESTROYED (child_source));
+  g_return_if_fail (child_source->context == NULL);
+  g_return_if_fail (child_source->priv == NULL || child_source->priv->parent_source == NULL);
+
+  context = source->context;
+
+  if (context)
+    LOCK_CONTEXT (context);
+
+  if (!source->priv)
+    source->priv = g_slice_new0 (GSourcePrivate);
+  if (!child_source->priv)
+    child_source->priv = g_slice_new0 (GSourcePrivate);
+
+  source->priv->child_sources = g_slist_prepend (source->priv->child_sources,
+						 g_source_ref (child_source));
+  child_source->priv->parent_source = source;
+  g_source_set_priority_unlocked (child_source, context, source->priority);
+
+  if (context)
+    {
+      UNLOCK_CONTEXT (context);
+      g_source_attach (child_source, context);
+    }
+}
+
+/**
+ * g_source_remove_child_source:
+ * @source:a #GSource
+ * @child_source: a #GSource previously passed to
+ *     g_source_add_child_source().
+ *
+ * Detaches @child_source from @source and destroys it.
+ **/
+void
+g_source_remove_child_source (GSource *source,
+			      GSource *child_source)
+{
+  GMainContext *context;
+
+  g_return_if_fail (source != NULL);
+  g_return_if_fail (child_source != NULL);
+  g_return_if_fail (child_source->priv != NULL && child_source->priv->parent_source == source);
+  g_return_if_fail (!SOURCE_DESTROYED (source));
+  g_return_if_fail (!SOURCE_DESTROYED (child_source));
+
+  context = source->context;
+
+  if (context)
+    LOCK_CONTEXT (context);
+
+  source->priv->child_sources = g_slist_remove (source->priv->child_sources, child_source);
+  g_source_destroy_internal (child_source, context, TRUE);
+  g_source_unref_internal (child_source, context, TRUE);
+
+  if (context)
+    UNLOCK_CONTEXT (context);
+}
+
+/**
  * g_source_set_callback_indirect:
  * @source: the source
  * @callback_data: pointer to callback data "object"
@@ -1263,35 +1404,19 @@ g_source_set_funcs (GSource     *source,
   source->source_funcs = funcs;
 }
 
-/**
- * g_source_set_priority:
- * @source: a #GSource
- * @priority: the new priority.
- * 
- * Sets the priority of a source. While the main loop is being
- * run, a source will be dispatched if it is ready to be dispatched and no sources 
- * at a higher (numerically smaller) priority are ready to be dispatched.
- **/
-void
-g_source_set_priority (GSource  *source,
-		       gint      priority)
+static void
+g_source_set_priority_unlocked (GSource      *source,
+				GMainContext *context,
+				gint          priority)
 {
   GSList *tmp_list;
-  GMainContext *context;
-  
-  g_return_if_fail (source != NULL);
-
-  context = source->context;
-
-  if (context)
-    LOCK_CONTEXT (context);
   
   source->priority = priority;
 
   if (context)
     {
       /* Remove the source from the context's source and then
-       * add it back so it is sorted in the correct plcae
+       * add it back so it is sorted in the correct place
        */
       g_source_list_remove (source, source->context);
       g_source_list_add (source, source->context);
@@ -1307,12 +1432,47 @@ g_source_set_priority (GSource  *source,
 	      tmp_list = tmp_list->next;
 	    }
 	}
-      
-      UNLOCK_CONTEXT (source->context);
+    }
+
+  if (source->priv && source->priv->child_sources)
+    {
+      tmp_list = source->priv->child_sources;
+      while (tmp_list)
+	{
+	  g_source_set_priority_unlocked (tmp_list->data, context, priority);
+	  tmp_list = tmp_list->next;
+	}
     }
 }
 
 /**
+ * g_source_set_priority:
+ * @source: a #GSource
+ * @priority: the new priority.
+ *
+ * Sets the priority of a source. While the main loop is being run, a
+ * source will be dispatched if it is ready to be dispatched and no
+ * sources at a higher (numerically smaller) priority are ready to be
+ * dispatched.
+ **/
+void
+g_source_set_priority (GSource  *source,
+		       gint      priority)
+{
+  GMainContext *context;
+
+  g_return_if_fail (source != NULL);
+
+  context = source->context;
+
+  if (context)
+    LOCK_CONTEXT (context);
+  g_source_set_priority_unlocked (source, context, priority);
+  if (context)
+    UNLOCK_CONTEXT (source->context);
+}
+
+/**
  * g_source_get_priority:
  * @source: a #GSource
  * 
@@ -2224,7 +2384,8 @@ g_main_dispatch (GMainContext *context)
 
       source->flags &= ~G_SOURCE_READY;
 
-      if (!SOURCE_DESTROYED (source))
+      if (!SOURCE_DESTROYED (source) &&
+	  (!source->priv || !source->priv->parent_source))
 	{
 	  gboolean was_in_call;
 	  gpointer user_data = NULL;
@@ -2596,7 +2757,13 @@ g_main_context_prepare (GMainContext *context,
 	  context->in_check_or_prepare--;
 
 	  if (result)
-	    source->flags |= G_SOURCE_READY;
+	    {
+	      GSource *ready_source = source;
+
+	      while (ready_source->priv && ready_source->priv->parent_source)
+		ready_source = ready_source->priv->parent_source;
+	      ready_source->flags |= G_SOURCE_READY;
+	    }
 	}
 
       if (source->flags & G_SOURCE_READY)
@@ -2788,7 +2955,13 @@ g_main_context_check (GMainContext *context,
 	  context->in_check_or_prepare--;
 	  
 	  if (result)
-	    source->flags |= G_SOURCE_READY;
+	    {
+	      GSource *ready_source = source;
+
+	      while (ready_source->priv && ready_source->priv->parent_source)
+		ready_source = ready_source->priv->parent_source;
+	      ready_source->flags |= G_SOURCE_READY;
+	    }
 	}
 
       if (source->flags & G_SOURCE_READY)
diff --git a/glib/gmain.h b/glib/gmain.h
index 3a7bba0..bd94651 100644
--- a/glib/gmain.h
+++ b/glib/gmain.h
@@ -53,6 +53,7 @@ typedef struct _GMainLoop               GMainLoop;
  * representing an event source.
  */
 typedef struct _GSource                 GSource;
+typedef struct _GSourcePrivate          GSourcePrivate;
 
 /**
  * GSourceCallbackFuncs:
@@ -157,7 +158,8 @@ struct _GSource
   GSource *next;
 
   char    *name;
-  gpointer reserved2;
+
+  GSourcePrivate *priv;
 };
 
 struct _GSourceCallbackFuncs
@@ -358,10 +360,15 @@ void g_source_set_callback_indirect (GSource              *source,
                                      gpointer              callback_data,
                                      GSourceCallbackFuncs *callback_funcs);
 
-void     g_source_add_poll         (GSource        *source,
-                                    GPollFD        *fd);
-void     g_source_remove_poll      (GSource        *source,
-                                    GPollFD        *fd);
+void     g_source_add_poll            (GSource        *source,
+				       GPollFD        *fd);
+void     g_source_remove_poll         (GSource        *source,
+				       GPollFD        *fd);
+
+void     g_source_add_child_source    (GSource        *source,
+				       GSource        *child_source);
+void     g_source_remove_child_source (GSource        *source,
+				       GSource        *child_source);
 
 #ifndef G_DISABLE_DEPRECATED
 void     g_source_get_current_time (GSource        *source,



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