X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=glib%2Fgmain.c;h=797aea9308daba34e9846b8ab1a46b652e037030;hb=0d1a92ca3d234a4291ef3ecbf7df2d57442a63e5;hp=3194c377778286feaee91ee879f3c5fd19767f4c;hpb=c3a0d32ef1413f942890796aa20f7b1a54254eff;p=platform%2Fupstream%2Fglib.git diff --git a/glib/gmain.c b/glib/gmain.c index 3194c37..797aea9 100644 --- a/glib/gmain.c +++ b/glib/gmain.c @@ -31,21 +31,6 @@ * MT safe */ -#ifndef _WIN32 -/* for pipe2; need to define it first to avoid - * other headers pulling in unistd.h - */ -/* The meaning of_GNU_SOURCE that is intended here is present only on - * Linux; avoid the possibility that some misguided header in MinGW - * looks at it. Ideally we should define _GNU_SOURCE only on platforms - * where we know what it means and that is what we want here - * (i.e. Linux with glibc). After all, there might be some other POSIX - * platform even where _GNU_SOURCE is used for some unrelated change - * in semantics that isn't wanted. Sigh. - */ -#define _GNU_SOURCE -#endif - #include "config.h" #include "glibconfig.h" @@ -62,6 +47,14 @@ #define G_MAIN_POLL_DEBUG #endif +#ifdef G_OS_UNIX +#include "glib-unix.h" +#include +#ifdef HAVE_EVENTFD +#include +#endif +#endif + #include #include #include @@ -73,6 +66,7 @@ #include #endif /* HAVE_UNISTD_H */ #include +#include #ifdef G_OS_WIN32 #define STRICT @@ -84,11 +78,6 @@ #include #endif /* G_OS_BEOS */ -#ifdef G_OS_UNIX -#include -#include -#endif - #include "gmain.h" #include "garray.h" @@ -108,6 +97,10 @@ #include "gtimer.h" #endif +#include "gwakeup.h" + +#include "glib-private.h" + /** * SECTION:main * @title: The Main Event Loop @@ -182,12 +175,17 @@ * * * + * + * On Unix, the GLib mainloop is incompatible with fork(). Any program + * using the mainloop must either exec() or exit() from the child + * without returning to the mainloop. */ /* Types */ typedef struct _GTimeoutSource GTimeoutSource; typedef struct _GChildWatchSource GChildWatchSource; +typedef struct _GUnixSignalWatchSource GUnixSignalWatchSource; typedef struct _GPollRec GPollRec; typedef struct _GSourceCallback GSourceCallback; @@ -197,7 +195,6 @@ typedef enum G_SOURCE_CAN_RECURSE = 1 << (G_HOOK_FLAG_USER_SHIFT + 1) } GSourceFlags; -#ifdef G_THREADS_ENABLED typedef struct _GMainWaiter GMainWaiter; struct _GMainWaiter @@ -205,7 +202,6 @@ struct _GMainWaiter GCond *cond; GMutex *mutex; }; -#endif typedef struct _GMainDispatch GMainDispatch; @@ -221,16 +217,14 @@ gboolean _g_main_poll_debug = FALSE; struct _GMainContext { -#ifdef G_THREADS_ENABLED /* The following lock is used for both the list of sources * and the list of poll records */ - GStaticMutex mutex; + GMutex mutex; GCond *cond; GThread *owner; guint owner_count; GSList *waiters; -#endif gint ref_count; @@ -241,33 +235,22 @@ struct _GMainContext GSource *source_list; gint in_check_or_prepare; - GPollRec *poll_records; + GPollRec *poll_records, *poll_records_tail; guint n_poll_records; GPollFD *cached_poll_array; guint cached_poll_array_size; -#ifdef G_THREADS_ENABLED -#ifndef G_OS_WIN32 -/* this pipe is used to wake up the main loop when a source is added. - */ - gint wake_up_pipe[2]; -#else /* G_OS_WIN32 */ - HANDLE wake_up_semaphore; -#endif /* G_OS_WIN32 */ + GWakeup *wakeup; GPollFD wake_up_rec; - gboolean poll_waiting; /* Flag indicating whether the set of fd's changed during a poll */ gboolean poll_changed; -#endif /* G_THREADS_ENABLED */ GPollFunc poll_func; gint64 time; gboolean time_is_fresh; - GTimeVal current_time; - gboolean current_time_is_fresh; }; struct _GSourceCallback @@ -301,27 +284,34 @@ struct _GChildWatchSource #ifdef G_OS_WIN32 GPollFD poll; #else /* G_OS_WIN32 */ - gint count; gboolean child_exited; #endif /* G_OS_WIN32 */ }; +struct _GUnixSignalWatchSource +{ + GSource source; + int signum; + gboolean pending; +}; + struct _GPollRec { GPollFD *fd; + GPollRec *prev; GPollRec *next; gint priority; }; -#ifdef G_THREADS_ENABLED -#define LOCK_CONTEXT(context) g_static_mutex_lock (&context->mutex) -#define UNLOCK_CONTEXT(context) g_static_mutex_unlock (&context->mutex) +struct _GSourcePrivate +{ + GSList *child_sources; + GSource *parent_source; +}; + +#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 () -#else -#define LOCK_CONTEXT(context) (void)0 -#define UNLOCK_CONTEXT(context) (void)0 -#define G_THREAD_SELF NULL -#endif #define SOURCE_DESTROYED(source) (((source)->flags & G_HOOK_FLAG_ACTIVE) == 0) #define SOURCE_BLOCKED(source) (((source)->flags & G_HOOK_FLAG_IN_CALL) != 0 && \ @@ -344,6 +334,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, @@ -354,7 +347,6 @@ static void g_main_context_add_poll_unlocked (GMainContext *context, GPollFD *fd); static void g_main_context_remove_poll_unlocked (GMainContext *context, GPollFD *fd); -static void g_main_context_wakeup_unlocked (GMainContext *context); static gboolean g_timeout_prepare (GSource *source, gint *timeout); @@ -368,6 +360,17 @@ static gboolean g_child_watch_check (GSource *source); static gboolean g_child_watch_dispatch (GSource *source, GSourceFunc callback, gpointer user_data); +static void g_child_watch_finalize (GSource *source); +#ifdef G_OS_UNIX +static void g_unix_signal_handler (int signum); +static gboolean g_unix_signal_watch_prepare (GSource *source, + gint *timeout); +static gboolean g_unix_signal_watch_check (GSource *source); +static gboolean g_unix_signal_watch_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data); +static void g_unix_signal_watch_finalize (GSource *source); +#endif static gboolean g_idle_prepare (GSource *source, gint *timeout); static gboolean g_idle_check (GSource *source); @@ -375,20 +378,33 @@ static gboolean g_idle_dispatch (GSource *source, GSourceFunc callback, gpointer user_data); +static GMainContext *glib_worker_context; +static gboolean g_main_context_fork_detected; + G_LOCK_DEFINE_STATIC (main_loop); static GMainContext *default_main_context; -static GSList *main_contexts_without_pipe = NULL; #ifndef G_OS_WIN32 -/* Child status monitoring code */ -enum { - CHILD_WATCH_UNINITIALIZED, - CHILD_WATCH_INITIALIZED_SINGLE, - CHILD_WATCH_INITIALIZED_THREADED + + +/* 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; + +/* 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 = +{ + g_unix_signal_watch_prepare, + g_unix_signal_watch_check, + g_unix_signal_watch_dispatch, + g_unix_signal_watch_finalize }; -static gint child_watch_init_state = CHILD_WATCH_UNINITIALIZED; -static gint child_watch_count = 1; -static gint child_watch_wake_up_pipe[2] = {0, 0}; #endif /* !G_OS_WIN32 */ G_LOCK_DEFINE_STATIC (main_context_list); static GSList *main_context_list = NULL; @@ -406,7 +422,7 @@ GSourceFuncs g_child_watch_funcs = g_child_watch_prepare, g_child_watch_check, g_child_watch_dispatch, - NULL + g_child_watch_finalize }; GSourceFuncs g_idle_funcs = @@ -472,89 +488,28 @@ g_main_context_unref (GMainContext *context) source = next; } -#ifdef G_THREADS_ENABLED - g_static_mutex_free (&context->mutex); -#endif + g_mutex_clear (&context->mutex); g_ptr_array_free (context->pending_dispatches, TRUE); g_free (context->cached_poll_array); poll_rec_list_free (context, context->poll_records); - -#ifdef G_THREADS_ENABLED - if (g_thread_supported()) - { -#ifndef G_OS_WIN32 - close (context->wake_up_pipe[0]); - close (context->wake_up_pipe[1]); -#else - CloseHandle (context->wake_up_semaphore); -#endif - } - else - main_contexts_without_pipe = g_slist_remove (main_contexts_without_pipe, - context); + + g_wakeup_free (context->wakeup); if (context->cond != NULL) g_cond_free (context->cond); -#endif - - g_free (context); -} - -#ifdef G_THREADS_ENABLED -static void -g_main_context_init_pipe (GMainContext *context) -{ -# ifndef G_OS_WIN32 - if (context->wake_up_pipe[0] != -1) - return; -#ifdef HAVE_PIPE2 - /* if this fails, we fall through and try pipe */ - pipe2 (context->wake_up_pipe, O_CLOEXEC); -#endif - if (context->wake_up_pipe[0] == -1) - { - if (pipe (context->wake_up_pipe) < 0) - g_error ("Cannot create pipe main loop wake-up: %s\n", - g_strerror (errno)); - - fcntl (context->wake_up_pipe[0], F_SETFD, FD_CLOEXEC); - fcntl (context->wake_up_pipe[1], F_SETFD, FD_CLOEXEC); - } - - context->wake_up_rec.fd = context->wake_up_pipe[0]; - context->wake_up_rec.events = G_IO_IN; -# else - if (context->wake_up_semaphore != NULL) - return; - context->wake_up_semaphore = CreateSemaphore (NULL, 0, 100, NULL); - if (context->wake_up_semaphore == NULL) - g_error ("Cannot create wake-up semaphore: %s", - g_win32_error_message (GetLastError ())); - context->wake_up_rec.fd = (gintptr) context->wake_up_semaphore; - context->wake_up_rec.events = G_IO_IN; - - if (_g_main_poll_debug) - g_print ("wake-up semaphore: %p\n", context->wake_up_semaphore); -# endif - g_main_context_add_poll_unlocked (context, 0, &context->wake_up_rec); + g_free (context); } -void -_g_main_thread_init (void) +#ifdef G_OS_UNIX +static void +g_main_context_forked (void) { - GSList *curr = main_contexts_without_pipe; - while (curr) - { - g_main_context_init_pipe ((GMainContext *)curr->data); - curr = curr->next; - } - g_slist_free (main_contexts_without_pipe); - main_contexts_without_pipe = NULL; + g_main_context_fork_detected = TRUE; } -#endif /* G_THREADS_ENABLED */ +#endif /** * g_main_context_new: @@ -566,35 +521,32 @@ _g_main_thread_init (void) GMainContext * g_main_context_new (void) { - GMainContext *context = g_new0 (GMainContext, 1); + static gsize initialised; + GMainContext *context; + + g_thread_init_glib (); + if (g_once_init_enter (&initialised)) + { #ifdef G_MAIN_POLL_DEBUG - { - static gboolean beenhere = FALSE; + if (getenv ("G_MAIN_POLL_DEBUG") != NULL) + _g_main_poll_debug = TRUE; +#endif - if (!beenhere) - { - if (getenv ("G_MAIN_POLL_DEBUG") != NULL) - _g_main_poll_debug = TRUE; - beenhere = TRUE; - } - } +#ifdef G_OS_UNIX + pthread_atfork (NULL, NULL, g_main_context_forked); #endif -#ifdef G_THREADS_ENABLED - g_static_mutex_init (&context->mutex); + g_once_init_leave (&initialised, TRUE); + } + + context = g_new0 (GMainContext, 1); + + g_mutex_init (&context->mutex); context->owner = NULL; context->waiters = NULL; -# ifndef G_OS_WIN32 - context->wake_up_pipe[0] = -1; - context->wake_up_pipe[1] = -1; -# else - context->wake_up_semaphore = NULL; -# endif -#endif - context->ref_count = 1; context->next_id = 1; @@ -609,15 +561,10 @@ g_main_context_new (void) context->pending_dispatches = g_ptr_array_new (); context->time_is_fresh = FALSE; - context->current_time_is_fresh = FALSE; -#ifdef G_THREADS_ENABLED - if (g_thread_supported ()) - g_main_context_init_pipe (context); - else - main_contexts_without_pipe = g_slist_prepend (main_contexts_without_pipe, - context); -#endif + context->wakeup = g_wakeup_new (); + g_wakeup_get_pollfd (context->wakeup, &context->wake_up_rec); + g_main_context_add_poll_unlocked (context, 0, &context->wake_up_rec); G_LOCK (main_context_list); main_context_list = g_slist_append (main_context_list, context); @@ -640,7 +587,7 @@ g_main_context_new (void) * specified, and corresponds to the "main" main loop. See also * g_main_context_get_thread_default(). * - * Return value: the global default main context. + * Return value: (transfer none): the global default main context. **/ GMainContext * g_main_context_default (void) @@ -781,8 +728,8 @@ g_main_context_pop_thread_default (GMainContext *context) * non-default context, so it is not safe to assume that this will * always return %NULL if threads are not initialized.) * - * Returns: the thread-default #GMainContext, or %NULL if the - * thread-default context is the global default context. + * Returns: (transfer none): the thread-default #GMainContext, or + * %NULL if the thread-default context is the global default context. * * Since: 2.22 **/ @@ -848,12 +795,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 +841,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 +890,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,23 +899,13 @@ 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() */ - g_main_context_wakeup_unlocked (context); -#endif + /* 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); UNLOCK_CONTEXT (context); @@ -972,6 +950,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); } @@ -1036,9 +1032,9 @@ g_source_get_id (GSource *source) * Gets the #GMainContext with which the source is associated. * Calling this function on a destroyed source is an error. * - * Return value: the #GMainContext with which the source is associated, - * or %NULL if the context has not yet been added - * to a source. + * Return value: (transfer 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) @@ -1119,6 +1115,98 @@ 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 (in addition to calling its 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 don't 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. + * + * Since: 2.28 + **/ +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. + * + * Since: 2.28 + **/ +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 +1351,19 @@ g_source_set_funcs (GSource *source, source->source_funcs = funcs; } -/** - * g_source_set_priority: - * @source: a #GSource - * @priority: the new priority. - * - * Sets the priority of a source. While the main loop is being - * run, a source will be dispatched if it is ready to be dispatched and no sources - * at a higher (numerically smaller) priority are ready to be dispatched. - **/ -void -g_source_set_priority (GSource *source, - gint priority) +static void +g_source_set_priority_unlocked (GSource *source, + GMainContext *context, + gint priority) { GSList *tmp_list; - GMainContext *context; - - g_return_if_fail (source != NULL); - - context = source->context; - - if (context) - LOCK_CONTEXT (context); source->priority = priority; if (context) { /* Remove the source from the context's source and then - * add it back so it is sorted in the correct plcae + * add it back so it is sorted in the correct place */ g_source_list_remove (source, source->context); g_source_list_add (source, source->context); @@ -1307,12 +1379,47 @@ g_source_set_priority (GSource *source, tmp_list = tmp_list->next; } } - - UNLOCK_CONTEXT (source->context); + } + + if (source->priv && source->priv->child_sources) + { + tmp_list = source->priv->child_sources; + while (tmp_list) + { + g_source_set_priority_unlocked (tmp_list->data, context, priority); + tmp_list = tmp_list->next; + } } } /** + * g_source_set_priority: + * @source: a #GSource + * @priority: the new priority. + * + * Sets the priority of a source. While the main loop is being run, a + * source will be dispatched if it is ready to be dispatched and no + * sources at a higher (numerically smaller) priority are ready to be + * dispatched. + **/ +void +g_source_set_priority (GSource *source, + gint priority) +{ + GMainContext *context; + + g_return_if_fail (source != NULL); + + context = source->context; + + if (context) + LOCK_CONTEXT (context); + g_source_set_priority_unlocked (source, context, priority); + if (context) + UNLOCK_CONTEXT (source->context); +} + +/** * g_source_get_priority: * @source: a #GSource * @@ -1423,7 +1530,7 @@ g_source_set_name (GSource *source, * Return value: the name of the source * Since: 2.26 **/ -G_CONST_RETURN char* +const char * g_source_get_name (GSource *source) { g_return_val_if_fail (source != NULL, NULL); @@ -1511,22 +1618,34 @@ g_source_unref_internal (GSource *source, source->callback_data = NULL; source->callback_funcs = NULL; - if (context && !SOURCE_DESTROYED (source)) + if (context) { - g_warning (G_STRLOC ": ref_count == 0, but source is still attached to a context!"); - source->ref_count++; + 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); } - else if (context) - g_source_list_remove (source, context); if (source->source_funcs->finalize) - source->source_funcs->finalize (source); + { + if (context) + UNLOCK_CONTEXT (context); + source->source_funcs->finalize (source); + if (context) + LOCK_CONTEXT (context); + } g_free (source->name); source->name = NULL; g_slist_free (source->poll_fds); source->poll_fds = NULL; + + if (source->priv) + { + g_slice_free (GSourcePrivate, source->priv); + source->priv = NULL; + } + g_free (source); } @@ -1568,7 +1687,7 @@ g_source_unref (GSource *source) * * Finds a #GSource given a pair of context and ID. * - * Return value: the #GSource if found, otherwise, %NULL + * Return value: (transfer none): the #GSource if found, otherwise, %NULL **/ GSource * g_main_context_find_source_by_id (GMainContext *context, @@ -1607,7 +1726,7 @@ g_main_context_find_source_by_id (GMainContext *context, * multiple sources exist with the same source function and user data, * the first one found will be returned. * - * Return value: the source, if one was found, otherwise %NULL + * Return value: (transfer none): the source, if one was found, otherwise %NULL **/ GSource * g_main_context_find_source_by_funcs_user_data (GMainContext *context, @@ -1655,7 +1774,7 @@ g_main_context_find_source_by_funcs_user_data (GMainContext *context, * multiple sources exist with the same user data, the first * one found will be returned. * - * Return value: the source, if one was found, otherwise %NULL + * Return value: (transfer none): the source, if one was found, otherwise %NULL **/ GSource * g_main_context_find_source_by_user_data (GMainContext *context, @@ -1777,8 +1896,10 @@ g_source_remove_by_funcs_user_data (GSourceFuncs *funcs, /** * g_get_current_time: * @result: #GTimeVal structure in which to store current time. - * + * * Equivalent to the UNIX gettimeofday() function, but portable. + * + * You may find g_get_real_time() to be more convenient. **/ void g_get_current_time (GTimeVal *result) @@ -1814,14 +1935,31 @@ g_get_current_time (GTimeVal *result) } /** - * GTimeSpec: + * g_get_real_time: + * + * Queries the system wall-clock time. + * + * This call is functionally equivalent to g_get_current_time() except + * that the return value is often more convenient than dealing with a + * #GTimeVal. * - * Represents a precise time, with seconds and nanoseconds. This is - * similar to POSIX struct timespec. This - * structure can be filled in with g_get_monotonic_time(). + * You should only use this call if you are actually interested in the real + * wall-clock time. g_get_monotonic_time() is probably more useful for + * measuring intervals. + * + * Returns: the number of microseconds since January 1, 1970 UTC. * * Since: 2.28 **/ +gint64 +g_get_real_time (void) +{ + GTimeVal tv; + + g_get_current_time (&tv); + + return (((gint64) tv.tv_sec) * 1000000) + tv.tv_usec; +} /** * g_get_monotonic_time: @@ -1833,10 +1971,13 @@ g_get_current_time (GTimeVal *result) * that probably involves returning the wall clock time (with at least * microsecond accuracy, subject to the limitations of the OS kernel). * - * Note that, 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). + * It's important to note that POSIX %CLOCK_MONOTONIC 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). * * Returns: the monotonic time, in microseconds * @@ -1848,25 +1989,24 @@ g_get_monotonic_time (void) #ifdef HAVE_CLOCK_GETTIME /* librt clock_gettime() is our first choice */ { - static int clockid = CLOCK_REALTIME; +#ifdef HAVE_MONOTONIC_CLOCK + static volatile gsize clockid = 0; +#else + static clockid_t clockid = CLOCK_REALTIME; +#endif struct timespec ts; #ifdef HAVE_MONOTONIC_CLOCK - /* We have to check if we actually have monotonic clock support. - * - * There is no thread safety issue here since there is no harm if we - * check twice. - */ - { - static gboolean checked; + if (g_once_init_enter (&clockid)) + { + clockid_t best_clockid; - if G_UNLIKELY (!checked) - { - if (sysconf (_SC_MONOTONIC_CLOCK) >= 0) - clockid = CLOCK_MONOTONIC; - checked = TRUE; - } - } + if (sysconf (_SC_MONOTONIC_CLOCK) >= 0) + best_clockid = CLOCK_MONOTONIC; + else + best_clockid = CLOCK_REALTIME; + g_once_init_leave (&clockid, (gsize)best_clockid); + } #endif clock_gettime (clockid, &ts); @@ -1941,7 +2081,7 @@ get_dispatch (void) * That is, when called from the toplevel, it gives 0. When * called from within a callback from g_main_context_iteration() * (or g_main_loop_run(), etc.) it returns 1. When called from within - * a callback to a recursive call to g_main_context_iterate(), + * a callback to a recursive call to g_main_context_iteration(), * it returns 2. And so forth. * * This function is useful in a situation like the following: @@ -2061,7 +2201,7 @@ g_main_depth (void) * * Returns the currently firing source for this thread. * - * Return value: The currently firing source or %NULL. + * Return value: (transfer none): The currently firing source or %NULL. * * Since: 2.12 */ @@ -2325,7 +2465,6 @@ next_valid_source (GMainContext *context, gboolean g_main_context_acquire (GMainContext *context) { -#ifdef G_THREADS_ENABLED gboolean result = FALSE; GThread *self = G_THREAD_SELF; @@ -2349,9 +2488,6 @@ g_main_context_acquire (GMainContext *context) UNLOCK_CONTEXT (context); return result; -#else /* !G_THREADS_ENABLED */ - return TRUE; -#endif /* G_THREADS_ENABLED */ } /** @@ -2366,7 +2502,6 @@ g_main_context_acquire (GMainContext *context) void g_main_context_release (GMainContext *context) { -#ifdef G_THREADS_ENABLED if (context == NULL) context = g_main_context_default (); @@ -2380,8 +2515,7 @@ g_main_context_release (GMainContext *context) if (context->waiters) { GMainWaiter *waiter = context->waiters->data; - gboolean loop_internal_waiter = - (waiter->mutex == g_static_mutex_get_mutex (&context->mutex)); + gboolean loop_internal_waiter = (waiter->mutex == &context->mutex); context->waiters = g_slist_delete_link (context->waiters, context->waiters); if (!loop_internal_waiter) @@ -2395,7 +2529,6 @@ g_main_context_release (GMainContext *context) } UNLOCK_CONTEXT (context); -#endif /* G_THREADS_ENABLED */ } /** @@ -2418,7 +2551,6 @@ g_main_context_wait (GMainContext *context, GCond *cond, GMutex *mutex) { -#ifdef G_THREADS_ENABLED gboolean result = FALSE; GThread *self = G_THREAD_SELF; gboolean loop_internal_waiter; @@ -2426,7 +2558,7 @@ g_main_context_wait (GMainContext *context, if (context == NULL) context = g_main_context_default (); - loop_internal_waiter = (mutex == g_static_mutex_get_mutex (&context->mutex)); + loop_internal_waiter = (mutex == &context->mutex); if (!loop_internal_waiter) LOCK_CONTEXT (context); @@ -2465,9 +2597,6 @@ g_main_context_wait (GMainContext *context, UNLOCK_CONTEXT (context); return result; -#else /* !G_THREADS_ENABLED */ - return TRUE; -#endif /* G_THREADS_ENABLED */ } /** @@ -2497,7 +2626,6 @@ g_main_context_prepare (GMainContext *context, LOCK_CONTEXT (context); context->time_is_fresh = FALSE; - context->current_time_is_fresh = FALSE; if (context->in_check_or_prepare) { @@ -2507,17 +2635,6 @@ g_main_context_prepare (GMainContext *context, return FALSE; } -#ifdef G_THREADS_ENABLED - if (context->poll_waiting) - { - g_warning("g_main_context_prepare(): main loop already active in another thread"); - UNLOCK_CONTEXT (context); - return FALSE; - } - - context->poll_waiting = TRUE; -#endif /* G_THREADS_ENABLED */ - #if 0 /* If recursing, finish up current dispatch, before starting over */ if (context->pending_dispatches) @@ -2572,7 +2689,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) @@ -2606,8 +2731,9 @@ g_main_context_prepare (GMainContext *context, * g_main_context_query: * @context: a #GMainContext * @max_priority: maximum priority source to check - * @timeout_: location to store timeout to be used in polling - * @fds: location to store #GPollFD records that need to be polled. + * @timeout_: (out): location to store timeout to be used in polling + * @fds: (out caller-allocates) (array length=n_fds): location to + * store #GPollFD records that need to be polled. * @n_fds: length of @fds. * * Determines information necessary to poll this main loop. @@ -2653,18 +2779,13 @@ g_main_context_query (GMainContext *context, n_poll++; } -#ifdef G_THREADS_ENABLED context->poll_changed = FALSE; -#endif if (timeout) { *timeout = context->timeout; if (*timeout != 0) - { - context->time_is_fresh = FALSE; - context->current_time_is_fresh = FALSE; - } + context->time_is_fresh = FALSE; } UNLOCK_CONTEXT (context); @@ -2676,8 +2797,8 @@ g_main_context_query (GMainContext *context, * g_main_context_check: * @context: a #GMainContext * @max_priority: the maximum numerical priority of sources to check - * @fds: array of #GPollFD's that was passed to the last call to - * g_main_context_query() + * @fds: (array length=n_fds): array of #GPollFD's that was passed to + * the last call to g_main_context_query() * @n_fds: return value of g_main_context_query() * * Passes the results of polling back to the main loop. @@ -2704,17 +2825,9 @@ g_main_context_check (GMainContext *context, UNLOCK_CONTEXT (context); return FALSE; } - -#ifdef G_THREADS_ENABLED - if (!context->poll_waiting) - { -#ifndef G_OS_WIN32 - gchar a; - read (context->wake_up_pipe[0], &a, 1); -#endif - } - else - context->poll_waiting = FALSE; + + if (context->wake_up_rec.events) + g_wakeup_acknowledge (context->wakeup); /* If the set of poll file descriptors changed, bail out * and let the main loop rerun @@ -2724,7 +2837,6 @@ g_main_context_check (GMainContext *context, UNLOCK_CONTEXT (context); return FALSE; } -#endif /* G_THREADS_ENABLED */ pollrec = context->poll_records; i = 0; @@ -2764,7 +2876,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) @@ -2823,15 +2943,12 @@ g_main_context_iterate (GMainContext *context, UNLOCK_CONTEXT (context); -#ifdef G_THREADS_ENABLED if (!g_main_context_acquire (context)) { gboolean got_ownership; LOCK_CONTEXT (context); - g_return_val_if_fail (g_thread_supported (), FALSE); - if (!block) return FALSE; @@ -2840,14 +2957,13 @@ g_main_context_iterate (GMainContext *context, got_ownership = g_main_context_wait (context, context->cond, - g_static_mutex_get_mutex (&context->mutex)); + &context->mutex); if (!got_ownership) return FALSE; } else LOCK_CONTEXT (context); -#endif /* G_THREADS_ENABLED */ if (!context->cached_poll_array) { @@ -2875,6 +2991,7 @@ g_main_context_iterate (GMainContext *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); @@ -2882,9 +2999,7 @@ g_main_context_iterate (GMainContext *context, if (dispatch) g_main_context_dispatch (context); -#ifdef G_THREADS_ENABLED g_main_context_release (context); -#endif /* G_THREADS_ENABLED */ LOCK_CONTEXT (context); @@ -2951,7 +3066,7 @@ g_main_context_iteration (GMainContext *context, gboolean may_block) /** * g_main_loop_new: - * @context: a #GMainContext (if %NULL, the default context will be used). + * @context: (allow-none): a #GMainContext (if %NULL, the default context will be used). * @is_running: set to %TRUE to indicate that the loop is running. This * is not very important since calling g_main_loop_run() will set this to * %TRUE anyway. @@ -3035,19 +3150,11 @@ g_main_loop_run (GMainLoop *loop) g_return_if_fail (loop != NULL); g_return_if_fail (g_atomic_int_get (&loop->ref_count) > 0); -#ifdef G_THREADS_ENABLED if (!g_main_context_acquire (loop->context)) { gboolean got_ownership = FALSE; /* Another thread owns this context */ - if (!g_thread_supported ()) - { - g_warning ("g_main_loop_run() was called from second thread but " - "g_thread_init() was never called."); - return; - } - LOCK_CONTEXT (loop->context); g_atomic_int_inc (&loop->ref_count); @@ -3061,7 +3168,7 @@ g_main_loop_run (GMainLoop *loop) while (loop->is_running && !got_ownership) got_ownership = g_main_context_wait (loop->context, loop->context->cond, - g_static_mutex_get_mutex (&loop->context->mutex)); + &loop->context->mutex); if (!loop->is_running) { @@ -3076,7 +3183,6 @@ g_main_loop_run (GMainLoop *loop) } else LOCK_CONTEXT (loop->context); -#endif /* G_THREADS_ENABLED */ if (loop->context->in_check_or_prepare) { @@ -3092,9 +3198,7 @@ g_main_loop_run (GMainLoop *loop) UNLOCK_CONTEXT (loop->context); -#ifdef G_THREADS_ENABLED g_main_context_release (loop->context); -#endif /* G_THREADS_ENABLED */ g_main_loop_unref (loop); } @@ -3117,12 +3221,10 @@ g_main_loop_quit (GMainLoop *loop) LOCK_CONTEXT (loop->context); loop->is_running = FALSE; - g_main_context_wakeup_unlocked (loop->context); + g_wakeup_signal (loop->context->wakeup); -#ifdef G_THREADS_ENABLED if (loop->context->cond) g_cond_broadcast (loop->context->cond); -#endif /* G_THREADS_ENABLED */ UNLOCK_CONTEXT (loop->context); } @@ -3150,7 +3252,7 @@ g_main_loop_is_running (GMainLoop *loop) * * Returns the #GMainContext of @loop. * - * Return value: the #GMainContext of @loop + * Return value: (transfer none): the #GMainContext of @loop **/ GMainContext * g_main_loop_get_context (GMainLoop *loop) @@ -3261,7 +3363,7 @@ g_main_context_poll (GMainContext *context, * 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 seldomly be used directly. Instead + * this context. This will very seldom be used directly. Instead * a typical event source will use g_source_add_poll() instead. **/ void @@ -3286,7 +3388,7 @@ g_main_context_add_poll_unlocked (GMainContext *context, gint priority, GPollFD *fd) { - GPollRec *lastrec, *pollrec; + GPollRec *prevrec, *nextrec; GPollRec *newrec = g_slice_new (GPollRec); /* This file descriptor may be checked before we ever poll */ @@ -3294,29 +3396,33 @@ g_main_context_add_poll_unlocked (GMainContext *context, newrec->fd = fd; newrec->priority = priority; - lastrec = NULL; - pollrec = context->poll_records; - while (pollrec && priority >= pollrec->priority) + prevrec = context->poll_records_tail; + nextrec = NULL; + while (prevrec && priority < prevrec->priority) { - lastrec = pollrec; - pollrec = pollrec->next; + nextrec = prevrec; + prevrec = prevrec->prev; } - - if (lastrec) - lastrec->next = newrec; + + if (prevrec) + prevrec->next = newrec; else context->poll_records = newrec; - newrec->next = pollrec; + newrec->prev = prevrec; + newrec->next = nextrec; + + if (nextrec) + nextrec->prev = newrec; + else + context->poll_records_tail = newrec; context->n_poll_records++; -#ifdef G_THREADS_ENABLED context->poll_changed = TRUE; /* Now wake up the main loop if it is waiting in the poll() */ - g_main_context_wakeup_unlocked (context); -#endif + g_wakeup_signal (context->wakeup); } /** @@ -3346,47 +3452,48 @@ static void g_main_context_remove_poll_unlocked (GMainContext *context, GPollFD *fd) { - GPollRec *pollrec, *lastrec; + GPollRec *pollrec, *prevrec, *nextrec; - lastrec = NULL; + prevrec = NULL; pollrec = context->poll_records; while (pollrec) { + nextrec = pollrec->next; if (pollrec->fd == fd) { - if (lastrec != NULL) - lastrec->next = pollrec->next; + if (prevrec != NULL) + prevrec->next = nextrec; + else + context->poll_records = nextrec; + + if (nextrec != NULL) + nextrec->prev = prevrec; else - context->poll_records = pollrec->next; + context->poll_records_tail = prevrec; g_slice_free (GPollRec, pollrec); context->n_poll_records--; break; } - lastrec = pollrec; - pollrec = pollrec->next; + prevrec = pollrec; + pollrec = nextrec; } -#ifdef G_THREADS_ENABLED context->poll_changed = TRUE; /* Now wake up the main loop if it is waiting in the poll() */ - g_main_context_wakeup_unlocked (context); -#endif + g_wakeup_signal (context->wakeup); } /** * g_source_get_current_time: * @source: a #GSource * @timeval: #GTimeVal structure in which to store current time. - * - * Gets the "current time" to be used when checking - * this source. The advantage of calling this function over - * calling g_get_current_time() directly is that when - * checking multiple sources, GLib can cache a single value - * instead of having to repeatedly get the system time. + * + * This function ignores @source and is otherwise the same as + * g_get_current_time(). * * Deprecated: 2.28: use g_source_get_time() instead **/ @@ -3394,23 +3501,7 @@ void g_source_get_current_time (GSource *source, GTimeVal *timeval) { - GMainContext *context; - - g_return_if_fail (source->context != NULL); - - context = source->context; - - LOCK_CONTEXT (context); - - if (!context->current_time_is_fresh) - { - g_get_current_time (&context->current_time); - context->current_time_is_fresh = TRUE; - } - - *timeval = context->current_time; - - UNLOCK_CONTEXT (context); + g_get_current_time (timeval); } /** @@ -3511,24 +3602,6 @@ g_main_context_get_poll_func (GMainContext *context) return result; } -/* HOLDS: context's lock */ -/* Wake the main loop up from a poll() */ -static void -g_main_context_wakeup_unlocked (GMainContext *context) -{ -#ifdef G_THREADS_ENABLED - if (g_thread_supported() && context->poll_waiting) - { - context->poll_waiting = FALSE; -#ifndef G_OS_WIN32 - write (context->wake_up_pipe[1], "A", 1); -#else - ReleaseSemaphore (context->wake_up_semaphore, 1, NULL); -#endif - } -#endif -} - /** * g_main_context_wakeup: * @context: a #GMainContext @@ -3541,12 +3614,10 @@ g_main_context_wakeup (GMainContext *context) { if (!context) context = g_main_context_default (); - + g_return_if_fail (g_atomic_int_get (&context->ref_count) > 0); - LOCK_CONTEXT (context); - g_main_context_wakeup_unlocked (context); - UNLOCK_CONTEXT (context); + g_wakeup_signal (context->wakeup); } /** @@ -3554,7 +3625,7 @@ g_main_context_wakeup (GMainContext *context) * @context: a #GMainContext * * Determines whether this thread holds the (recursive) - * ownership of this #GMaincontext. This is useful to + * ownership of this #GMainContext. This is useful to * know before waiting on another thread that may be * blocking to get ownership of @context. * @@ -3570,13 +3641,9 @@ g_main_context_is_owner (GMainContext *context) if (!context) context = g_main_context_default (); -#ifdef G_THREADS_ENABLED LOCK_CONTEXT (context); is_owner = context->owner == G_THREAD_SELF; UNLOCK_CONTEXT (context); -#else - is_owner = TRUE; -#endif return is_owner; } @@ -3587,10 +3654,12 @@ static void g_timeout_set_expiration (GTimeoutSource *timeout_source, gint64 current_time) { - timeout_source->expiration = current_time + timeout_source->interval * 1000; + timeout_source->expiration = current_time + + (guint64) timeout_source->interval * 1000; if (timeout_source->seconds) { + gint64 remainder; static gint timer_perturb = -1; if (timer_perturb == -1) @@ -3615,11 +3684,14 @@ g_timeout_set_expiration (GTimeoutSource *timeout_source, * always only *increase* the expiration time by adding a full * second in the case that the microsecond portion decreases. */ - if (timer_perturb < timeout_source->expiration % 1000000) + timeout_source->expiration -= timer_perturb; + + remainder = timeout_source->expiration % 1000000; + if (remainder >= 1000000/4) timeout_source->expiration += 1000000; - timeout_source->expiration = - ((timeout_source->expiration / 1000000) * 1000000) + timer_perturb; + timeout_source->expiration -= remainder; + timeout_source->expiration += timer_perturb; } } @@ -3682,6 +3754,9 @@ g_timeout_dispatch (GSource *source, * The source will not initially be associated with any #GMainContext * and must be added to one with g_source_attach() before it will be * executed. + * + * The interval given is in terms of monotonic time, not wall clock + * time. See g_get_monotonic_time(). * * Return value: the newly-created timeout source **/ @@ -3710,6 +3785,9 @@ g_timeout_source_new (guint interval) * The scheduling granularity/accuracy of this timeout source will be * in seconds. * + * The interval given in terms of monotonic time, not wall clock time. + * See g_get_monotonic_time(). + * * Return value: the newly-created timeout source * * Since: 2.14 @@ -3755,8 +3833,12 @@ g_timeout_source_new_seconds (guint interval) * This internally creates a main loop source using g_timeout_source_new() * and attaches it to the main loop context using g_source_attach(). You can * do these steps manually if you need greater control. + * + * The interval given in terms of monotonic time, not wall clock time. + * See g_get_monotonic_time(). * * Return value: the ID (greater than 0) of the event source. + * Rename to: g_timeout_add **/ guint g_timeout_add_full (gint priority, @@ -3810,6 +3892,9 @@ g_timeout_add_full (gint priority, * and attaches it to the main loop context using g_source_attach(). You can * do these steps manually if you need greater control. * + * The interval given is in terms of monotonic time, not wall clock + * time. See g_get_monotonic_time(). + * * Return value: the ID (greater than 0) of the event source. **/ guint @@ -3861,8 +3946,12 @@ g_timeout_add (guint32 interval, * using g_source_attach(). You can do these steps manually if you need * greater control. * + * The interval given is in terms of monotonic time, not wall clock + * time. See g_get_monotonic_time(). + * * Return value: the ID (greater than 0) of the event source. * + * Rename to: g_timeout_add_seconds * Since: 2.14 **/ guint @@ -3900,10 +3989,17 @@ g_timeout_add_seconds_full (gint priority, * it returns %FALSE, at which point the timeout is automatically destroyed * and the function will not be called again. * - * This internally creates a main loop source using - * g_timeout_source_new_seconds() and attaches it to the main loop context - * using g_source_attach(). You can do these steps manually if you need - * greater control. Also see g_timout_add_seconds_full(). + * This internally creates a main loop source using + * g_timeout_source_new_seconds() and attaches it to the main loop context + * using g_source_attach(). You can do these steps manually if you need + * greater control. Also see g_timeout_add_seconds_full(). + * + * Note that the first call of the timer may not be precise for timeouts + * of one second. If you need finer precision and have such a timeout, + * you may want to use g_timeout_add() instead. + * + * The interval given is in terms of monotonic time, not wall clock + * time. See g_get_monotonic_time(). * * Return value: the ID (greater than 0) of the event source. * @@ -3931,7 +4027,6 @@ g_child_watch_prepare (GSource *source, return FALSE; } - static gboolean g_child_watch_check (GSource *source) { @@ -3967,185 +4062,272 @@ g_child_watch_check (GSource *source) return child_exited; } +static void +g_child_watch_finalize (GSource *source) +{ +} + #else /* G_OS_WIN32 */ -static gboolean -check_for_child_exited (GSource *source) +static void +wake_source (GSource *source) { - GChildWatchSource *child_watch_source; - gint count; + GMainContext *context; - /* protect against another SIGCHLD in the middle of this call */ - count = child_watch_count; + /* This should be thread-safe: + * + * - if the source is currently being added to a context, that + * context will be woken up anyway + * + * - if the source is currently being destroyed, we simply need not + * to crash: + * + * - the memory for the source will remain valid until after the + * source finalize function was called (which would remove the + * source from the global list which we are currently holding the + * lock for) + * + * - the GMainContext will either be NULL or point to a live + * GMainContext + * + * - the GMainContext will remain valid since we hold the + * main_context_list lock + * + * Since we are holding a lot of locks here, don't try to enter any + * more GMainContext functions for fear of dealock -- just hit the + * GWakeup and run. Even if that's safe now, it could easily become + * unsafe with some very minor changes in the future, and signal + * handling is not the most well-tested codepath. + */ + G_LOCK(main_context_list); + context = source->context; + if (context) + g_wakeup_signal (context->wakeup); + G_UNLOCK(main_context_list); +} - child_watch_source = (GChildWatchSource *) source; +static void +dispatch_unix_signals (void) +{ + GSList *node; - if (child_watch_source->child_exited) - return TRUE; + /* clear this first incase another one arrives while we're processing */ + any_unix_signal_pending = FALSE; - if (child_watch_source->count < count) + G_LOCK(unix_signal_lock); + + /* handle GChildWatchSource instances */ + if (unix_signal_pending[SIGCHLD]) { - gint child_status; + 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 + * explicitly watching, so that ties our hands from calling + * waitpid(-1). We also can't use siginfo's si_pid field since if + * multiple SIGCHLD arrive at the same time, one of them can be + * dropped (since a given UNIX signal can only be pending once). + */ + for (node = unix_child_watches; node; node = node->next) + { + GChildWatchSource *source = node->data; - if (waitpid (child_watch_source->pid, &child_status, WNOHANG) > 0) - { - child_watch_source->child_status = child_status; - child_watch_source->child_exited = TRUE; - } - child_watch_source->count = count; + if (!source->child_exited) + { + if (waitpid (source->pid, &source->child_status, WNOHANG) > 0) + { + source->child_exited = TRUE; + + wake_source ((GSource *) source); + } + } + } } - return child_watch_source->child_exited; + /* handle GUnixSignalWatchSource instances */ + for (node = unix_signal_watches; node; node = node->next) + { + GUnixSignalWatchSource *source = node->data; + + if (!source->pending) + { + if (unix_signal_pending[source->signum]) + { + unix_signal_pending[source->signum] = FALSE; + source->pending = TRUE; + + wake_source ((GSource *) source); + } + } + } + + G_UNLOCK(unix_signal_lock); } static gboolean g_child_watch_prepare (GSource *source, gint *timeout) { - *timeout = -1; + GChildWatchSource *child_watch_source; + + child_watch_source = (GChildWatchSource *) source; - return check_for_child_exited (source); + return child_watch_source->child_exited; } +static gboolean +g_child_watch_check (GSource *source) +{ + GChildWatchSource *child_watch_source; + + child_watch_source = (GChildWatchSource *) source; -static gboolean -g_child_watch_check (GSource *source) + return child_watch_source->child_exited; +} + +static gboolean +g_unix_signal_watch_prepare (GSource *source, + gint *timeout) { - return check_for_child_exited (source); + GUnixSignalWatchSource *unix_signal_source; + + unix_signal_source = (GUnixSignalWatchSource *) source; + + return unix_signal_source->pending; } -#endif /* G_OS_WIN32 */ +static gboolean +g_unix_signal_watch_check (GSource *source) +{ + GUnixSignalWatchSource *unix_signal_source; + + unix_signal_source = (GUnixSignalWatchSource *) source; + + return unix_signal_source->pending; +} static gboolean -g_child_watch_dispatch (GSource *source, - GSourceFunc callback, - gpointer user_data) +g_unix_signal_watch_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) { - GChildWatchSource *child_watch_source; - GChildWatchFunc child_watch_callback = (GChildWatchFunc) callback; + GUnixSignalWatchSource *unix_signal_source; - child_watch_source = (GChildWatchSource *) source; + unix_signal_source = (GUnixSignalWatchSource *) source; if (!callback) { - g_warning ("Child watch source dispatched without callback\n" + g_warning ("Unix signal source dispatched without callback\n" "You must call g_source_set_callback()."); return FALSE; } - (child_watch_callback) (child_watch_source->pid, child_watch_source->child_status, user_data); + (callback) (user_data); - /* We never keep a child watch source around as the child is gone */ - return FALSE; -} + unix_signal_source->pending = FALSE; -#ifndef G_OS_WIN32 + return TRUE; +} static void -g_child_watch_signal_handler (int signum) +ensure_unix_signal_handler_installed_unlocked (int signum) { - child_watch_count ++; + static sigset_t installed_signal_mask; + static gboolean initialized; + struct sigaction action; - if (child_watch_init_state == CHILD_WATCH_INITIALIZED_THREADED) - { - write (child_watch_wake_up_pipe[1], "B", 1); - } - else + if (!initialized) { - /* We count on the signal interrupting the poll in the same thread. - */ + sigemptyset (&installed_signal_mask); + g_get_worker_context (); + initialized = TRUE; } -} - -static void -g_child_watch_source_init_single (void) -{ - struct sigaction action; - g_assert (! g_thread_supported()); - g_assert (child_watch_init_state == CHILD_WATCH_UNINITIALIZED); + if (sigismember (&installed_signal_mask, signum)) + return; - child_watch_init_state = CHILD_WATCH_INITIALIZED_SINGLE; + sigaddset (&installed_signal_mask, signum); - action.sa_handler = g_child_watch_signal_handler; + action.sa_handler = g_unix_signal_handler; sigemptyset (&action.sa_mask); - action.sa_flags = SA_NOCLDSTOP; - sigaction (SIGCHLD, &action, NULL); + action.sa_flags = SA_RESTART | SA_NOCLDSTOP; + sigaction (signum, &action, NULL); } -G_GNUC_NORETURN static gpointer -child_watch_helper_thread (gpointer data) +GSource * +_g_main_create_unix_signal_watch (int signum) { - while (1) - { - gchar b[20]; - GSList *list; + GSource *source; + GUnixSignalWatchSource *unix_signal_source; - read (child_watch_wake_up_pipe[0], b, 20); + source = g_source_new (&g_unix_signal_funcs, sizeof (GUnixSignalWatchSource)); + unix_signal_source = (GUnixSignalWatchSource *) source; - /* We were woken up. Wake up all other contexts in all other threads */ - G_LOCK (main_context_list); - for (list = main_context_list; list; list = list->next) - { - GMainContext *context; - - context = list->data; - if (g_atomic_int_get (&context->ref_count) > 0) - /* Due to racing conditions we can find ref_count == 0, in - * that case, however, the context is still not destroyed - * and no poll can be active, otherwise the ref_count - * wouldn't be 0 */ - g_main_context_wakeup (context); - } - G_UNLOCK (main_context_list); - } + unix_signal_source->signum = signum; + unix_signal_source->pending = FALSE; + + G_LOCK (unix_signal_lock); + ensure_unix_signal_handler_installed_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; + G_UNLOCK (unix_signal_lock); + + return source; } static void -g_child_watch_source_init_multi_threaded (void) +g_unix_signal_watch_finalize (GSource *source) { - GError *error = NULL; - struct sigaction action; - - g_assert (g_thread_supported()); - - if (pipe (child_watch_wake_up_pipe) < 0) - g_error ("Cannot create wake up pipe: %s\n", g_strerror (errno)); - fcntl (child_watch_wake_up_pipe[1], F_SETFL, O_NONBLOCK | fcntl (child_watch_wake_up_pipe[1], F_GETFL)); - - /* We create a helper thread that polls on the wakeup pipe indefinitely */ - /* FIXME: Think this through for races */ - if (g_thread_create (child_watch_helper_thread, NULL, FALSE, &error) == NULL) - g_error ("Cannot create a thread to monitor child exit status: %s\n", error->message); - child_watch_init_state = CHILD_WATCH_INITIALIZED_THREADED; - - action.sa_handler = g_child_watch_signal_handler; - sigemptyset (&action.sa_mask); - action.sa_flags = SA_RESTART | SA_NOCLDSTOP; - sigaction (SIGCHLD, &action, NULL); + G_LOCK (unix_signal_lock); + unix_signal_watches = g_slist_remove (unix_signal_watches, source); + G_UNLOCK (unix_signal_lock); } static void -g_child_watch_source_init_promote_single_to_threaded (void) +g_child_watch_finalize (GSource *source) { - g_child_watch_source_init_multi_threaded (); + G_LOCK (unix_signal_lock); + unix_child_watches = g_slist_remove (unix_child_watches, source); + G_UNLOCK (unix_signal_lock); } -static void -g_child_watch_source_init (void) +#endif /* G_OS_WIN32 */ + +static gboolean +g_child_watch_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) { - if (g_thread_supported()) - { - if (child_watch_init_state == CHILD_WATCH_UNINITIALIZED) - g_child_watch_source_init_multi_threaded (); - else if (child_watch_init_state == CHILD_WATCH_INITIALIZED_SINGLE) - g_child_watch_source_init_promote_single_to_threaded (); - } - else + GChildWatchSource *child_watch_source; + GChildWatchFunc child_watch_callback = (GChildWatchFunc) callback; + + child_watch_source = (GChildWatchSource *) source; + + if (!callback) { - if (child_watch_init_state == CHILD_WATCH_UNINITIALIZED) - g_child_watch_source_init_single (); + g_warning ("Child watch source dispatched without callback\n" + "You must call g_source_set_callback()."); + return FALSE; } + + (child_watch_callback) (child_watch_source->pid, child_watch_source->child_status, user_data); + + /* We never keep a child watch source around as the child is gone */ + return FALSE; +} + +#ifndef G_OS_WIN32 + +static void +g_unix_signal_handler (int signum) +{ + unix_signal_pending[signum] = TRUE; + any_unix_signal_pending = TRUE; + + g_wakeup_signal (glib_worker_context->wakeup); } #endif /* !G_OS_WIN32 */ @@ -4185,17 +4367,22 @@ g_child_watch_source_new (GPid pid) GSource *source = g_source_new (&g_child_watch_funcs, sizeof (GChildWatchSource)); GChildWatchSource *child_watch_source = (GChildWatchSource *)source; + child_watch_source->pid = pid; + #ifdef G_OS_WIN32 child_watch_source->poll.fd = (gintptr) pid; child_watch_source->poll.events = G_IO_IN; g_source_add_poll (source, &child_watch_source->poll); #else /* G_OS_WIN32 */ - g_child_watch_source_init (); + G_LOCK (unix_signal_lock); + ensure_unix_signal_handler_installed_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; + G_UNLOCK (unix_signal_lock); #endif /* G_OS_WIN32 */ - child_watch_source->pid = pid; - return source; } @@ -4230,6 +4417,7 @@ g_child_watch_source_new (GPid pid) * * Return value: the ID (greater than 0) of the event source. * + * Rename to: g_child_watch_add * Since: 2.4 **/ guint @@ -4368,6 +4556,7 @@ g_idle_source_new (void) * You can do these steps manually if you need greater control. * * Return value: the ID (greater than 0) of the event source. + * Rename to: g_idle_add **/ guint g_idle_add_full (gint priority, @@ -4432,7 +4621,7 @@ g_idle_remove_by_data (gpointer data) /** * g_main_context_invoke: - * @context: a #GMainContext, or %NULL + * @context: (allow-none): a #GMainContext, or %NULL * @function: function to call * @data: data to pass to @function * @@ -4472,7 +4661,7 @@ g_main_context_invoke (GMainContext *context, /** * g_main_context_invoke_full: - * @context: a #GMainContext, or %NULL + * @context: (allow-none): a #GMainContext, or %NULL * @priority: the priority at which to run @function * @function: function to call * @data: data to pass to @function @@ -4539,3 +4728,40 @@ g_main_context_invoke_full (GMainContext *context, } } } + +static gpointer +glib_worker_main (gpointer data) +{ + while (TRUE) + { + g_main_context_iteration (glib_worker_context, TRUE); + +#ifdef G_OS_UNIX + if (any_unix_signal_pending) + dispatch_unix_signals (); +#endif + } + + return NULL; /* worst GCC warning message ever... */ +} + +GMainContext * +g_get_worker_context (void) +{ + static gsize initialised; + + g_thread_init_glib (); + + if (g_once_init_enter (&initialised)) + { + GError *error = NULL; + + 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_once_init_leave (&initialised, TRUE); + } + + return glib_worker_context; +}