diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index e12a855ba..e851942f6 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -528,6 +528,8 @@ g_source_get_context g_source_set_callback GSourceFunc g_source_set_callback_indirect +g_source_set_ready_time +g_source_get_ready_time g_source_add_poll g_source_remove_poll g_source_add_child_source diff --git a/glib/glib.symbols b/glib/glib.symbols index 724e8d9a5..b8f39b018 100644 --- a/glib/glib.symbols +++ b/glib/glib.symbols @@ -638,6 +638,7 @@ g_source_get_current_time g_source_get_id g_source_get_name g_source_get_priority +g_source_get_ready_time g_source_new g_source_ref g_source_remove @@ -653,6 +654,7 @@ g_source_set_name g_source_set_name_by_id g_source_is_destroyed g_source_set_priority +g_source_set_ready_time g_source_unref g_idle_add g_idle_add_full diff --git a/glib/gmain.c b/glib/gmain.c index 071694c0c..b294459cf 100644 --- a/glib/gmain.c +++ b/glib/gmain.c @@ -317,6 +317,8 @@ struct _GSourcePrivate { GSList *child_sources; GSource *parent_source; + + gint64 ready_time; }; typedef struct _GSourceIter @@ -853,6 +855,8 @@ g_source_new (GSourceFuncs *source_funcs, source->flags = G_HOOK_FLAG_ACTIVE; + source->priv->ready_time = -1; + /* NULL/0 initialization for all other fields */ return source; @@ -1697,6 +1701,73 @@ g_source_get_priority (GSource *source) return source->priority; } +/** + * g_source_set_ready_time: + * @source: a #GSource + * @ready_time: the monotonic time at which the source will be ready, + * 0 for "immediately", -1 for "never" + * + * Sets a #GSource to be dispatched when the given monotonic time is + * reached (or passed). If the monotonic time is in the past (as it + * always will be if @ready_time is 0) then the source will be + * dispatched immediately. + * + * If @ready_time is -1 then the source is never woken up on the basis + * of the passage of time. + * + * Dispatching the source does not reset the ready time. You should do + * so yourself, from the source dispatch function. + * + * Since: 2.36 + **/ +void +g_source_set_ready_time (GSource *source, + gint64 ready_time) +{ + GMainContext *context; + + g_return_if_fail (source != NULL); + g_return_if_fail (source->ref_count > 0); + + if (source->priv->ready_time == ready_time) + return; + + context = source->context; + + if (context) + LOCK_CONTEXT (context); + + source->priv->ready_time = ready_time; + + if (context) + { + /* Quite likely that we need to change the timeout on the poll */ + if (!SOURCE_BLOCKED (source)) + g_wakeup_signal (context->wakeup); + UNLOCK_CONTEXT (context); + } +} + +/** + * g_source_get_ready_time: + * @source: a #GSource + * + * Gets the "ready time" of @source, as set by + * g_source_set_ready_time(). + * + * Any time before the current monotonic time (including 0) is an + * indication that the source will fire immediately. + * + * Returns: the monotonic ready time, -1 for "never" + **/ +gint64 +g_source_get_ready_time (GSource *source) +{ + g_return_val_if_fail (source != NULL, -1); + + return source->priv->ready_time; +} + /** * g_source_set_can_recurse: * @source: a #GSource @@ -3066,6 +3137,31 @@ g_main_context_prepare (GMainContext *context, result = FALSE; } + if (result == FALSE && source->priv->ready_time != -1) + { + if (!context->time_is_fresh) + { + context->time = g_get_monotonic_time (); + context->time_is_fresh = TRUE; + } + + if (source->priv->ready_time <= context->time) + { + source_timeout = 0; + result = TRUE; + } + else + { + gint timeout; + + /* rounding down will lead to spinning, so always round up */ + timeout = (source->priv->ready_time - context->time + 999) / 1000; + + if (source_timeout < 0 || timeout < source_timeout) + source_timeout = timeout; + } + } + if (result) { GSource *ready_source = source; @@ -3243,6 +3339,7 @@ g_main_context_check (GMainContext *context, if (check) { + /* If the check function is set, call it. */ context->in_check_or_prepare++; UNLOCK_CONTEXT (context); @@ -3254,6 +3351,18 @@ g_main_context_check (GMainContext *context, else result = FALSE; + if (result == FALSE && source->priv->ready_time != -1) + { + if (!context->time_is_fresh) + { + context->time = g_get_monotonic_time (); + context->time_is_fresh = TRUE; + } + + if (source->priv->ready_time <= context->time) + result = TRUE; + } + if (result) { GSource *ready_source = source; diff --git a/glib/gmain.h b/glib/gmain.h index 04cf2f80e..e8d64f403 100644 --- a/glib/gmain.h +++ b/glib/gmain.h @@ -88,7 +88,9 @@ typedef struct _GSourceCallbackFuncs GSourceCallbackFuncs; * be -1 if all sources returned -1, or it will be the minimum of all the * @timeout_ values returned which were >= 0. Since 2.36 this may * be %NULL, in which case the effect is as if the function always - * returns %FALSE with a timeout of -1. + * returns %FALSE with a timeout of -1. If @prepare returns a + * timeout and the source also has a 'ready time' set then the + * nearer of the two will be used. * @check: Called after all the file descriptors are polled. The source * should return %TRUE if it is ready to be dispatched. Note that some * time may have passed since the previous prepare function was called, @@ -462,6 +464,11 @@ GLIB_AVAILABLE_IN_ALL void g_source_set_name_by_id (guint tag, const char *name); +GLIB_AVAILABLE_IN_2_36 +void g_source_set_ready_time (GSource *source, + gint64 ready_time); +GLIB_AVAILABLE_IN_2_36 +gint64 g_source_get_ready_time (GSource *source); /* Used to implement g_source_connect_closure and internally*/ GLIB_AVAILABLE_IN_ALL