* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
/*
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif /* HAVE_SYS_TIME_H */
-#ifdef HAVE_UNISTD_H
+#ifdef G_OS_UNIX
#include <unistd.h>
-#endif /* HAVE_UNISTD_H */
+#endif /* G_OS_UNIX */
#include <errno.h>
#include <string.h>
#include <windows.h>
#endif /* G_OS_WIN32 */
-#ifdef G_OS_BEOS
-#include <sys/socket.h>
-#include <sys/wait.h>
-#endif /* G_OS_BEOS */
+#include "glib_trace.h"
#include "gmain.h"
#endif
#include "gwakeup.h"
-
+#include "gmain-internal.h"
+#include "glib-init.h"
#include "glib-private.h"
/**
typedef enum
{
G_SOURCE_READY = 1 << G_HOOK_FLAG_USER_SHIFT,
- G_SOURCE_CAN_RECURSE = 1 << (G_HOOK_FLAG_USER_SHIFT + 1)
+ G_SOURCE_CAN_RECURSE = 1 << (G_HOOK_FLAG_USER_SHIFT + 1),
+ G_SOURCE_BLOCKED = 1 << (G_HOOK_FLAG_USER_SHIFT + 2)
} GSourceFlags;
+typedef struct _GSourceList GSourceList;
+
+struct _GSourceList
+{
+ GSource *head, *tail;
+ gint priority;
+};
+
typedef struct _GMainWaiter GMainWaiter;
struct _GMainWaiter
struct _GMainDispatch
{
gint depth;
- GSList *dispatching_sources; /* stack of current sources */
+ GSource *source;
};
#ifdef G_MAIN_POLL_DEBUG
gint timeout; /* Timeout for current iteration */
guint next_id;
- GSource *source_list;
+ GHashTable *overflow_used_source_ids; /* set<guint> */
+ GList *source_lists;
gint in_check_or_prepare;
GPollRec *poll_records, *poll_records_tail;
struct _GTimeoutSource
{
GSource source;
- gint64 expiration;
guint interval;
gboolean seconds;
};
{
GSList *child_sources;
GSource *parent_source;
+
+ gint64 ready_time;
+
+ /* This is currently only used on UNIX, but we always declare it (and
+ * let it remain empty on Windows) to avoid #ifdef all over the place.
+ */
+ GSList *fds;
};
+typedef struct _GSourceIter
+{
+ GMainContext *context;
+ gboolean may_modify;
+ GList *current_list;
+ GSource *source;
+} GSourceIter;
+
#define LOCK_CONTEXT(context) g_mutex_lock (&context->mutex)
#define UNLOCK_CONTEXT(context) g_mutex_unlock (&context->mutex)
#define G_THREAD_SELF g_thread_self ()
#define SOURCE_DESTROYED(source) (((source)->flags & G_HOOK_FLAG_ACTIVE) == 0)
-#define SOURCE_BLOCKED(source) (((source)->flags & G_HOOK_FLAG_IN_CALL) != 0 && \
- ((source)->flags & G_SOURCE_CAN_RECURSE) == 0)
+#define SOURCE_BLOCKED(source) (((source)->flags & G_SOURCE_BLOCKED) != 0)
#define SOURCE_UNREF(source, context) \
G_STMT_START { \
static void g_source_set_priority_unlocked (GSource *source,
GMainContext *context,
gint priority);
+static void g_child_source_remove_internal (GSource *child_source,
+ GMainContext *context);
+
static void g_main_context_poll (GMainContext *context,
gint timeout,
gint priority,
static void g_main_context_remove_poll_unlocked (GMainContext *context,
GPollFD *fd);
-static gboolean g_timeout_prepare (GSource *source,
- gint *timeout);
-static gboolean g_timeout_check (GSource *source);
+static void g_source_iter_init (GSourceIter *iter,
+ GMainContext *context,
+ gboolean may_modify);
+static gboolean g_source_iter_next (GSourceIter *iter,
+ GSource **source);
+static void g_source_iter_clear (GSourceIter *iter);
+
static gboolean g_timeout_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data);
GSourceFunc callback,
gpointer user_data);
+static void block_source (GSource *source);
+
static GMainContext *glib_worker_context;
-static gboolean g_main_context_fork_detected;
G_LOCK_DEFINE_STATIC (main_loop);
static GMainContext *default_main_context;
/* UNIX signals work by marking one of these variables then waking the
* worker context to check on them and dispatch accordingly.
*/
-static volatile gchar unix_signal_pending[NSIG];
-static volatile gboolean any_unix_signal_pending;
+#ifdef HAVE_SIG_ATOMIC_T
+static volatile sig_atomic_t unix_signal_pending[NSIG];
+static volatile sig_atomic_t any_unix_signal_pending;
+#else
+static volatile int unix_signal_pending[NSIG];
+static volatile int any_unix_signal_pending;
+#endif
+static volatile guint unix_signal_refcount[NSIG];
/* Guards all the data below */
G_LOCK_DEFINE_STATIC (unix_signal_lock);
static GSList *unix_signal_watches;
static GSList *unix_child_watches;
-static GSourceFuncs g_unix_signal_funcs =
+GSourceFuncs g_unix_signal_funcs =
{
g_unix_signal_watch_prepare,
g_unix_signal_watch_check,
GSourceFuncs g_timeout_funcs =
{
- g_timeout_prepare,
- g_timeout_check,
+ NULL, /* prepare */
+ NULL, /* check */
g_timeout_dispatch,
NULL
};
void
g_main_context_unref (GMainContext *context)
{
+ GSourceIter iter;
GSource *source;
+ GList *sl_iter;
+ GSourceList *list;
+ gint i;
+
g_return_if_fail (context != NULL);
g_return_if_fail (g_atomic_int_get (&context->ref_count) > 0);
main_context_list = g_slist_remove (main_context_list, context);
G_UNLOCK (main_context_list);
- source = context->source_list;
- while (source)
+ /* Free pending dispatches */
+ for (i = 0; i < context->pending_dispatches->len; i++)
+ g_source_unref_internal (context->pending_dispatches->pdata[i], context, FALSE);
+
+ /* g_source_iter_next() assumes the context is locked. */
+ LOCK_CONTEXT (context);
+ g_source_iter_init (&iter, context, TRUE);
+ while (g_source_iter_next (&iter, &source))
{
- GSource *next = source->next;
- g_source_destroy_internal (source, context, FALSE);
- source = next;
+ source->context = NULL;
+ g_source_destroy_internal (source, context, TRUE);
}
+ UNLOCK_CONTEXT (context);
+
+ for (sl_iter = context->source_lists; sl_iter; sl_iter = sl_iter->next)
+ {
+ list = sl_iter->data;
+ g_slice_free (GSourceList, list);
+ }
+ g_list_free (context->source_lists);
+
+ if (context->overflow_used_source_ids)
+ g_hash_table_destroy (context->overflow_used_source_ids);
g_mutex_clear (&context->mutex);
g_free (context);
}
-#ifdef G_OS_UNIX
-static void
-g_main_context_forked (void)
+/* Helper function used by mainloop/overflow test.
+ */
+GMainContext *
+g_main_context_new_with_next_id (guint next_id)
{
- g_main_context_fork_detected = TRUE;
+ GMainContext *ret = g_main_context_new ();
+
+ ret->next_id = next_id;
+
+ return ret;
}
-#endif
/**
* g_main_context_new:
_g_main_poll_debug = TRUE;
#endif
-#ifdef G_OS_UNIX
- pthread_atfork (NULL, NULL, g_main_context_forked);
-#endif
-
g_once_init_leave (&initialised, TRUE);
}
context->next_id = 1;
- context->source_list = NULL;
+ context->source_lists = NULL;
context->poll_func = g_poll;
}
static void
-free_context_stack (gpointer data)
+free_context (gpointer data)
{
- GQueue *stack = data;
- GMainContext *context;
+ GMainContext *context = data;
- while (!g_queue_is_empty (stack))
- {
- context = g_queue_pop_head (stack);
- g_main_context_release (context);
- if (context)
- g_main_context_unref (context);
- }
- g_queue_free (stack);
+ g_main_context_release (context);
+ if (context)
+ g_main_context_unref (context);
+}
+
+static void
+free_context_stack (gpointer data)
+{
+ g_queue_free_full((GQueue *) data, (GDestroyNotify) free_context);
}
static GPrivate thread_context_stack = G_PRIVATE_INIT (free_context_stack);
/**
* g_main_context_push_thread_default:
- * @context: a #GMainContext, or %NULL for the global default context
+ * @context: (allow-none): a #GMainContext, or %NULL for the global default context
*
* Acquires @context and sets it as the thread-default context for the
* current thread. This will cause certain asynchronous operations
* started in this thread to run under @context and deliver their
* results to its main loop, rather than running under the global
* default context in the main thread. Note that calling this function
- * changes the context returned by
- * g_main_context_get_thread_default(), <emphasis>not</emphasis> the
- * one returned by g_main_context_default(), so it does not affect the
- * context used by functions like g_idle_add().
+ * changes the context returned by g_main_context_get_thread_default(),
+ * not the one returned by g_main_context_default(), so it does not affect
+ * the context used by functions like g_idle_add().
*
* Normally you would call this function shortly after creating a new
* thread, passing it a #GMainContext which will be run by a
/**
* g_main_context_pop_thread_default:
- * @context: a #GMainContext object, or %NULL
+ * @context: (allow-none): a #GMainContext object, or %NULL
*
* Pops @context off the thread-default context stack (verifying that
* it was on the top of the stack).
* operations that want to be able to be run in contexts other than
* the default one should call this method or
* g_main_context_ref_thread_default() to get a #GMainContext to add
- * their #GSource<!-- -->s to. (Note that even in single-threaded
+ * their #GSources to. (Note that even in single-threaded
* programs applications may sometimes want to temporarily push a
* non-default context, so it is not safe to assume that this will
* always return %NULL if you are running in the default thread.)
g_return_val_if_fail (struct_size >= sizeof (GSource), NULL);
source = (GSource*) g_malloc0 (struct_size);
-
+ source->priv = g_slice_new0 (GSourcePrivate);
source->source_funcs = source_funcs;
source->ref_count = 1;
source->flags = G_HOOK_FLAG_ACTIVE;
+ source->priv->ready_time = -1;
+
/* NULL/0 initialization for all other fields */
return source;
}
+/* Holds context's lock */
+static void
+g_source_iter_init (GSourceIter *iter,
+ GMainContext *context,
+ gboolean may_modify)
+{
+ iter->context = context;
+ iter->current_list = NULL;
+ iter->source = NULL;
+ iter->may_modify = may_modify;
+}
+
+/* Holds context's lock */
+static gboolean
+g_source_iter_next (GSourceIter *iter, GSource **source)
+{
+ GSource *next_source;
+
+ if (iter->source)
+ next_source = iter->source->next;
+ else
+ next_source = NULL;
+
+ if (!next_source)
+ {
+ if (iter->current_list)
+ iter->current_list = iter->current_list->next;
+ else
+ iter->current_list = iter->context->source_lists;
+
+ if (iter->current_list)
+ {
+ GSourceList *source_list = iter->current_list->data;
+
+ next_source = source_list->head;
+ }
+ }
+
+ /* Note: unreffing iter->source could potentially cause its
+ * GSourceList to be removed from source_lists (if iter->source is
+ * the only source in its list, and it is destroyed), so we have to
+ * keep it reffed until after we advance iter->current_list, above.
+ */
+
+ if (iter->source && iter->may_modify)
+ SOURCE_UNREF (iter->source, iter->context);
+ iter->source = next_source;
+ if (iter->source && iter->may_modify)
+ iter->source->ref_count++;
+
+ *source = iter->source;
+ return *source != NULL;
+}
+
+/* Holds context's lock. Only necessary to call if you broke out of
+ * the g_source_iter_next() loop early.
+ */
+static void
+g_source_iter_clear (GSourceIter *iter)
+{
+ if (iter->source && iter->may_modify)
+ {
+ SOURCE_UNREF (iter->source, iter->context);
+ iter->source = NULL;
+ }
+}
+
+/* Holds context's lock
+ */
+static GSourceList *
+find_source_list_for_priority (GMainContext *context,
+ gint priority,
+ gboolean create)
+{
+ GList *iter, *last;
+ GSourceList *source_list;
+
+ last = NULL;
+ for (iter = context->source_lists; iter != NULL; last = iter, iter = iter->next)
+ {
+ source_list = iter->data;
+
+ if (source_list->priority == priority)
+ return source_list;
+
+ if (source_list->priority > priority)
+ {
+ if (!create)
+ return NULL;
+
+ source_list = g_slice_new0 (GSourceList);
+ source_list->priority = priority;
+ context->source_lists = g_list_insert_before (context->source_lists,
+ iter,
+ source_list);
+ return source_list;
+ }
+ }
+
+ if (!create)
+ return NULL;
+
+ source_list = g_slice_new0 (GSourceList);
+ source_list->priority = priority;
+
+ if (!last)
+ context->source_lists = g_list_append (NULL, source_list);
+ else
+ {
+ /* This just appends source_list to the end of
+ * context->source_lists without having to walk the list again.
+ */
+ last = g_list_append (last, source_list);
+ }
+ return source_list;
+}
+
/* Holds context's lock
*/
static void
-g_source_list_add (GSource *source,
- GMainContext *context)
+source_add_to_context (GSource *source,
+ GMainContext *context)
{
- GSource *tmp_source, *last_source;
-
- if (source->priv && source->priv->parent_source)
+ GSourceList *source_list;
+ GSource *prev, *next;
+
+ source_list = find_source_list_for_priority (context, source->priority, TRUE);
+
+ if (source->priv->parent_source)
{
+ g_assert (source_list->head != NULL);
+
/* Put the source immediately before its parent */
- tmp_source = source->priv->parent_source;
- last_source = source->priv->parent_source->prev;
+ prev = source->priv->parent_source->prev;
+ next = source->priv->parent_source;
}
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;
- }
+ prev = source_list->tail;
+ next = NULL;
}
- source->next = tmp_source;
- if (tmp_source)
- tmp_source->prev = source;
+ source->next = next;
+ if (next)
+ next->prev = source;
+ else
+ source_list->tail = source;
- source->prev = last_source;
- if (last_source)
- last_source->next = source;
+ source->prev = prev;
+ if (prev)
+ prev->next = source;
else
- context->source_list = source;
+ source_list->head = source;
}
/* Holds context's lock
*/
static void
-g_source_list_remove (GSource *source,
- GMainContext *context)
+source_remove_from_context (GSource *source,
+ GMainContext *context)
{
+ GSourceList *source_list;
+
+ source_list = find_source_list_for_priority (context, source->priority, FALSE);
+ g_return_if_fail (source_list != NULL);
+
if (source->prev)
source->prev->next = source->next;
else
- context->source_list = source->next;
+ source_list->head = source->next;
if (source->next)
source->next->prev = source->prev;
+ else
+ source_list->tail = source->prev;
source->prev = NULL;
source->next = NULL;
+
+ if (source_list->head == NULL)
+ {
+ context->source_lists = g_list_remove (context->source_lists, source_list);
+ g_slice_free (GSourceList, source_list);
+ }
+
+ if (context->overflow_used_source_ids)
+ g_hash_table_remove (context->overflow_used_source_ids,
+ GUINT_TO_POINTER (source->source_id));
+
+}
+
+static void
+assign_source_id_unlocked (GMainContext *context,
+ GSource *source)
+{
+ guint id;
+
+ /* Are we about to overflow back to 0?
+ * See https://bugzilla.gnome.org/show_bug.cgi?id=687098
+ */
+ if (G_UNLIKELY (context->next_id == G_MAXUINT &&
+ context->overflow_used_source_ids == NULL))
+ {
+ GSourceIter iter;
+ GSource *source;
+
+ context->overflow_used_source_ids = g_hash_table_new (NULL, NULL);
+
+ g_source_iter_init (&iter, context, FALSE);
+ while (g_source_iter_next (&iter, &source))
+ {
+ g_hash_table_add (context->overflow_used_source_ids,
+ GUINT_TO_POINTER (source->source_id));
+ }
+ id = G_MAXUINT;
+ g_hash_table_add (context->overflow_used_source_ids, GUINT_TO_POINTER (id));
+ }
+ else if (context->overflow_used_source_ids == NULL)
+ {
+ id = context->next_id++;
+ }
+ else
+ {
+ /*
+ * If we overran G_MAXUINT, we fall back to randomly probing the
+ * source ids for the current context. This will be slower the more
+ * sources there are, but we're mainly concerned right now about
+ * correctness and code size. There's time for a more clever solution
+ * later.
+ */
+ do
+ id = g_random_int ();
+ while (id == 0 ||
+ g_hash_table_contains (context->overflow_used_source_ids,
+ GUINT_TO_POINTER (id)));
+ g_hash_table_add (context->overflow_used_source_ids, GUINT_TO_POINTER (id));
+ }
+
+ source->source_id = id;
}
static guint
g_source_attach_unlocked (GSource *source,
- GMainContext *context)
+ GMainContext *context,
+ gboolean do_wakeup)
{
- guint result = 0;
GSList *tmp_list;
source->context = context;
- result = source->source_id = context->next_id++;
-
+ assign_source_id_unlocked (context, source);
source->ref_count++;
- g_source_list_add (source, context);
+ source_add_to_context (source, context);
- tmp_list = source->poll_fds;
- while (tmp_list)
+ if (!SOURCE_BLOCKED (source))
{
- g_main_context_add_poll_unlocked (context, source->priority, tmp_list->data);
- tmp_list = tmp_list->next;
+ 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;
+ }
+
+ for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
+ g_main_context_add_poll_unlocked (context, source->priority, tmp_list->data);
}
- if (source->priv)
+ tmp_list = source->priv->child_sources;
+ while (tmp_list)
{
- tmp_list = source->priv->child_sources;
- while (tmp_list)
- {
- g_source_attach_unlocked (tmp_list->data, context);
- tmp_list = tmp_list->next;
- }
+ g_source_attach_unlocked (tmp_list->data, context, FALSE);
+ tmp_list = tmp_list->next;
}
- return result;
+ /* If another thread has acquired the context, wake it up since it
+ * might be in poll() right now.
+ */
+ if (do_wakeup && context->owner && context->owner != G_THREAD_SELF)
+ g_wakeup_signal (context->wakeup);
+
+ return source->source_id;
}
/**
* g_source_attach:
* @source: a #GSource
- * @context: a #GMainContext (if %NULL, the default context will be used)
+ * @context: (allow-none): a #GMainContext (if %NULL, the default context will be used)
*
* Adds a #GSource to a @context so that it will be executed within
* that context. Remove it by calling g_source_destroy().
g_return_val_if_fail (source->context == NULL, 0);
g_return_val_if_fail (!SOURCE_DESTROYED (source), 0);
+ TRACE (GLIB_MAIN_SOURCE_ATTACH (g_source_get_name (source)));
+
if (!context)
context = g_main_context_default ();
LOCK_CONTEXT (context);
- result = g_source_attach_unlocked (source, context);
-
- /* If another thread has acquired the context, wake it up since it
- * might be in poll() right now.
- */
- if (context->owner && context->owner != G_THREAD_SELF)
- g_wakeup_signal (context->wakeup);
+ result = g_source_attach_unlocked (source, context, TRUE);
UNLOCK_CONTEXT (context);
GMainContext *context,
gboolean have_lock)
{
+ TRACE (GLIB_MAIN_SOURCE_DESTROY (g_source_get_name (source)));
+
if (!have_lock)
LOCK_CONTEXT (context);
g_main_context_remove_poll_unlocked (context, tmp_list->data);
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;
+ for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
+ g_main_context_remove_poll_unlocked (context, tmp_list->data);
}
+
+ while (source->priv->child_sources)
+ g_child_source_remove_internal (source->priv->child_sources->data, context);
+
+ if (source->priv->parent_source)
+ g_child_source_remove_internal (source, context);
g_source_unref_internal (source, context, TRUE);
}
* @source: a #GSource
*
* Gets the #GMainContext with which the source is associated.
- * Calling this function on a destroyed source is an error.
+ *
+ * You can call this on a source that has been destroyed, provided
+ * that the #GMainContext it was attached to still exists (in which
+ * case it will return that #GMainContext). In particular, you can
+ * always call this function on the source returned from
+ * g_main_current_source(). But calling this function on a source
+ * whose #GMainContext has been destroyed is an error.
*
- * Return value: (transfer none): the #GMainContext with which the
+ * Return value: (transfer none) (allow-none): the #GMainContext with which the
* source is associated, or %NULL if the context has not
* yet been added to a source.
**/
GMainContext *
g_source_get_context (GSource *source)
{
- g_return_val_if_fail (!SOURCE_DESTROYED (source), NULL);
+ g_return_val_if_fail (source->context != NULL || !SOURCE_DESTROYED (source), NULL);
return source->context;
}
* @source:a #GSource
* @fd: a #GPollFD structure holding information about a file
* descriptor to watch.
- *
+ *
* Adds a file descriptor to the set of file descriptors polled for
* this source. This is usually combined with g_source_new() to add an
* event source. The event source's check function will typically test
* the @revents field in the #GPollFD struct and return %TRUE if events need
* to be processed.
+ *
+ * Using this API forces the linear scanning of event sources on each
+ * main loop iteration. Newly-written event sources should try to use
+ * g_source_add_unix_fd() instead of this API.
**/
void
g_source_add_poll (GSource *source,
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);
+ g_return_if_fail (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);
+ g_source_set_priority_unlocked (child_source, NULL, source->priority);
+ if (SOURCE_BLOCKED (source))
+ block_source (child_source);
if (context)
{
+ g_source_attach_unlocked (child_source, context, TRUE);
UNLOCK_CONTEXT (context);
- g_source_attach (child_source, context);
}
}
+static void
+g_child_source_remove_internal (GSource *child_source,
+ GMainContext *context)
+{
+ GSource *parent_source = child_source->priv->parent_source;
+
+ parent_source->priv->child_sources =
+ g_slist_remove (parent_source->priv->child_sources, child_source);
+ child_source->priv->parent_source = NULL;
+
+ g_source_destroy_internal (child_source, context, TRUE);
+ g_source_unref_internal (child_source, context, TRUE);
+}
+
/**
* g_source_remove_child_source:
* @source:a #GSource
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 (child_source->priv->parent_source == source);
g_return_if_fail (!SOURCE_DESTROYED (source));
g_return_if_fail (!SOURCE_DESTROYED (child_source));
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);
+ g_child_source_remove_internal (child_source, context);
if (context)
UNLOCK_CONTEXT (context);
* @source: the source
* @func: a callback function
* @data: the data to pass to callback function
- * @notify: a function to call when @data is no longer in use, or %NULL.
+ * @notify: (allow-none): a function to call when @data is no longer in use, or %NULL.
*
* Sets the callback function for a source. The callback for a source is
* called from the source's dispatch function.
{
GSList *tmp_list;
- source->priority = priority;
+ g_return_if_fail (source->priv->parent_source == NULL ||
+ source->priv->parent_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 place
+ * add it back after so it is sorted in the correct place
*/
- g_source_list_remove (source, source->context);
- g_source_list_add (source, source->context);
+ source_remove_from_context (source, source->context);
+ }
+
+ source->priority = priority;
+
+ if (context)
+ {
+ source_add_to_context (source, source->context);
if (!SOURCE_BLOCKED (source))
{
tmp_list = tmp_list->next;
}
+
+ for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
+ {
+ g_main_context_remove_poll_unlocked (context, tmp_list->data);
+ g_main_context_add_poll_unlocked (context, priority, tmp_list->data);
+ }
}
}
- if (source->priv && source->priv->child_sources)
+ if (source->priv->child_sources)
{
tmp_list = source->priv->child_sources;
while (tmp_list)
}
/**
+ * 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.
+ *
+ * Note that if you have a pair of sources where the ready time of one
+ * suggests that it will be delivered first but the priority for the
+ * other suggests that it would be delivered first, and the ready time
+ * for both sources is reached during the same main context iteration
+ * then the order of dispatch is undefined.
+ *
+ * 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
* @can_recurse: whether recursion is allowed for this source
{
if (!SOURCE_DESTROYED (source))
g_warning (G_STRLOC ": ref_count == 0, but source was still attached to a context!");
- g_source_list_remove (source, context);
+ source_remove_from_context (source, context);
}
if (source->source_funcs->finalize)
g_slist_free (source->poll_fds);
source->poll_fds = NULL;
- if (source->priv)
- {
- g_slice_free (GSourcePrivate, source->priv);
- source->priv = NULL;
- }
+ g_slist_free_full (source->priv->fds, g_free);
+
+ g_slice_free (GSourcePrivate, source->priv);
+ source->priv = NULL;
g_free (source);
}
/**
* g_main_context_find_source_by_id:
- * @context: a #GMainContext (if %NULL, the default context will be used)
+ * @context: (allow-none): a #GMainContext (if %NULL, the default context will be used)
* @source_id: the source ID, as returned by g_source_get_id().
*
* Finds a #GSource given a pair of context and ID.
g_main_context_find_source_by_id (GMainContext *context,
guint source_id)
{
+ GSourceIter iter;
GSource *source;
g_return_val_if_fail (source_id > 0, NULL);
LOCK_CONTEXT (context);
- source = context->source_list;
- while (source)
+ g_source_iter_init (&iter, context, FALSE);
+ while (g_source_iter_next (&iter, &source))
{
if (!SOURCE_DESTROYED (source) &&
source->source_id == source_id)
break;
- source = source->next;
}
+ g_source_iter_clear (&iter);
UNLOCK_CONTEXT (context);
/**
* g_main_context_find_source_by_funcs_user_data:
- * @context: a #GMainContext (if %NULL, the default context will be used).
+ * @context: (allow-none): a #GMainContext (if %NULL, the default context will be used).
* @funcs: the @source_funcs passed to g_source_new().
* @user_data: the user data from the callback.
*
GSourceFuncs *funcs,
gpointer user_data)
{
+ GSourceIter iter;
GSource *source;
g_return_val_if_fail (funcs != NULL, NULL);
LOCK_CONTEXT (context);
- source = context->source_list;
- while (source)
+ g_source_iter_init (&iter, context, FALSE);
+ while (g_source_iter_next (&iter, &source))
{
if (!SOURCE_DESTROYED (source) &&
source->source_funcs == funcs &&
if (callback_data == user_data)
break;
}
- source = source->next;
}
+ g_source_iter_clear (&iter);
UNLOCK_CONTEXT (context);
g_main_context_find_source_by_user_data (GMainContext *context,
gpointer user_data)
{
+ GSourceIter iter;
GSource *source;
if (context == NULL)
LOCK_CONTEXT (context);
- source = context->source_list;
- while (source)
+ g_source_iter_init (&iter, context, FALSE);
+ while (g_source_iter_next (&iter, &source))
{
if (!SOURCE_DESTROYED (source) &&
source->callback_funcs)
if (callback_data == user_data)
break;
}
- source = source->next;
}
+ g_source_iter_clear (&iter);
UNLOCK_CONTEXT (context);
/**
* g_source_remove:
* @tag: the ID of the source to remove.
- *
- * Removes the source with the given id from the default main context.
- * The id of
- * a #GSource is given by g_source_get_id(), or will be returned by the
- * functions g_source_attach(), g_idle_add(), g_idle_add_full(),
- * g_timeout_add(), g_timeout_add_full(), g_child_watch_add(),
- * g_child_watch_add_full(), g_io_add_watch(), and g_io_add_watch_full().
+ *
+ * Removes the source with the given id from the default main context.
+ *
+ * The id of a #GSource is given by g_source_get_id(), or will be
+ * returned by the functions g_source_attach(), g_idle_add(),
+ * g_idle_add_full(), g_timeout_add(), g_timeout_add_full(),
+ * g_child_watch_add(), g_child_watch_add_full(), g_io_add_watch(), and
+ * g_io_add_watch_full().
*
* See also g_source_destroy(). You must use g_source_destroy() for sources
* added to a non-default main context.
*
- * Return value: %TRUE if the source was found and removed.
+ * It is a programmer error to attempt to remove a non-existent source.
+ *
+ * Return value: For historical reasons, this function always returns %TRUE
**/
gboolean
g_source_remove (guint tag)
{
GSource *source;
-
+
g_return_val_if_fail (tag > 0, FALSE);
source = g_main_context_find_source_by_id (NULL, tag);
if (source)
g_source_destroy (source);
+ else
+ g_critical ("Source ID %u was not found when attempting to remove it", tag);
return source != NULL;
}
{
GSource *source;
- g_return_val_if_fail (funcs != NULL, FALSE);
+ g_return_val_if_fail (funcs != NULL, FALSE);
+
+ source = g_main_context_find_source_by_funcs_user_data (NULL, funcs, user_data);
+ if (source)
+ {
+ g_source_destroy (source);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+#ifdef G_OS_UNIX
+/**
+ * g_source_add_unix_fd:
+ * @source: a #GSource
+ * @fd: the fd to monitor
+ * @events: an event mask
+ *
+ * Monitors @fd for the IO events in @events.
+ *
+ * The tag returned by this function can be used to remove or modify the
+ * monitoring of the fd using g_source_remove_unix_fd() or
+ * g_source_modify_unix_fd().
+ *
+ * It is not necessary to remove the fd before destroying the source; it
+ * will be cleaned up automatically.
+ *
+ * As the name suggests, this function is not available on Windows.
+ *
+ * Returns: an opaque tag
+ *
+ * Since: 2.36
+ **/
+gpointer
+g_source_add_unix_fd (GSource *source,
+ gint fd,
+ GIOCondition events)
+{
+ GMainContext *context;
+ GPollFD *poll_fd;
+
+ g_return_val_if_fail (source != NULL, NULL);
+ g_return_val_if_fail (!SOURCE_DESTROYED (source), NULL);
+
+ poll_fd = g_new (GPollFD, 1);
+ poll_fd->fd = fd;
+ poll_fd->events = events;
+ poll_fd->revents = 0;
+
+ context = source->context;
+
+ if (context)
+ LOCK_CONTEXT (context);
+
+ source->priv->fds = g_slist_prepend (source->priv->fds, poll_fd);
+
+ if (context)
+ {
+ if (!SOURCE_BLOCKED (source))
+ g_main_context_add_poll_unlocked (context, source->priority, poll_fd);
+ UNLOCK_CONTEXT (context);
+ }
+
+ return poll_fd;
+}
+
+/**
+ * g_source_modify_unix_fd:
+ * @source: a #GSource
+ * @tag: the tag from g_source_add_unix_fd()
+ * @new_events: the new event mask to watch
+ *
+ * Updates the event mask to watch for the fd identified by @tag.
+ *
+ * @tag is the tag returned from g_source_add_unix_fd().
+ *
+ * If you want to remove a fd, don't set its event mask to zero.
+ * Instead, call g_source_remove_unix_fd().
+ *
+ * As the name suggests, this function is not available on Windows.
+ *
+ * Since: 2.36
+ **/
+void
+g_source_modify_unix_fd (GSource *source,
+ gpointer tag,
+ GIOCondition new_events)
+{
+ GMainContext *context;
+ GPollFD *poll_fd;
+
+ g_return_if_fail (source != NULL);
+ g_return_if_fail (g_slist_find (source->priv->fds, tag));
+
+ context = source->context;
+ poll_fd = tag;
+
+ poll_fd->events = new_events;
+
+ if (context)
+ g_main_context_wakeup (context);
+}
+
+/**
+ * g_source_remove_unix_fd:
+ * @source: a #GSource
+ * @tag: the tag from g_source_add_unix_fd()
+ *
+ * Reverses the effect of a previous call to g_source_add_unix_fd().
+ *
+ * You only need to call this if you want to remove an fd from being
+ * watched while keeping the same source around. In the normal case you
+ * will just want to destroy the source.
+ *
+ * As the name suggests, this function is not available on Windows.
+ *
+ * Since: 2.36
+ **/
+void
+g_source_remove_unix_fd (GSource *source,
+ gpointer tag)
+{
+ GMainContext *context;
+ GPollFD *poll_fd;
+
+ g_return_if_fail (source != NULL);
+ g_return_if_fail (g_slist_find (source->priv->fds, tag));
+
+ context = source->context;
+ poll_fd = tag;
+
+ if (context)
+ LOCK_CONTEXT (context);
+
+ source->priv->fds = g_slist_remove (source->priv->fds, poll_fd);
- source = g_main_context_find_source_by_funcs_user_data (NULL, funcs, user_data);
- if (source)
+ if (context)
{
- g_source_destroy (source);
- return TRUE;
+ if (!SOURCE_BLOCKED (source))
+ g_main_context_remove_poll_unlocked (context, poll_fd);
+
+ UNLOCK_CONTEXT (context);
}
- else
- return FALSE;
+
+ g_free (poll_fd);
+}
+
+/**
+ * g_source_query_unix_fd:
+ * @source: a #GSource
+ * @tag: the tag from g_source_add_unix_fd()
+ *
+ * Queries the events reported for the fd corresponding to @tag on
+ * @source during the last poll.
+ *
+ * The return value of this function is only defined when the function
+ * is called from the check or dispatch functions for @source.
+ *
+ * As the name suggests, this function is not available on Windows.
+ *
+ * Returns: the conditions reported on the fd
+ *
+ * Since: 2.36
+ **/
+GIOCondition
+g_source_query_unix_fd (GSource *source,
+ gpointer tag)
+{
+ GPollFD *poll_fd;
+
+ g_return_val_if_fail (source != NULL, 0);
+ g_return_val_if_fail (g_slist_find (source->priv->fds, tag), 0);
+
+ poll_fd = tag;
+
+ return poll_fd->revents;
}
+#endif /* G_OS_UNIX */
/**
* g_get_current_time:
return (((gint64) tv.tv_sec) * 1000000) + tv.tv_usec;
}
+#ifdef G_OS_WIN32
+static ULONGLONG (*g_GetTickCount64) (void) = NULL;
+static guint32 g_win32_tick_epoch = 0;
+
+void
+g_clock_win32_init (void)
+{
+ HMODULE kernel32;
+
+ g_GetTickCount64 = NULL;
+ kernel32 = GetModuleHandle ("KERNEL32.DLL");
+ if (kernel32 != NULL)
+ g_GetTickCount64 = (void *) GetProcAddress (kernel32, "GetTickCount64");
+ g_win32_tick_epoch = ((guint32)GetTickCount()) >> 31;
+}
+#endif
+
/**
* g_get_monotonic_time:
*
* Queries the system monotonic time, if available.
*
- * On POSIX systems with clock_gettime() and %CLOCK_MONOTONIC this call
+ * On POSIX systems with clock_gettime() and <literal>CLOCK_MONOTONIC</literal> this call
* is a very shallow wrapper for that. Otherwise, we make a best effort
* that probably involves returning the wall clock time (with at least
* microsecond accuracy, subject to the limitations of the OS kernel).
*
- * It's important to note that POSIX %CLOCK_MONOTONIC does not count
- * time spent while the machine is suspended.
+ * It's important to note that POSIX <literal>CLOCK_MONOTONIC</literal> does
+ * not count time spent while the machine is suspended.
*
* On Windows, "limitations of the OS kernel" is a rather substantial
* statement. Depending on the configuration of the system, the wall
* clock time is updated as infrequently as 64 times a second (which
- * is approximately every 16ms).
+ * is approximately every 16ms). Also, on XP (but not on Vista or later)
+ * the monotonic clock is locally monotonic, but may differ in exact
+ * value between processes due to timer wrap handling.
*
* Returns: the monotonic time, in microseconds
*
return (((gint64) ts.tv_sec) * 1000000) + (ts.tv_nsec / 1000);
-#else /* !HAVE_CLOCK_GETTIME */
+#elif defined (G_OS_WIN32)
+ guint64 ticks;
+ guint32 ticks32;
- /* It may look like we are discarding accuracy on Windows (since its
- * current time is expressed in 100s of nanoseconds) but according to
- * many sources, the time is only updated 64 times per second, so
- * microsecond accuracy is more than enough.
+ /* There are four sources for the monotonic time on Windows:
+ *
+ * Three are based on a (1 msec accuracy, but only read periodically) clock chip:
+ * - GetTickCount (GTC)
+ * 32bit msec counter, updated each ~15msec, wraps in ~50 days
+ * - GetTickCount64 (GTC64)
+ * Same as GetTickCount, but extended to 64bit, so no wrap
+ * Only available in Vista or later
+ * - timeGetTime (TGT)
+ * similar to GetTickCount by default: 15msec, 50 day wrap.
+ * available in winmm.dll (thus known as the multimedia timers)
+ * However apps can raise the system timer clock frequency using timeBeginPeriod()
+ * increasing the accuracy up to 1 msec, at a cost in general system performance
+ * and battery use.
+ *
+ * One is based on high precision clocks:
+ * - QueryPrecisionCounter (QPC)
+ * This has much higher accuracy, but is not guaranteed monotonic, and
+ * has lots of complications like clock jumps and different times on different
+ * CPUs. It also has lower long term accuracy (i.e. it will drift compared to
+ * the low precision clocks.
+ *
+ * Additionally, the precision available in the timer-based wakeup such as
+ * MsgWaitForMultipleObjectsEx (which is what the mainloop is based on) is based
+ * on the TGT resolution, so by default it is ~15msec, but can be increased by apps.
+ *
+ * The QPC timer has too many issues to be used as is. The only way it could be used
+ * is to use it to interpolate the lower precision clocks. Firefox does something like
+ * this:
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=363258
+ *
+ * However this seems quite complicated, so we're not doing this right now.
+ *
+ * The approach we take instead is to use the TGT timer, extending it to 64bit
+ * either by using the GTC64 value, or if that is not available, a process local
+ * time epoch that we increment when we detect a timer wrap (assumes that we read
+ * the time at least once every 50 days).
+ *
+ * This means that:
+ * - We have a globally consistent monotonic clock on Vista and later
+ * - We have a locally monotonic clock on XP
+ * - Apps that need higher precision in timeouts and clock reads can call
+ * timeBeginPeriod() to increase it as much as they want
*/
+
+ if (g_GetTickCount64 != NULL)
+ {
+ guint32 ticks_as_32bit;
+
+ ticks = g_GetTickCount64 ();
+ ticks32 = timeGetTime();
+
+ /* GTC64 and TGT are sampled at different times, however they
+ * have the same base and source (msecs since system boot).
+ * They can differ by as much as -16 to +16 msecs.
+ * We can't just inject the low bits into the 64bit counter
+ * as one of the counters can have wrapped in 32bit space and
+ * the other not. Instead we calculate the signed difference
+ * in 32bit space and apply that difference to the 64bit counter.
+ */
+ ticks_as_32bit = (guint32)ticks;
+
+ /* We could do some 2's complement hack, but we play it safe */
+ if (ticks32 - ticks_as_32bit <= G_MAXINT32)
+ ticks += ticks32 - ticks_as_32bit;
+ else
+ ticks -= ticks_as_32bit - ticks32;
+ }
+ else
+ {
+ guint32 epoch;
+
+ epoch = g_atomic_int_get (&g_win32_tick_epoch);
+
+ /* Must read ticks after the epoch. Then we're guaranteed
+ * that the ticks value we read is higher or equal to any
+ * previous ones that lead to the writing of the epoch.
+ */
+ ticks32 = timeGetTime();
+
+ /* We store the MSB of the current time as the LSB
+ * of the epoch. Comparing these bits lets us detect when
+ * the 32bit counter has wrapped so we can increase the
+ * epoch.
+ *
+ * This will work as long as this function is called at
+ * least once every ~24 days, which is half the wrap time
+ * of a 32bit msec counter. I think this is pretty likely.
+ *
+ * Note that g_win32_tick_epoch is a process local state,
+ * so the monotonic clock will not be the same between
+ * processes.
+ */
+ if ((ticks32 >> 31) != (epoch & 1))
+ {
+ epoch++;
+ g_atomic_int_set (&g_win32_tick_epoch, epoch);
+ }
+
+
+ ticks = (guint64)ticks32 | ((guint64)epoch) << 31;
+ }
+
+ return ticks * 1000;
+
+#else /* !HAVE_CLOCK_GETTIME && ! G_OS_WIN32*/
+
GTimeVal tv;
g_get_current_time (&tv);
* many things that the user could do. Instead, you can use the
* following techniques:
*
- * <orderedlist>
- * <listitem>
- * <para>
- * Use gtk_widget_set_sensitive() or modal dialogs to prevent
- * the user from interacting with elements while the main
- * loop is recursing.
- * </para>
- * </listitem>
- * <listitem>
- * <para>
- * Avoid main loop recursion in situations where you can't handle
- * arbitrary callbacks. Instead, structure your code so that you
- * simply return to the main loop and then get called again when
- * there is more work to do.
- * </para>
- * </listitem>
- * </orderedlist>
+ * 1. Use gtk_widget_set_sensitive() or modal dialogs to prevent
+ * the user from interacting with elements while the main
+ * loop is recursing.
+ *
+ * 2. Avoid main loop recursion in situations where you can't handle
+ * arbitrary callbacks. Instead, structure your code so that you
+ * simply return to the main loop and then get called again when
+ * there is more work to do.
*
* Return value: The main loop recursion level in the current thread
- **/
+ */
int
g_main_depth (void)
{
g_main_current_source (void)
{
GMainDispatch *dispatch = get_dispatch ();
- return dispatch->dispatching_sources ? dispatch->dispatching_sources->data : NULL;
+ return dispatch->source;
}
/**
* {
* SomeWidget *self = data;
*
- * GDK_THREADS_ENTER (<!-- -->);
- * /<!-- -->* do stuff with self *<!-- -->/
+ * GDK_THREADS_ENTER ();
+ * /* do stuff with self */
* self->idle_id = 0;
- * GDK_THREADS_LEAVE (<!-- -->);
+ * GDK_THREADS_LEAVE ();
*
- * return FALSE;
+ * return G_SOURCE_REMOVE;
* }
*
* static void
* GDK_THREADS_ENTER ();
* if (!g_source_is_destroyed (g_main_current_source ()))
* {
- * /<!-- -->* do stuff with self *<!-- -->/
+ * /* do stuff with self */
* }
* GDK_THREADS_LEAVE ();
*
g_return_if_fail (!SOURCE_BLOCKED (source));
- tmp_list = source->poll_fds;
- while (tmp_list)
+ source->flags |= G_SOURCE_BLOCKED;
+
+ if (source->context)
{
- g_main_context_remove_poll_unlocked (source->context, tmp_list->data);
- tmp_list = tmp_list->next;
+ tmp_list = source->poll_fds;
+ while (tmp_list)
+ {
+ g_main_context_remove_poll_unlocked (source->context, tmp_list->data);
+ tmp_list = tmp_list->next;
+ }
+
+ for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
+ g_main_context_remove_poll_unlocked (source->context, tmp_list->data);
+ }
+
+ if (source->priv && source->priv->child_sources)
+ {
+ tmp_list = source->priv->child_sources;
+ while (tmp_list)
+ {
+ block_source (tmp_list->data);
+ tmp_list = tmp_list->next;
+ }
}
}
unblock_source (GSource *source)
{
GSList *tmp_list;
-
- g_return_if_fail (!SOURCE_BLOCKED (source)); /* Source already unblocked */
+
+ g_return_if_fail (SOURCE_BLOCKED (source)); /* Source already unblocked */
g_return_if_fail (!SOURCE_DESTROYED (source));
+ source->flags &= ~G_SOURCE_BLOCKED;
+
tmp_list = source->poll_fds;
while (tmp_list)
{
g_main_context_add_poll_unlocked (source->context, source->priority, tmp_list->data);
tmp_list = tmp_list->next;
}
+
+ for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
+ g_main_context_add_poll_unlocked (source->context, source->priority, tmp_list->data);
+
+ if (source->priv && source->priv->child_sources)
+ {
+ tmp_list = source->priv->child_sources;
+ while (tmp_list)
+ {
+ unblock_source (tmp_list->data);
+ tmp_list = tmp_list->next;
+ }
+ }
}
/* HOLDS: context's lock */
gboolean (*dispatch) (GSource *,
GSourceFunc,
gpointer);
- GSList current_source_link;
+ GSource *prev_source;
dispatch = source->source_funcs->dispatch;
cb_funcs = source->callback_funcs;
UNLOCK_CONTEXT (context);
- current->depth++;
- /* The on-stack allocation of the GSList is unconventional, but
- * we know that the lifetime of the link is bounded to this
- * function as the link is kept in a thread specific list and
- * not manipulated outside of this function and its descendants.
- * Avoiding the overhead of a g_slist_alloc() is useful as many
- * applications do little more than dispatch events.
- *
- * This is a performance hack - do not revert to g_slist_prepend()!
- */
- current_source_link.data = source;
- current_source_link.next = current->dispatching_sources;
- current->dispatching_sources = ¤t_source_link;
- need_destroy = ! dispatch (source,
- callback,
- user_data);
- g_assert (current->dispatching_sources == ¤t_source_link);
- current->dispatching_sources = current_source_link.next;
- current->depth--;
-
+ /* These operations are safe because 'current' is thread-local
+ * and not modified from anywhere but this function.
+ */
+ prev_source = current->source;
+ current->source = source;
+ current->depth++;
+
+ TRACE( GLIB_MAIN_BEFORE_DISPATCH (g_source_get_name (source)));
+ need_destroy = !(* dispatch) (source, callback, user_data);
+ TRACE( GLIB_MAIN_AFTER_DISPATCH (g_source_get_name (source)));
+
+ current->source = prev_source;
+ current->depth--;
+
if (cb_funcs)
cb_funcs->unref (cb_data);
if (!was_in_call)
source->flags &= ~G_HOOK_FLAG_IN_CALL;
- if ((source->flags & G_SOURCE_CAN_RECURSE) == 0 &&
- !SOURCE_DESTROYED (source))
+ if (SOURCE_BLOCKED (source) && !SOURCE_DESTROYED (source))
unblock_source (source);
/* Note: this depends on the fact that we can't switch
g_ptr_array_set_size (context->pending_dispatches, 0);
}
-/* Holds context's lock */
-static inline GSource *
-next_valid_source (GMainContext *context,
- GSource *source)
-{
- GSource *new_source = source ? source->next : context->source_list;
-
- while (new_source)
- {
- if (!SOURCE_DESTROYED (new_source))
- {
- new_source->ref_count++;
- break;
- }
-
- new_source = new_source->next;
- }
-
- if (source)
- SOURCE_UNREF (source, context);
-
- return new_source;
-}
-
/**
* g_main_context_acquire:
* @context: a #GMainContext
gint n_ready = 0;
gint current_priority = G_MAXINT;
GSource *source;
+ GSourceIter iter;
if (context == NULL)
context = g_main_context_default ();
context->timeout = -1;
- source = next_valid_source (context, NULL);
- while (source)
+ g_source_iter_init (&iter, context, TRUE);
+ while (g_source_iter_next (&iter, &source))
{
gint source_timeout = -1;
+ if (SOURCE_DESTROYED (source) || SOURCE_BLOCKED (source))
+ continue;
if ((n_ready > 0) && (source->priority > current_priority))
- {
- SOURCE_UNREF (source, context);
- break;
- }
- if (SOURCE_BLOCKED (source))
- goto next;
+ break;
if (!(source->flags & G_SOURCE_READY))
{
gboolean result;
- gboolean (*prepare) (GSource *source,
- gint *timeout);
+ gboolean (* prepare) (GSource *source,
+ gint *timeout);
- prepare = source->source_funcs->prepare;
- context->in_check_or_prepare++;
- UNLOCK_CONTEXT (context);
+ prepare = source->source_funcs->prepare;
- result = (*prepare) (source, &source_timeout);
+ if (prepare)
+ {
+ context->in_check_or_prepare++;
+ UNLOCK_CONTEXT (context);
- LOCK_CONTEXT (context);
- context->in_check_or_prepare--;
+ result = (* prepare) (source, &source_timeout);
+
+ LOCK_CONTEXT (context);
+ context->in_check_or_prepare--;
+ }
+ else
+ {
+ source_timeout = -1;
+ 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)
{
while (ready_source)
{
ready_source->flags |= G_SOURCE_READY;
- ready_source = ready_source->priv ? ready_source->priv->parent_source : NULL;
+ ready_source = ready_source->priv->parent_source;
}
}
}
else
context->timeout = MIN (context->timeout, source_timeout);
}
-
- next:
- source = next_valid_source (context, source);
}
+ g_source_iter_clear (&iter);
UNLOCK_CONTEXT (context);
gint n_fds)
{
GSource *source;
+ GSourceIter iter;
GPollRec *pollrec;
gint n_ready = 0;
gint i;
return FALSE;
}
- if (context->wake_up_rec.events)
+ if (context->wake_up_rec.revents)
g_wakeup_acknowledge (context->wakeup);
/* If the set of poll file descriptors changed, bail out
i++;
}
- source = next_valid_source (context, NULL);
- while (source)
+ g_source_iter_init (&iter, context, TRUE);
+ while (g_source_iter_next (&iter, &source))
{
+ if (SOURCE_DESTROYED (source) || SOURCE_BLOCKED (source))
+ continue;
if ((n_ready > 0) && (source->priority > max_priority))
- {
- SOURCE_UNREF (source, context);
- break;
- }
- if (SOURCE_BLOCKED (source))
- goto next;
+ break;
if (!(source->flags & G_SOURCE_READY))
{
- gboolean result;
- gboolean (*check) (GSource *source);
+ gboolean result;
+ gboolean (* check) (GSource *source);
+
+ check = source->source_funcs->check;
+
+ if (check)
+ {
+ /* If the check function is set, call it. */
+ context->in_check_or_prepare++;
+ UNLOCK_CONTEXT (context);
+
+ result = (* check) (source);
+
+ LOCK_CONTEXT (context);
+ context->in_check_or_prepare--;
+ }
+ else
+ result = FALSE;
+
+ if (result == FALSE)
+ {
+ GSList *tmp_list;
+
+ /* If not already explicitly flagged ready by ->check()
+ * (or if we have no check) then we can still be ready if
+ * any of our fds poll as ready.
+ */
+ for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
+ {
+ GPollFD *pollfd = tmp_list->data;
+
+ if (pollfd->revents)
+ {
+ result = TRUE;
+ break;
+ }
+ }
+ }
+
+ 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;
+ }
- check = source->source_funcs->check;
-
- context->in_check_or_prepare++;
- UNLOCK_CONTEXT (context);
-
- result = (*check) (source);
-
- LOCK_CONTEXT (context);
- context->in_check_or_prepare--;
-
if (result)
{
GSource *ready_source = source;
while (ready_source)
{
ready_source->flags |= G_SOURCE_READY;
- ready_source = ready_source->priv ? ready_source->priv->parent_source : NULL;
+ ready_source = ready_source->priv->parent_source;
}
}
}
*/
max_priority = source->priority;
}
-
- next:
- source = next_valid_source (context, source);
}
+ g_source_iter_clear (&iter);
UNLOCK_CONTEXT (context);
if (!block)
timeout = 0;
- g_assert (!g_main_context_fork_detected);
g_main_context_poll (context, timeout, max_priority, fds, nfds);
some_ready = g_main_context_check (context, max_priority, fds, nfds);
/**
* g_main_context_pending:
- * @context: a #GMainContext (if %NULL, the default context will be used)
+ * @context: (allow-none): a #GMainContext (if %NULL, the default context will be used)
*
* Checks if any sources have pending events for the given context.
*
/**
* g_main_context_iteration:
- * @context: a #GMainContext (if %NULL, the default context will be used)
+ * @context: (allow-none): a #GMainContext (if %NULL, the default context will be used)
* @may_block: whether the call may block.
- *
+ *
* Runs a single iteration for the given main loop. This involves
* checking to see if any event sources are ready to be processed,
* then if no events sources are ready and @may_block is %TRUE, waiting
* for a source to become ready, then dispatching the highest priority
- * events sources that are ready. Otherwise, if @may_block is %FALSE
- * sources are not waited to become ready, only those highest priority
- * events sources will be dispatched (if any), that are ready at this
+ * events sources that are ready. Otherwise, if @may_block is %FALSE
+ * sources are not waited to become ready, only those highest priority
+ * events sources will be dispatched (if any), that are ready at this
* given moment without further waiting.
*
- * Note that even when @may_block is %TRUE, it is still possible for
- * g_main_context_iteration() to return %FALSE, since the the wait may
+ * Note that even when @may_block is %TRUE, it is still possible for
+ * g_main_context_iteration() to return %FALSE, since the wait may
* be interrupted for other reasons than an event source becoming ready.
- *
+ *
* Return value: %TRUE if events were dispatched.
**/
gboolean
/**
* g_main_context_add_poll:
- * @context: a #GMainContext (or %NULL for the default context)
+ * @context: (allow-none): a #GMainContext (or %NULL for the default context)
* @fd: a #GPollFD structure holding information about a file
* descriptor to watch.
* @priority: the priority for this file descriptor which should be
* the same as the priority used for g_source_attach() to ensure that the
* file descriptor is polled whenever the results may be needed.
- *
+ *
* Adds a file descriptor to the set of file descriptors polled for
* this context. This will very seldom be used directly. Instead
- * a typical event source will use g_source_add_poll() instead.
+ * a typical event source will use g_source_add_unix_fd() instead.
**/
void
g_main_context_add_poll (GMainContext *context,
* g_main_context_wakeup:
* @context: a #GMainContext
*
- * If @context is currently waiting in a poll(), interrupt
- * the poll(), and continue the iteration process.
+ * If @context is currently blocking in g_main_context_iteration()
+ * waiting for a source to become ready, cause it to stop blocking
+ * and return. Otherwise, cause the next invocation of
+ * g_main_context_iteration() to return without blocking.
+ *
+ * This API is useful for low-level control over #GMainContext; for
+ * example, integrating it with main loop implementations such as
+ * #GMainLoop.
+ *
+ * Another related use for this function is when implementing a main
+ * loop with a termination condition, computed from multiple threads:
+ *
+ * |[
+ * #define NUM_TASKS 10
+ * static volatile gint tasks_remaining = NUM_TASKS;
+ * ...
+ *
+ * while (g_atomic_int_get (&tasks_remaining) != 0)
+ * g_main_context_iteration (NULL, TRUE);
+ * ]|
+ *
+ * Then in a thread:
+ * |[
+ * perform_work();
+ *
+ * if (g_atomic_int_dec_and_test (&tasks_remaining))
+ * g_main_context_wakeup (NULL);
+ * ]|
**/
void
g_main_context_wakeup (GMainContext *context)
g_timeout_set_expiration (GTimeoutSource *timeout_source,
gint64 current_time)
{
- timeout_source->expiration = current_time +
- (guint64) timeout_source->interval * 1000;
+ gint64 expiration;
+
+ expiration = current_time + (guint64) timeout_source->interval * 1000;
if (timeout_source->seconds)
{
* always only *increase* the expiration time by adding a full
* second in the case that the microsecond portion decreases.
*/
- timeout_source->expiration -= timer_perturb;
+ expiration -= timer_perturb;
- remainder = timeout_source->expiration % 1000000;
+ remainder = expiration % 1000000;
if (remainder >= 1000000/4)
- timeout_source->expiration += 1000000;
-
- timeout_source->expiration -= remainder;
- timeout_source->expiration += timer_perturb;
- }
-}
-
-static gboolean
-g_timeout_prepare (GSource *source,
- gint *timeout)
-{
- GTimeoutSource *timeout_source = (GTimeoutSource *) source;
- gint64 now = g_source_get_time (source);
+ expiration += 1000000;
- if (now < timeout_source->expiration)
- {
- /* Round up to ensure that we don't try again too early */
- *timeout = (timeout_source->expiration - now + 999) / 1000;
- return FALSE;
+ expiration -= remainder;
+ expiration += timer_perturb;
}
- *timeout = 0;
- return TRUE;
-}
-
-static gboolean
-g_timeout_check (GSource *source)
-{
- GTimeoutSource *timeout_source = (GTimeoutSource *) source;
- gint64 now = g_source_get_time (source);
-
- return timeout_source->expiration <= now;
+ g_source_set_ready_time ((GSource *) timeout_source, expiration);
}
static gboolean
* (1/1000ths of a second)
* @function: function to call
* @data: data to pass to @function
- * @notify: function to call when the timeout is removed, or %NULL
+ * @notify: (allow-none): function to call when the timeout is removed, or %NULL
*
* Sets a function to be called at regular intervals, with the given
* priority. The function is called repeatedly until it returns
* @interval: the time between calls to the function, in seconds
* @function: function to call
* @data: data to pass to @function
- * @notify: function to call when the timeout is removed, or %NULL
+ * @notify: (allow-none): function to call when the timeout is removed, or %NULL
*
* Sets a function to be called at regular intervals, with @priority.
* The function is called repeatedly until it returns %FALSE, at which
}
static void
-dispatch_unix_signals (void)
+dispatch_unix_signals_unlocked (void)
{
+ gboolean pending[NSIG];
GSList *node;
+ gint i;
/* clear this first incase another one arrives while we're processing */
any_unix_signal_pending = FALSE;
- G_LOCK(unix_signal_lock);
+ /* We atomically test/clear the bit from the global array in case
+ * other signals arrive while we are dispatching.
+ *
+ * We then can safely use our own array below without worrying about
+ * races.
+ */
+ for (i = 0; i < NSIG; i++)
+ {
+ /* Be very careful with (the volatile) unix_signal_pending.
+ *
+ * We must ensure that it's not possible that we clear it without
+ * handling the signal. We therefore must ensure that our pending
+ * array has a field set (ie: we will do something about the
+ * signal) before we clear the item in unix_signal_pending.
+ *
+ * Note specifically: we must check _our_ array.
+ */
+ pending[i] = unix_signal_pending[i];
+ if (pending[i])
+ unix_signal_pending[i] = FALSE;
+ }
/* handle GChildWatchSource instances */
- if (unix_signal_pending[SIGCHLD])
+ if (pending[SIGCHLD])
{
- unix_signal_pending[SIGCHLD] = FALSE;
-
/* The only way we can do this is to scan all of the children.
*
* The docs promise that we will not reap children that we are not
if (!source->child_exited)
{
- if (waitpid (source->pid, &source->child_status, WNOHANG) > 0)
+ pid_t pid;
+ do
{
- source->child_exited = TRUE;
-
- wake_source ((GSource *) source);
+ pid = waitpid (source->pid, &source->child_status, WNOHANG);
+ if (pid > 0)
+ {
+ source->child_exited = TRUE;
+ wake_source ((GSource *) source);
+ }
+ else if (pid == -1 && errno == ECHILD)
+ {
+ g_warning ("GChildWatchSource: Exit status of a child process was requested but ECHILD was received by waitpid(). Most likely the process is ignoring SIGCHLD, or some other thread is invoking waitpid() with a nonpositive first argument; either behavior can break applications that use g_child_watch_add()/g_spawn_sync() either directly or indirectly.");
+ source->child_exited = TRUE;
+ source->child_status = 0;
+ wake_source ((GSource *) source);
+ }
}
+ while (pid == -1 && errno == EINTR);
}
}
}
if (!source->pending)
{
- if (unix_signal_pending[source->signum])
+ if (pending[source->signum])
{
- unix_signal_pending[source->signum] = FALSE;
source->pending = TRUE;
wake_source ((GSource *) source);
}
}
+}
+
+static void
+dispatch_unix_signals (void)
+{
+ G_LOCK(unix_signal_lock);
+ dispatch_unix_signals_unlocked ();
G_UNLOCK(unix_signal_lock);
}
gpointer user_data)
{
GUnixSignalWatchSource *unix_signal_source;
+ gboolean again;
unix_signal_source = (GUnixSignalWatchSource *) source;
return FALSE;
}
- (callback) (user_data);
+ again = (callback) (user_data);
unix_signal_source->pending = FALSE;
- return TRUE;
+ return again;
}
static void
-ensure_unix_signal_handler_installed_unlocked (int signum)
+ref_unix_signal_handler_unlocked (int signum)
{
- static sigset_t installed_signal_mask;
- static gboolean initialized;
- struct sigaction action;
-
- if (!initialized)
+ /* Ensure we have the worker context */
+ g_get_worker_context ();
+ unix_signal_refcount[signum]++;
+ if (unix_signal_refcount[signum] == 1)
{
- sigemptyset (&installed_signal_mask);
- g_get_worker_context ();
- initialized = TRUE;
+ struct sigaction action;
+ action.sa_handler = g_unix_signal_handler;
+ sigemptyset (&action.sa_mask);
+#ifdef SA_RESTART
+ action.sa_flags = SA_RESTART | SA_NOCLDSTOP;
+#else
+ action.sa_flags = SA_NOCLDSTOP;
+#endif
+ sigaction (signum, &action, NULL);
}
+}
- if (sigismember (&installed_signal_mask, signum))
- return;
-
- sigaddset (&installed_signal_mask, signum);
-
- action.sa_handler = g_unix_signal_handler;
- sigemptyset (&action.sa_mask);
- action.sa_flags = SA_RESTART | SA_NOCLDSTOP;
- sigaction (signum, &action, NULL);
+static void
+unref_unix_signal_handler_unlocked (int signum)
+{
+ unix_signal_refcount[signum]--;
+ if (unix_signal_refcount[signum] == 0)
+ {
+ struct sigaction action;
+ memset (&action, 0, sizeof (action));
+ action.sa_handler = SIG_DFL;
+ sigemptyset (&action.sa_mask);
+ sigaction (signum, &action, NULL);
+ }
}
GSource *
unix_signal_source->pending = FALSE;
G_LOCK (unix_signal_lock);
- ensure_unix_signal_handler_installed_unlocked (signum);
+ ref_unix_signal_handler_unlocked (signum);
unix_signal_watches = g_slist_prepend (unix_signal_watches, unix_signal_source);
- if (unix_signal_pending[signum])
- unix_signal_source->pending = TRUE;
- unix_signal_pending[signum] = FALSE;
+ dispatch_unix_signals_unlocked ();
G_UNLOCK (unix_signal_lock);
return source;
static void
g_unix_signal_watch_finalize (GSource *source)
{
+ GUnixSignalWatchSource *unix_signal_source;
+
+ unix_signal_source = (GUnixSignalWatchSource *) source;
+
G_LOCK (unix_signal_lock);
+ unref_unix_signal_handler_unlocked (unix_signal_source->signum);
unix_signal_watches = g_slist_remove (unix_signal_watches, source);
G_UNLOCK (unix_signal_lock);
}
{
G_LOCK (unix_signal_lock);
unix_child_watches = g_slist_remove (unix_child_watches, source);
+ unref_unix_signal_handler_unlocked (SIGCHLD);
G_UNLOCK (unix_signal_lock);
}
* source is still active. Typically, you will want to call
* g_spawn_close_pid() in the callback function for the source.
*
- * Note further that using g_child_watch_source_new() is not
- * compatible with calling <literal>waitpid(-1)</literal> in
- * the application. Calling waitpid() for individual pids will
- * still work fine.
+ * Note further that using g_child_watch_source_new() is not
+ * compatible with calling <literal>waitpid</literal> with a
+ * nonpositive first argument in the application. Calling waitpid()
+ * for individual pids will still work fine.
*
* Return value: the newly-created child watch source
*
g_source_add_poll (source, &child_watch_source->poll);
#else /* G_OS_WIN32 */
G_LOCK (unix_signal_lock);
- ensure_unix_signal_handler_installed_unlocked (SIGCHLD);
+ ref_unix_signal_handler_unlocked (SIGCHLD);
unix_child_watches = g_slist_prepend (unix_child_watches, child_watch_source);
if (waitpid (pid, &child_watch_source->child_status, WNOHANG) > 0)
child_watch_source->child_exited = TRUE;
* Windows a handle for a process (which doesn't have to be a child).
* @function: function to call
* @data: data to pass to @function
- * @notify: function to call when the idle is removed, or %NULL
+ * @notify: (allow-none): function to call when the idle is removed, or %NULL
*
* Sets a function to be called when the child indicated by @pid
* exits, at the priority @priority.
* If you obtain @pid from g_spawn_async() or g_spawn_async_with_pipes()
* you will need to pass #G_SPAWN_DO_NOT_REAP_CHILD as flag to
* the spawn function for the child watching to work.
+ *
+ * In many programs, you will want to call g_spawn_check_exit_status()
+ * in the callback to determine whether or not the child exited
+ * successfully.
*
- * Note that on platforms where #GPid must be explicitly closed
- * (see g_spawn_close_pid()) @pid must not be closed while the
- * source is still active. Typically, you will want to call
- * g_spawn_close_pid() in the callback function for the source.
+ * Also, note that on platforms where #GPid must be explicitly closed
+ * (see g_spawn_close_pid()) @pid must not be closed while the source
+ * is still active. Typically, you should invoke g_spawn_close_pid()
+ * in the callback function for the source.
*
* GLib supports only a single callback per process id.
*
* range between #G_PRIORITY_DEFAULT_IDLE and #G_PRIORITY_HIGH_IDLE.
* @function: function to call
* @data: data to pass to @function
- * @notify: function to call when the idle is removed, or %NULL
+ * @notify: (allow-none): function to call when the idle is removed, or %NULL
*
* Adds a function to be called whenever there are no higher priority
* events pending. If the function returns %FALSE it is automatically
* @priority: the priority at which to run @function
* @function: function to call
* @data: data to pass to @function
- * @notify: a function to call when @data is no longer in use, or %NULL.
+ * @notify: (allow-none): a function to call when @data is no longer in use, or %NULL.
*
* Invokes a function in such a way that @context is owned during the
* invocation of @function.
if (g_once_init_enter (&initialised))
{
- GError *error = NULL;
+ /* mask all signals in the worker thread */
+#ifdef G_OS_UNIX
+ sigset_t prev_mask;
+ sigset_t all;
+ sigfillset (&all);
+ pthread_sigmask (SIG_SETMASK, &all, &prev_mask);
+#endif
glib_worker_context = g_main_context_new ();
- if (g_thread_new ("gmain", glib_worker_main, NULL, FALSE, &error) == NULL)
- g_error ("Creating GLib worker thread failed: %s\n", error->message);
-
+ g_thread_new ("gmain", glib_worker_main, NULL);
+#ifdef G_OS_UNIX
+ pthread_sigmask (SIG_SETMASK, &prev_mask, NULL);
+#endif
g_once_init_leave (&initialised, TRUE);
}