mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-03-31 04:43:06 +02:00
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
This commit is contained in:
parent
e910205557
commit
d15cdbefec
@ -535,6 +535,8 @@ GSourceFunc
|
|||||||
g_source_set_callback_indirect
|
g_source_set_callback_indirect
|
||||||
g_source_add_poll
|
g_source_add_poll
|
||||||
g_source_remove_poll
|
g_source_remove_poll
|
||||||
|
g_source_add_child_source
|
||||||
|
g_source_remove_child_source
|
||||||
g_source_get_time
|
g_source_get_time
|
||||||
g_source_get_current_time
|
g_source_get_current_time
|
||||||
g_source_remove
|
g_source_remove
|
||||||
|
@ -722,6 +722,7 @@ g_main_loop_quit
|
|||||||
g_main_loop_ref
|
g_main_loop_ref
|
||||||
g_main_loop_run
|
g_main_loop_run
|
||||||
g_main_loop_unref
|
g_main_loop_unref
|
||||||
|
g_source_add_child_source
|
||||||
g_source_add_poll
|
g_source_add_poll
|
||||||
g_source_attach
|
g_source_attach
|
||||||
g_source_destroy
|
g_source_destroy
|
||||||
@ -739,6 +740,7 @@ g_source_ref
|
|||||||
g_source_remove
|
g_source_remove
|
||||||
g_source_remove_by_funcs_user_data
|
g_source_remove_by_funcs_user_data
|
||||||
g_source_remove_by_user_data
|
g_source_remove_by_user_data
|
||||||
|
g_source_remove_child_source
|
||||||
g_source_remove_poll
|
g_source_remove_poll
|
||||||
g_source_set_callback
|
g_source_set_callback
|
||||||
g_source_set_callback_indirect
|
g_source_set_callback_indirect
|
||||||
|
266
glib/gmain.c
266
glib/gmain.c
@ -313,6 +313,12 @@ struct _GPollRec
|
|||||||
gint priority;
|
gint priority;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct _GSourcePrivate
|
||||||
|
{
|
||||||
|
GSList *child_sources;
|
||||||
|
GSource *parent_source;
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef G_THREADS_ENABLED
|
#ifdef G_THREADS_ENABLED
|
||||||
#define LOCK_CONTEXT(context) g_static_mutex_lock (&context->mutex)
|
#define LOCK_CONTEXT(context) g_static_mutex_lock (&context->mutex)
|
||||||
#define UNLOCK_CONTEXT(context) g_static_mutex_unlock (&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,
|
static void g_source_destroy_internal (GSource *source,
|
||||||
GMainContext *context,
|
GMainContext *context,
|
||||||
gboolean have_lock);
|
gboolean have_lock);
|
||||||
|
static void g_source_set_priority_unlocked (GSource *source,
|
||||||
|
GMainContext *context,
|
||||||
|
gint priority);
|
||||||
static void g_main_context_poll (GMainContext *context,
|
static void g_main_context_poll (GMainContext *context,
|
||||||
gint timeout,
|
gint timeout,
|
||||||
gint priority,
|
gint priority,
|
||||||
@ -848,12 +857,21 @@ g_source_list_add (GSource *source,
|
|||||||
{
|
{
|
||||||
GSource *tmp_source, *last_source;
|
GSource *tmp_source, *last_source;
|
||||||
|
|
||||||
last_source = NULL;
|
if (source->priv && source->priv->parent_source)
|
||||||
tmp_source = context->source_list;
|
|
||||||
while (tmp_source && tmp_source->priority <= source->priority)
|
|
||||||
{
|
{
|
||||||
last_source = tmp_source;
|
/* Put the source immediately before its parent */
|
||||||
tmp_source = tmp_source->next;
|
tmp_source = source->priv->parent_source;
|
||||||
|
last_source = source->priv->parent_source->prev;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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;
|
source->next = tmp_source;
|
||||||
@ -885,6 +903,39 @@ g_source_list_remove (GSource *source,
|
|||||||
source->next = NULL;
|
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:
|
* g_source_attach:
|
||||||
* @source: a #GSource
|
* @source: a #GSource
|
||||||
@ -901,7 +952,6 @@ g_source_attach (GSource *source,
|
|||||||
GMainContext *context)
|
GMainContext *context)
|
||||||
{
|
{
|
||||||
guint result = 0;
|
guint result = 0;
|
||||||
GSList *tmp_list;
|
|
||||||
|
|
||||||
g_return_val_if_fail (source->context == NULL, 0);
|
g_return_val_if_fail (source->context == NULL, 0);
|
||||||
g_return_val_if_fail (!SOURCE_DESTROYED (source), 0);
|
g_return_val_if_fail (!SOURCE_DESTROYED (source), 0);
|
||||||
@ -911,18 +961,7 @@ g_source_attach (GSource *source,
|
|||||||
|
|
||||||
LOCK_CONTEXT (context);
|
LOCK_CONTEXT (context);
|
||||||
|
|
||||||
source->context = context;
|
result = g_source_attach_unlocked (source, 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef G_THREADS_ENABLED
|
#ifdef G_THREADS_ENABLED
|
||||||
/* Now wake up the main loop if it is waiting in the poll() */
|
/* 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;
|
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);
|
g_source_unref_internal (source, context, TRUE);
|
||||||
}
|
}
|
||||||
@ -1118,6 +1175,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:
|
* g_source_set_callback_indirect:
|
||||||
* @source: the source
|
* @source: the source
|
||||||
@ -1263,35 +1408,19 @@ g_source_set_funcs (GSource *source,
|
|||||||
source->source_funcs = funcs;
|
source->source_funcs = funcs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static void
|
||||||
* g_source_set_priority:
|
g_source_set_priority_unlocked (GSource *source,
|
||||||
* @source: a #GSource
|
GMainContext *context,
|
||||||
* @priority: the new priority.
|
gint 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)
|
|
||||||
{
|
{
|
||||||
GSList *tmp_list;
|
GSList *tmp_list;
|
||||||
GMainContext *context;
|
|
||||||
|
|
||||||
g_return_if_fail (source != NULL);
|
|
||||||
|
|
||||||
context = source->context;
|
|
||||||
|
|
||||||
if (context)
|
|
||||||
LOCK_CONTEXT (context);
|
|
||||||
|
|
||||||
source->priority = priority;
|
source->priority = priority;
|
||||||
|
|
||||||
if (context)
|
if (context)
|
||||||
{
|
{
|
||||||
/* Remove the source from the context's source and then
|
/* 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_remove (source, source->context);
|
||||||
g_source_list_add (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;
|
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--;
|
context->in_check_or_prepare--;
|
||||||
|
|
||||||
if (result)
|
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)
|
if (source->flags & G_SOURCE_READY)
|
||||||
@ -2788,7 +2960,15 @@ g_main_context_check (GMainContext *context,
|
|||||||
context->in_check_or_prepare--;
|
context->in_check_or_prepare--;
|
||||||
|
|
||||||
if (result)
|
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)
|
if (source->flags & G_SOURCE_READY)
|
||||||
|
17
glib/gmain.h
17
glib/gmain.h
@ -53,6 +53,7 @@ typedef struct _GMainLoop GMainLoop;
|
|||||||
* representing an event source.
|
* representing an event source.
|
||||||
*/
|
*/
|
||||||
typedef struct _GSource GSource;
|
typedef struct _GSource GSource;
|
||||||
|
typedef struct _GSourcePrivate GSourcePrivate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GSourceCallbackFuncs:
|
* GSourceCallbackFuncs:
|
||||||
@ -157,7 +158,8 @@ struct _GSource
|
|||||||
GSource *next;
|
GSource *next;
|
||||||
|
|
||||||
char *name;
|
char *name;
|
||||||
gpointer reserved2;
|
|
||||||
|
GSourcePrivate *priv;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GSourceCallbackFuncs
|
struct _GSourceCallbackFuncs
|
||||||
@ -358,10 +360,15 @@ void g_source_set_callback_indirect (GSource *source,
|
|||||||
gpointer callback_data,
|
gpointer callback_data,
|
||||||
GSourceCallbackFuncs *callback_funcs);
|
GSourceCallbackFuncs *callback_funcs);
|
||||||
|
|
||||||
void g_source_add_poll (GSource *source,
|
void g_source_add_poll (GSource *source,
|
||||||
GPollFD *fd);
|
GPollFD *fd);
|
||||||
void g_source_remove_poll (GSource *source,
|
void g_source_remove_poll (GSource *source,
|
||||||
GPollFD *fd);
|
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
|
#ifndef G_DISABLE_DEPRECATED
|
||||||
void g_source_get_current_time (GSource *source,
|
void g_source_get_current_time (GSource *source,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user