X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=glib%2Fgmain.c;h=797aea9308daba34e9846b8ab1a46b652e037030;hb=0d1a92ca3d234a4291ef3ecbf7df2d57442a63e5;hp=f31405aa83e9b735484dc9ce7be257af67efdd26;hpb=e56498ee0b1bdac0ad3c18a1f7bff83d4a5b1323;p=platform%2Fupstream%2Fglib.git diff --git a/glib/gmain.c b/glib/gmain.c index f31405a..797aea9 100644 --- a/glib/gmain.c +++ b/glib/gmain.c @@ -49,6 +49,10 @@ #ifdef G_OS_UNIX #include "glib-unix.h" +#include +#ifdef HAVE_EVENTFD +#include +#endif #endif #include @@ -93,6 +97,10 @@ #include "gtimer.h" #endif +#include "gwakeup.h" + +#include "glib-private.h" + /** * SECTION:main * @title: The Main Event Loop @@ -167,6 +175,10 @@ * * * + * + * 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 */ @@ -183,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 @@ -191,7 +202,6 @@ struct _GMainWaiter GCond *cond; GMutex *mutex; }; -#endif typedef struct _GMainDispatch GMainDispatch; @@ -207,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; @@ -227,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; - gint64 real_time; - gboolean real_time_is_fresh; }; struct _GSourceCallback @@ -287,7 +284,6 @@ struct _GChildWatchSource #ifdef G_OS_WIN32 GPollFD poll; #else /* G_OS_WIN32 */ - gint count; gboolean child_exited; #endif /* G_OS_WIN32 */ }; @@ -302,6 +298,7 @@ struct _GUnixSignalWatchSource struct _GPollRec { GPollFD *fd; + GPollRec *prev; GPollRec *next; gint priority; }; @@ -312,15 +309,9 @@ struct _GSourcePrivate GSource *parent_source; }; -#ifdef G_THREADS_ENABLED -#define LOCK_CONTEXT(context) g_static_mutex_lock (&context->mutex) -#define UNLOCK_CONTEXT(context) g_static_mutex_unlock (&context->mutex) +#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 && \ @@ -356,9 +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 void _g_main_wake_up_all_contexts (void); static gboolean g_timeout_prepare (GSource *source, gint *timeout); @@ -372,10 +360,9 @@ 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 void init_unix_signal_wakeup_state_unlocked (void); -static void init_unix_signal_wakeup_state (void); static gboolean g_unix_signal_watch_prepare (GSource *source, gint *timeout); static gboolean g_unix_signal_watch_check (GSource *source); @@ -391,44 +378,25 @@ 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 -/* The UNIX signal pipe contains a single byte specifying which - * signal was received. - */ -#define _UNIX_SIGNAL_PIPE_SIGCHLD_CHAR 'C' -#define _UNIX_SIGNAL_PIPE_SIGHUP_CHAR 'H' -#define _UNIX_SIGNAL_PIPE_SIGINT_CHAR 'I' -#define _UNIX_SIGNAL_PIPE_SIGTERM_CHAR 'T' -/* Guards all the data below */ + +/* 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); -enum { - UNIX_SIGNAL_UNINITIALIZED = 0, - UNIX_SIGNAL_INITIALIZED_SINGLE, - UNIX_SIGNAL_INITIALIZED_THREADED -}; -static gint unix_signal_init_state = UNIX_SIGNAL_UNINITIALIZED; -typedef struct { - gboolean sigchld_handler_installed : 1; - gboolean sighup_handler_installed : 1; - gboolean sigint_handler_installed : 1; - gboolean sigterm_handler_installed : 1; - - /* These are only used in the UNIX_SIGNAL_INITIALIZED_SINGLE case */ - gboolean sighup_delivered : 1; - gboolean sigint_delivered : 1; - gboolean sigterm_delivered : 1; -} UnixSignalState; -static UnixSignalState unix_signal_state; -static gint unix_signal_wake_up_pipe[2]; -GSList *unix_signal_watches; - -/* Not guarded ( FIXME should it be? ) */ -static gint child_watch_count = 1; +static GSList *unix_signal_watches; +static GSList *unix_child_watches; static GSourceFuncs g_unix_signal_funcs = { @@ -454,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 = @@ -520,80 +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) -{ - GError *error = NULL; - -# ifndef G_OS_WIN32 - if (context->wake_up_pipe[0] != -1) - return; - - if (!g_unix_pipe_flags (context->wake_up_pipe, FD_CLOEXEC, &error)) - g_error ("Cannot create pipe main loop wake-up: %s", error->message); - - 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: @@ -605,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; @@ -648,15 +561,10 @@ g_main_context_new (void) context->pending_dispatches = g_ptr_array_new (); context->time_is_fresh = FALSE; - context->real_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); @@ -679,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) @@ -820,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 **/ @@ -993,10 +901,11 @@ g_source_attach (GSource *source, 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); @@ -1123,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) @@ -1621,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); @@ -1778,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, @@ -1817,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, @@ -1865,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, @@ -2062,10 +1971,13 @@ g_get_real_time (void) * 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 * @@ -2077,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); @@ -2170,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: @@ -2290,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 */ @@ -2554,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; @@ -2578,9 +2488,6 @@ g_main_context_acquire (GMainContext *context) UNLOCK_CONTEXT (context); return result; -#else /* !G_THREADS_ENABLED */ - return TRUE; -#endif /* G_THREADS_ENABLED */ } /** @@ -2595,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 (); @@ -2609,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) @@ -2624,7 +2529,6 @@ g_main_context_release (GMainContext *context) } UNLOCK_CONTEXT (context); -#endif /* G_THREADS_ENABLED */ } /** @@ -2647,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; @@ -2655,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); @@ -2694,9 +2597,6 @@ g_main_context_wait (GMainContext *context, UNLOCK_CONTEXT (context); return result; -#else /* !G_THREADS_ENABLED */ - return TRUE; -#endif /* G_THREADS_ENABLED */ } /** @@ -2726,7 +2626,6 @@ g_main_context_prepare (GMainContext *context, LOCK_CONTEXT (context); context->time_is_fresh = FALSE; - context->real_time_is_fresh = FALSE; if (context->in_check_or_prepare) { @@ -2736,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) @@ -2843,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. @@ -2890,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->real_time_is_fresh = FALSE; - } + context->time_is_fresh = FALSE; } UNLOCK_CONTEXT (context); @@ -2913,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. @@ -2941,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 @@ -2961,7 +2837,6 @@ g_main_context_check (GMainContext *context, UNLOCK_CONTEXT (context); return FALSE; } -#endif /* G_THREADS_ENABLED */ pollrec = context->poll_records; i = 0; @@ -3068,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; @@ -3085,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) { @@ -3120,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); @@ -3127,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); @@ -3196,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. @@ -3280,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); @@ -3306,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) { @@ -3321,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) { @@ -3337,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); } @@ -3362,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); } @@ -3395,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) @@ -3506,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 @@ -3531,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 */ @@ -3539,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); } /** @@ -3591,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 = pollrec->next; + context->poll_records = nextrec; + + if (nextrec != NULL) + nextrec->prev = prevrec; + else + 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 **/ @@ -3639,24 +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->real_time_is_fresh) - { - context->real_time = g_get_real_time (); - context->real_time_is_fresh = TRUE; - } - - timeval->tv_sec = context->real_time / 1000000; - timeval->tv_usec = context->real_time % 1000000; - - UNLOCK_CONTEXT (context); + g_get_current_time (timeval); } /** @@ -3757,48 +3602,6 @@ g_main_context_get_poll_func (GMainContext *context) return result; } -static void -_g_main_wake_up_all_contexts (void) -{ - GSList *list; - - /* 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); -} - - -/* 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 @@ -3811,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); } /** @@ -3824,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. * @@ -3840,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; } @@ -3957,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 **/ @@ -3985,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 @@ -4030,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, @@ -4085,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 @@ -4136,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 @@ -4175,15 +3989,18 @@ 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. * * Since: 2.14 @@ -4210,7 +4027,6 @@ g_child_watch_prepare (GSource *source, return FALSE; } - static gboolean g_child_watch_check (GSource *source) { @@ -4246,101 +4062,149 @@ 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; - return check_for_child_exited (source); -} + child_watch_source = (GChildWatchSource *) source; -static gboolean -g_child_watch_check (GSource *source) -{ - return check_for_child_exited (source); + return child_watch_source->child_exited; } static gboolean -check_for_signal_delivery (GSource *source) +g_child_watch_check (GSource *source) { - GUnixSignalWatchSource *unix_signal_source = (GUnixSignalWatchSource*) source; - gboolean delivered; + GChildWatchSource *child_watch_source; - G_LOCK (unix_signal_lock); - if (unix_signal_init_state == UNIX_SIGNAL_INITIALIZED_SINGLE) - { - switch (unix_signal_source->signum) - { - case SIGHUP: - delivered = unix_signal_state.sighup_delivered; - break; - case SIGINT: - delivered = unix_signal_state.sigint_delivered; - break; - case SIGTERM: - delivered = unix_signal_state.sigterm_delivered; - break; - default: - g_assert_not_reached (); - delivered = FALSE; - break; - } - } - else - { - g_assert (unix_signal_init_state == UNIX_SIGNAL_INITIALIZED_THREADED); - delivered = unix_signal_source->pending; - } - G_UNLOCK (unix_signal_lock); + child_watch_source = (GChildWatchSource *) source; - return delivered; + return child_watch_source->child_exited; } static gboolean g_unix_signal_watch_prepare (GSource *source, gint *timeout) { - *timeout = -1; + GUnixSignalWatchSource *unix_signal_source; - return check_for_signal_delivery (source); + unix_signal_source = (GUnixSignalWatchSource *) source; + + return unix_signal_source->pending; } -static gboolean +static gboolean g_unix_signal_watch_check (GSource *source) { - return check_for_signal_delivery (source); + GUnixSignalWatchSource *unix_signal_source; + + unix_signal_source = (GUnixSignalWatchSource *) source; + + return unix_signal_source->pending; } static gboolean @@ -4360,29 +4224,8 @@ g_unix_signal_watch_dispatch (GSource *source, } (callback) (user_data); - - G_LOCK (unix_signal_lock); - if (unix_signal_init_state == UNIX_SIGNAL_INITIALIZED_SINGLE) - { - switch (unix_signal_source->signum) - { - case SIGHUP: - unix_signal_state.sighup_delivered = FALSE; - break; - case SIGINT: - unix_signal_state.sigint_delivered = FALSE; - break; - case SIGTERM: - unix_signal_state.sigterm_delivered = FALSE; - break; - } - } - else - { - g_assert (unix_signal_init_state == UNIX_SIGNAL_INITIALIZED_THREADED); - unix_signal_source->pending = FALSE; - } - G_UNLOCK (unix_signal_lock); + + unix_signal_source->pending = FALSE; return TRUE; } @@ -4390,32 +4233,25 @@ g_unix_signal_watch_dispatch (GSource *source, static void ensure_unix_signal_handler_installed_unlocked (int signum) { + static sigset_t installed_signal_mask; + static gboolean initialized; struct sigaction action; - switch (signum) + if (!initialized) { - case SIGHUP: - if (unix_signal_state.sighup_handler_installed) - return; - unix_signal_state.sighup_handler_installed = TRUE; - break; - case SIGINT: - if (unix_signal_state.sigint_handler_installed) - return; - unix_signal_state.sigint_handler_installed = TRUE; - break; - case SIGTERM: - if (unix_signal_state.sigterm_handler_installed) - return; - unix_signal_state.sigterm_handler_installed = TRUE; - break; + sigemptyset (&installed_signal_mask); + g_get_worker_context (); + initialized = TRUE; } - init_unix_signal_wakeup_state_unlocked (); + 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 = 0; + action.sa_flags = SA_RESTART | SA_NOCLDSTOP; sigaction (signum, &action, NULL); } @@ -4425,8 +4261,6 @@ _g_main_create_unix_signal_watch (int signum) GSource *source; GUnixSignalWatchSource *unix_signal_source; - init_unix_signal_wakeup_state (); - source = g_source_new (&g_unix_signal_funcs, sizeof (GUnixSignalWatchSource)); unix_signal_source = (GUnixSignalWatchSource *) source; @@ -4436,12 +4270,15 @@ _g_main_create_unix_signal_watch (int signum) 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 +static void g_unix_signal_watch_finalize (GSource *source) { G_LOCK (unix_signal_lock); @@ -4449,6 +4286,14 @@ g_unix_signal_watch_finalize (GSource *source) G_UNLOCK (unix_signal_lock); } +static void +g_child_watch_finalize (GSource *source) +{ + G_LOCK (unix_signal_lock); + unix_child_watches = g_slist_remove (unix_child_watches, source); + G_UNLOCK (unix_signal_lock); +} + #endif /* G_OS_WIN32 */ static gboolean @@ -4479,200 +4324,10 @@ g_child_watch_dispatch (GSource *source, static void g_unix_signal_handler (int signum) { - if (signum == SIGCHLD) - child_watch_count ++; - - if (unix_signal_init_state == UNIX_SIGNAL_INITIALIZED_THREADED) - { - char buf[1]; - switch (signum) - { - case SIGCHLD: - buf[0] = _UNIX_SIGNAL_PIPE_SIGCHLD_CHAR; - break; - case SIGHUP: - buf[0] = _UNIX_SIGNAL_PIPE_SIGHUP_CHAR; - break; - case SIGINT: - buf[0] = _UNIX_SIGNAL_PIPE_SIGINT_CHAR; - break; - case SIGTERM: - buf[0] = _UNIX_SIGNAL_PIPE_SIGTERM_CHAR; - break; - default: - /* Shouldn't happen */ - return; - } - write (unix_signal_wake_up_pipe[1], buf, 1); - } - else - { - /* We count on the signal interrupting the poll in the same thread. */ - switch (signum) - { - case SIGCHLD: - /* Nothing to do - the handler will call waitpid() */ - break; - case SIGHUP: - unix_signal_state.sighup_delivered = TRUE; - break; - case SIGINT: - unix_signal_state.sigint_delivered = TRUE; - break; - case SIGTERM: - unix_signal_state.sigterm_delivered = TRUE; - break; - default: - g_assert_not_reached (); - break; - } - } -} - -static void -deliver_unix_signal (int signum) -{ - GSList *iter; - g_assert (signum == SIGHUP || signum == SIGINT || signum == SIGTERM); - - G_LOCK (unix_signal_lock); - for (iter = unix_signal_watches; iter; iter = iter->next) - { - GUnixSignalWatchSource *source = iter->data; - - if (source->signum != signum) - continue; - - source->pending = TRUE; - } - G_UNLOCK (unix_signal_lock); -} - -static gpointer unix_signal_helper_thread (gpointer data) G_GNUC_NORETURN; - -/* - * This thread is created whenever anything in GLib needs - * to deal with UNIX signals; at present, just SIGCHLD - * from g_child_watch_source_new(). - * - * Note: We could eventually make this thread a more public interface - * and allow e.g. GDBus to use it instead of its own worker thread. - */ -static gpointer -unix_signal_helper_thread (gpointer data) -{ - while (1) - { - gchar b[128]; - ssize_t i, bytes_read; - gboolean sigterm_received = FALSE; - gboolean sigint_received = FALSE; - gboolean sighup_received = FALSE; - - bytes_read = read (unix_signal_wake_up_pipe[0], b, sizeof (b)); - if (bytes_read < 0) - { - g_warning ("Failed to read from child watch wake up pipe: %s", - strerror (errno)); - /* Not much we can do here sanely; just wait a second and hope - * it was transient. - */ - g_usleep (G_USEC_PER_SEC); - continue; - } - for (i = 0; i < bytes_read; i++) - { - switch (b[i]) - { - case _UNIX_SIGNAL_PIPE_SIGCHLD_CHAR: - /* The child watch source will call waitpid() in its - * prepare() and check() methods; however, we don't - * know which pid exited, so we need to wake up - * all contexts. Note: actually we could get the pid - * from the "siginfo_t" via the handler, but to pass - * that info down the pipe would require a more structured - * data stream (as opposed to a single byte). - */ - break; - case _UNIX_SIGNAL_PIPE_SIGTERM_CHAR: - sigterm_received = TRUE; - break; - case _UNIX_SIGNAL_PIPE_SIGHUP_CHAR: - sighup_received = TRUE; - break; - case _UNIX_SIGNAL_PIPE_SIGINT_CHAR: - sigint_received = TRUE; - break; - default: - g_warning ("Invalid char '%c' read from child watch pipe", b[i]); - break; - } - if (sigterm_received) - deliver_unix_signal (SIGTERM); - if (sigint_received) - deliver_unix_signal (SIGINT); - if (sighup_received) - deliver_unix_signal (SIGHUP); - _g_main_wake_up_all_contexts (); - } - } -} - -static void -init_unix_signal_wakeup_state_unlocked (void) -{ - GError *error = NULL; - - if (!g_thread_supported ()) - { - /* There is nothing to do for initializing in the non-threaded - * case. - */ - if (unix_signal_init_state == UNIX_SIGNAL_UNINITIALIZED) - unix_signal_init_state = UNIX_SIGNAL_INITIALIZED_SINGLE; - return; - } - - if (unix_signal_init_state == UNIX_SIGNAL_INITIALIZED_THREADED) - return; - - if (!g_unix_pipe_flags (unix_signal_wake_up_pipe, FD_CLOEXEC, &error)) - g_error ("Cannot create UNIX signal wake up pipe: %s\n", error->message); - fcntl (unix_signal_wake_up_pipe[1], F_SETFL, O_NONBLOCK | fcntl (unix_signal_wake_up_pipe[1], F_GETFL)); - - /* We create a helper thread that polls on the wakeup pipe indefinitely */ - if (g_thread_create (unix_signal_helper_thread, NULL, FALSE, &error) == NULL) - g_error ("Cannot create a thread to monitor UNIX signals: %s\n", error->message); + unix_signal_pending[signum] = TRUE; + any_unix_signal_pending = TRUE; - unix_signal_init_state = UNIX_SIGNAL_INITIALIZED_THREADED; -} - -static void -init_unix_signal_wakeup_state (void) -{ - G_LOCK (unix_signal_lock); - - init_unix_signal_wakeup_state_unlocked (); - - G_UNLOCK (unix_signal_lock); -} - -static void -g_child_watch_source_init (void) -{ - init_unix_signal_wakeup_state (); - - G_LOCK (unix_signal_lock); - if (!unix_signal_state.sigchld_handler_installed) - { - struct sigaction action; - action.sa_handler = g_unix_signal_handler; - sigemptyset (&action.sa_mask); - action.sa_flags = SA_RESTART | SA_NOCLDSTOP; - sigaction (SIGCHLD, &action, NULL); - unix_signal_state.sigchld_handler_installed = TRUE; - } - G_UNLOCK (unix_signal_lock); + g_wakeup_signal (glib_worker_context->wakeup); } #endif /* !G_OS_WIN32 */ @@ -4712,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; } @@ -4757,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 @@ -4895,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, @@ -4959,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 * @@ -4999,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 @@ -5066,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; +}