diff options
author | Dan Winship <danw@gnome.org> | 2010-11-06 10:11:15 -0400 |
---|---|---|
committer | Dan Winship <danw@gnome.org> | 2010-11-26 15:07:28 -0500 |
commit | d15cdbefecc235cfa431ee7de9c35af174bd1552 (patch) | |
tree | 1809d3561342666dc36cbb9b21e4797fc8878c1d | |
parent | e910205557b2461eaf1b2ce94176c6525cc716d1 (diff) | |
download | glib-d15cdbefecc235cfa431ee7de9c35af174bd1552.tar.gz |
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.
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 different callback signature
- creating a source that triggers based on different conditions
at different times.
https://bugzilla.gnome.org/show_bug.cgi?id=634239
-rw-r--r-- | docs/reference/glib/glib-sections.txt | 2 | ||||
-rw-r--r-- | glib/glib.symbols | 2 | ||||
-rw-r--r-- | glib/gmain.c | 266 | ||||
-rw-r--r-- | glib/gmain.h | 17 |
4 files changed, 239 insertions, 48 deletions
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index 1594bc8a9..6540fe8fa 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -535,6 +535,8 @@ GSourceFunc g_source_set_callback_indirect g_source_add_poll g_source_remove_poll +g_source_add_child_source +g_source_remove_child_source g_source_get_time g_source_get_current_time g_source_remove diff --git a/glib/glib.symbols b/glib/glib.symbols index 92f62f2f6..9bb041919 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 05b081341..b02606795 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,94 @@ 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.) + * + * If you need @child_source to do anything on its own when it + * triggers, you can call g_source_set_dummy_callback() on it to set a + * callback that does nothing (except return %TRUE if appropriate). + * + * @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 +1408,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,9 +1436,44 @@ 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); } /** @@ -2596,7 +2760,15 @@ 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) + { + ready_source->flags |= G_SOURCE_READY; + ready_source = ready_source->priv ? ready_source->priv->parent_source : NULL; + } + } } if (source->flags & G_SOURCE_READY) @@ -2788,7 +2960,15 @@ 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) + { + ready_source->flags |= G_SOURCE_READY; + ready_source = ready_source->priv ? ready_source->priv->parent_source : NULL; + } + } } if (source->flags & G_SOURCE_READY) diff --git a/glib/gmain.h b/glib/gmain.h index 3a7bba08f..bd94651cc 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, |