#ifdef G_OS_UNIX
#include "glib-unix.h"
+#include <pthread.h>
#ifdef HAVE_EVENTFD
#include <sys/eventfd.h>
#endif
#include "gqueue.h"
#include "gstrfuncs.h"
#include "gtestutils.h"
-#include "gthreadprivate.h"
#ifdef G_OS_WIN32
#include "gwin32.h"
#include "gwakeup.h"
-#include "glibprivate.h"
+#include "glib-private.h"
/**
* SECTION:main
* <graphic fileref="mainloop-states.gif" format="GIF"></graphic>
* </figure>
* </refsect2>
+ *
+ * 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 */
/* The following lock is used for both the list of sources
* and the list of poll records
*/
- GStaticMutex mutex;
- GCond *cond;
+ GMutex mutex;
+ GCond cond;
GThread *owner;
guint owner_count;
GSList *waiters;
GWakeup *wakeup;
GPollFD wake_up_rec;
- gboolean poll_waiting;
/* Flag indicating whether the set of fd's changed during a poll */
gboolean poll_changed;
GSource *parent_source;
};
-#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 ()
#define SOURCE_DESTROYED(source) (((source)->flags & G_HOOK_FLAG_ACTIVE) == 0)
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);
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;
source = next;
}
- g_static_mutex_free (&context->mutex);
+ 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);
g_wakeup_free (context->wakeup);
-
- if (context->cond != NULL)
- g_cond_free (context->cond);
+ g_cond_clear (&context->cond);
g_free (context);
}
+#ifdef G_OS_UNIX
static void
-g_main_context_init_pipe (GMainContext *context)
+g_main_context_forked (void)
{
- 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_main_context_fork_detected = TRUE;
}
+#endif
/**
* g_main_context_new:
GMainContext *
g_main_context_new (void)
{
+ static gsize initialised;
GMainContext *context;
- g_thread_init_glib ();
-
- context = g_new0 (GMainContext, 1);
-
+ if (g_once_init_enter (&initialised))
+ {
#ifdef G_MAIN_POLL_DEBUG
- {
- static gboolean beenhere = FALSE;
-
- if (!beenhere)
- {
- if (getenv ("G_MAIN_POLL_DEBUG") != NULL)
- _g_main_poll_debug = TRUE;
- beenhere = TRUE;
- }
- }
+ if (getenv ("G_MAIN_POLL_DEBUG") != NULL)
+ _g_main_poll_debug = TRUE;
+#endif
+
+#ifdef G_OS_UNIX
+ pthread_atfork (NULL, NULL, g_main_context_forked);
#endif
- g_static_mutex_init (&context->mutex);
+ g_once_init_leave (&initialised, TRUE);
+ }
+
+ context = g_new0 (GMainContext, 1);
+
+ g_mutex_init (&context->mutex);
+ g_cond_init (&context->cond);
context->owner = NULL;
context->waiters = NULL;
context->time_is_fresh = FALSE;
- g_main_context_init_pipe (context);
+ 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);
return default_main_context;
}
-static GStaticPrivate thread_context_stack = G_STATIC_PRIVATE_INIT;
-
static void
free_context_stack (gpointer data)
{
context = g_queue_pop_head (stack);
g_main_context_release (context);
if (context)
- g_main_context_unref (context);
+ g_main_context_unref (context);
}
g_queue_free (stack);
}
+static GPrivate thread_context_stack = G_PRIVATE_INIT (free_context_stack);
+
/**
* g_main_context_push_thread_default:
* @context: a #GMainContext, or %NULL for the global default context
else if (context)
g_main_context_ref (context);
- stack = g_static_private_get (&thread_context_stack);
+ stack = g_private_get (&thread_context_stack);
if (!stack)
{
stack = g_queue_new ();
- g_static_private_set (&thread_context_stack, stack,
- free_context_stack);
+ g_private_set (&thread_context_stack, stack);
}
g_queue_push_head (stack, context);
if (context == g_main_context_default ())
context = NULL;
- stack = g_static_private_get (&thread_context_stack);
+ stack = g_private_get (&thread_context_stack);
g_return_if_fail (stack != NULL);
g_return_if_fail (g_queue_peek_head (stack) == context);
*
* Gets the thread-default #GMainContext for this thread. Asynchronous
* operations that want to be able to be run in contexts other than
- * the default one should call this method to get a #GMainContext to
- * add their #GSource<!-- -->s to. (Note that even in single-threaded
+ * the default one should call this method or
+ * g_main_context_ref_thread_default() to get a #GMainContext to add
+ * their #GSource<!-- -->s to. (Note that even in single-threaded
* programs applications may sometimes want to temporarily push a
* non-default context, so it is not safe to assume that this will
- * always return %NULL if threads are not initialized.)
+ * always return %NULL if you are running in the default thread.)
+ *
+ * If you need to hold a reference on the context, use
+ * g_main_context_ref_thread_default() instead.
*
* Returns: (transfer none): the thread-default #GMainContext, or
* %NULL if the thread-default context is the global default context.
{
GQueue *stack;
- stack = g_static_private_get (&thread_context_stack);
+ stack = g_private_get (&thread_context_stack);
if (stack)
return g_queue_peek_head (stack);
else
return NULL;
}
+/**
+ * g_main_context_ref_thread_default:
+ *
+ * Gets the thread-default #GMainContext for this thread, as with
+ * g_main_context_get_thread_default(), but also adds a reference to
+ * it with g_main_context_ref(). In addition, unlike
+ * g_main_context_get_thread_default(), if the thread-default context
+ * is the global default context, this will return that #GMainContext
+ * (with a ref added to it) rather than returning %NULL.
+ *
+ * Returns: (transfer full): the thread-default #GMainContext. Unref
+ * with g_main_context_unref() when you are done with it.
+ *
+ * Since: 2.32
+ */
+GMainContext *
+g_main_context_ref_thread_default (void)
+{
+ GMainContext *context;
+
+ context = g_main_context_get_thread_default ();
+ if (!context)
+ context = g_main_context_default ();
+ return g_main_context_ref (context);
+}
+
/* Hooks for adding to the main loop */
/**
result = g_source_attach_unlocked (source, context);
- /* Now wake up the main loop if it is waiting in the poll() */
- g_main_context_wakeup_unlocked (context);
+ /* If another thread has acquired the context, wake it up since it
+ * might be in poll() right now.
+ */
+ if (context->owner && context->owner != G_THREAD_SELF)
+ g_wakeup_signal (context->wakeup);
UNLOCK_CONTEXT (context);
{
#ifdef HAVE_CLOCK_GETTIME
/* librt clock_gettime() is our first choice */
- {
-#ifdef HAVE_MONOTONIC_CLOCK
- static volatile gsize clockid = 0;
+ struct timespec ts;
+
+#ifdef CLOCK_MONOTONIC
+ clock_gettime (CLOCK_MONOTONIC, &ts);
#else
- static clockid_t clockid = CLOCK_REALTIME;
-#endif
- struct timespec ts;
-
-#ifdef HAVE_MONOTONIC_CLOCK
- if (g_once_init_enter (&clockid))
- {
- clockid_t best_clockid;
-
- if (sysconf (_SC_MONOTONIC_CLOCK) >= 0)
- best_clockid = CLOCK_MONOTONIC;
- else
- best_clockid = CLOCK_REALTIME;
- g_once_init_leave (&clockid, (gsize)best_clockid);
- }
+ clock_gettime (CLOCK_REALTIME, &ts);
#endif
- clock_gettime (clockid, &ts);
-
- /* In theory monotonic time can have any epoch.
- *
- * glib presently assumes the following:
- *
- * 1) The epoch comes some time after the birth of Jesus of Nazareth, but
- * not more than 10000 years later.
- *
- * 2) The current time also falls sometime within this range.
- *
- * These two reasonable assumptions leave us with a maximum deviation from
- * the epoch of 10000 years, or 315569520000000000 seconds.
- *
- * If we restrict ourselves to this range then the number of microseconds
- * will always fit well inside the constraints of a int64 (by a factor of
- * about 29).
- *
- * If you actually hit the following assertion, probably you should file a
- * bug against your operating system for being excessively silly.
- **/
- g_assert (G_GINT64_CONSTANT (-315569520000000000) < ts.tv_sec &&
- ts.tv_sec < G_GINT64_CONSTANT (315569520000000000));
-
- return (((gint64) ts.tv_sec) * 1000000) + (ts.tv_nsec / 1000);
- }
-#else
+ /* In theory monotonic time can have any epoch.
+ *
+ * glib presently assumes the following:
+ *
+ * 1) The epoch comes some time after the birth of Jesus of Nazareth, but
+ * not more than 10000 years later.
+ *
+ * 2) The current time also falls sometime within this range.
+ *
+ * These two reasonable assumptions leave us with a maximum deviation from
+ * the epoch of 10000 years, or 315569520000000000 seconds.
+ *
+ * If we restrict ourselves to this range then the number of microseconds
+ * will always fit well inside the constraints of a int64 (by a factor of
+ * about 29).
+ *
+ * If you actually hit the following assertion, probably you should file a
+ * bug against your operating system for being excessively silly.
+ **/
+ g_assert (G_GINT64_CONSTANT (-315569520000000000) < ts.tv_sec &&
+ ts.tv_sec < G_GINT64_CONSTANT (315569520000000000));
+
+ return (((gint64) ts.tv_sec) * 1000000) + (ts.tv_nsec / 1000);
+
+#else /* !HAVE_CLOCK_GETTIME */
+
/* It may look like we are discarding accuracy on Windows (since its
* current time is expressed in 100s of nanoseconds) but according to
* many sources, the time is only updated 64 times per second, so
* microsecond accuracy is more than enough.
*/
- {
- GTimeVal tv;
+ GTimeVal tv;
- g_get_current_time (&tv);
+ g_get_current_time (&tv);
- return (((gint64) tv.tv_sec) * 1000000) + tv.tv_usec;
- }
+ return (((gint64) tv.tv_sec) * 1000000) + tv.tv_usec;
#endif
}
static GMainDispatch *
get_dispatch (void)
{
- static GStaticPrivate depth_private = G_STATIC_PRIVATE_INIT;
- GMainDispatch *dispatch = g_static_private_get (&depth_private);
+ static GPrivate depth_private = G_PRIVATE_INIT (g_main_dispatch_free);
+ GMainDispatch *dispatch;
+
+ dispatch = g_private_get (&depth_private);
+
if (!dispatch)
{
dispatch = g_slice_new0 (GMainDispatch);
- g_static_private_set (&depth_private, dispatch, g_main_dispatch_free);
+ g_private_set (&depth_private, dispatch);
}
return dispatch;
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)
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);
return FALSE;
}
- 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;
-
#if 0
/* If recursing, finish up current dispatch, before starting over */
if (context->pending_dispatches)
if (context->wake_up_rec.events)
g_wakeup_acknowledge (context->wakeup);
- context->poll_waiting = FALSE;
-
/* If the set of poll file descriptors changed, bail out
* and let the main loop rerun
*/
if (!block)
return FALSE;
- if (!context->cond)
- context->cond = g_cond_new ();
-
got_ownership = g_main_context_wait (context,
- context->cond,
- g_static_mutex_get_mutex (&context->mutex));
+ &context->cond,
+ &context->mutex);
if (!got_ownership)
return FALSE;
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);
if (!loop->is_running)
loop->is_running = TRUE;
- if (!loop->context->cond)
- loop->context->cond = g_cond_new ();
-
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->cond,
+ &loop->context->mutex);
if (!loop->is_running)
{
LOCK_CONTEXT (loop->context);
loop->is_running = FALSE;
- g_main_context_wakeup_unlocked (loop->context);
+ g_wakeup_signal (loop->context->wakeup);
- if (loop->context->cond)
- g_cond_broadcast (loop->context->cond);
+ g_cond_broadcast (&loop->context->cond);
UNLOCK_CONTEXT (loop->context);
}
context->poll_changed = TRUE;
/* Now wake up the main loop if it is waiting in the poll() */
- g_main_context_wakeup_unlocked (context);
+ g_wakeup_signal (context->wakeup);
}
/**
context->poll_changed = TRUE;
/* Now wake up the main loop if it is waiting in the poll() */
- g_main_context_wakeup_unlocked (context);
+ g_wakeup_signal (context->wakeup);
}
/**
return result;
}
-/* HOLDS: context's lock */
-/* Wake the main loop up from a poll() */
-static void
-g_main_context_wakeup_unlocked (GMainContext *context)
-{
- if (context->poll_waiting)
- {
- context->poll_waiting = FALSE;
- g_wakeup_signal (context->wakeup);
- }
-}
-
/**
* g_main_context_wakeup:
* @context: a #GMainContext
{
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);
}
/**
return FALSE;
}
-
static gboolean
g_child_watch_check (GSource *source)
{
return child_exited;
}
+static void
+g_child_watch_finalize (GSource *source)
+{
+}
+
#else /* G_OS_WIN32 */
static void
if (!initialized)
{
sigemptyset (&installed_signal_mask);
- glib_get_worker_context ();
+ g_get_worker_context ();
initialized = TRUE;
}
return source;
}
-static void
+static void
g_unix_signal_watch_finalize (GSource *source)
{
G_LOCK (unix_signal_lock);
G_UNLOCK (unix_signal_lock);
}
-#endif /* G_OS_WIN32 */
-
static void
g_child_watch_finalize (GSource *source)
{
G_UNLOCK (unix_signal_lock);
}
+#endif /* G_OS_WIN32 */
+
static gboolean
g_child_watch_dispatch (GSource *source,
GSourceFunc callback,
{
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 *
-glib_get_worker_context (void)
+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_create (glib_worker_main, NULL, FALSE, &error) == NULL)
+ 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);