blob: 6d56ab3e8df1d1288562caab539cedd8a75a1088 [file] [log] [blame]
/* GLIB - Library of useful routines for C programming
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* gmain.c: Main loop abstraction, timeouts, and idle functions
* Copyright 1998 Owen Taylor
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GLib Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GLib at ftp://ftp.gtk.org/pub/gtk/.
*/
/*
* MT safe
*/
#include "config.h"
#include "glibconfig.h"
#include "glib_trace.h"
/* Uncomment the next line (and the corresponding line in gpoll.c) to
* enable debugging printouts if the environment variable
* G_MAIN_POLL_DEBUG is set to some value.
*/
/* #define G_MAIN_POLL_DEBUG */
#ifdef _WIN32
/* Always enable debugging printout on Windows, as it is more often
* needed there...
*/
#define G_MAIN_POLL_DEBUG
#endif
#ifdef G_OS_UNIX
#include "glib-unix.h"
#include <pthread.h>
#ifdef HAVE_EVENTFD
#include <sys/eventfd.h>
#endif
#endif
#include <signal.h>
#include <sys/types.h>
#include <time.h>
#include <stdlib.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif /* HAVE_SYS_TIME_H */
#ifdef G_OS_UNIX
#include <unistd.h>
#endif /* G_OS_UNIX */
#include <errno.h>
#include <string.h>
#ifdef HAVE_PIDFD
#include <sys/syscall.h>
#include <sys/wait.h>
#include <linux/wait.h> /* P_PIDFD */
#ifndef W_EXITCODE
#define W_EXITCODE(ret, sig) ((ret) << 8 | (sig))
#endif
#ifndef W_STOPCODE
#define W_STOPCODE(sig) ((sig) << 8 | 0x7f)
#endif
#ifndef WCOREFLAG
/* musl doesn’t define WCOREFLAG while glibc does. Unfortunately, there’s no way
* to detect we’re building against musl, so just define it and hope.
* See https://git.musl-libc.org/cgit/musl/tree/include/sys/wait.h#n51 */
#define WCOREFLAG 0x80
#endif
#ifndef __W_CONTINUED
/* Same as above, for musl */
#define __W_CONTINUED 0xffff
#endif
#endif /* HAVE_PIDFD */
#ifdef G_OS_WIN32
#define STRICT
#include <windows.h>
#endif /* G_OS_WIN32 */
#ifdef HAVE_MACH_MACH_TIME_H
#include <mach/mach_time.h>
#endif
#include "glib_trace.h"
#include "gmain.h"
#include "garray.h"
#include "giochannel.h"
#include "ghash.h"
#include "ghook.h"
#include "gqueue.h"
#include "gstrfuncs.h"
#include "gtestutils.h"
#include "gthreadprivate.h"
#include "gtrace-private.h"
#ifdef G_OS_WIN32
#include "gwin32.h"
#endif
#ifdef G_MAIN_POLL_DEBUG
#include "gtimer.h"
#endif
#include "gwakeup.h"
#include "gmain-internal.h"
#include "glib-init.h"
#include "glib-private.h"
/* Types */
typedef struct _GIdleSource GIdleSource;
typedef struct _GTimeoutSource GTimeoutSource;
typedef struct _GChildWatchSource GChildWatchSource;
typedef struct _GUnixSignalWatchSource GUnixSignalWatchSource;
typedef struct _GPollRec GPollRec;
typedef struct _GSourceCallback GSourceCallback;
typedef enum
{
G_SOURCE_READY = 1 << G_HOOK_FLAG_USER_SHIFT,
G_SOURCE_CAN_RECURSE = 1 << (G_HOOK_FLAG_USER_SHIFT + 1),
G_SOURCE_BLOCKED = 1 << (G_HOOK_FLAG_USER_SHIFT + 2)
} GSourceFlags;
typedef struct _GSourceList GSourceList;
struct _GSourceList
{
GList link;
GSource *head, *tail;
gint priority;
};
typedef struct _GMainWaiter GMainWaiter;
struct _GMainWaiter
{
GCond *cond;
GMutex *mutex;
};
typedef struct _GMainDispatch GMainDispatch;
struct _GMainDispatch
{
gint depth;
GSource *source;
};
#ifdef G_MAIN_POLL_DEBUG
gboolean _g_main_poll_debug = FALSE;
#endif
struct _GMainContext
{
/* The following lock is used for both the list of sources
* and the list of poll records
*/
GMutex mutex;
GCond cond;
GThread *owner;
guint owner_count;
GMainContextFlags flags;
GSList *waiters;
gint ref_count; /* (atomic) */
GHashTable *sources; /* guint -> GSource */
GPtrArray *pending_dispatches;
gint timeout; /* Timeout for current iteration */
guint next_id;
GQueue source_lists;
gint in_check_or_prepare;
GPollRec *poll_records;
guint n_poll_records;
GPollFD *cached_poll_array;
guint cached_poll_array_size;
GWakeup *wakeup;
GPollFD wake_up_rec;
/* Flag indicating whether the set of fd's changed during a poll */
gboolean poll_changed;
GPollFunc poll_func;
gint64 time;
gboolean time_is_fresh;
};
struct _GSourceCallback
{
gint ref_count; /* (atomic) */
GSourceFunc func;
gpointer data;
GDestroyNotify notify;
};
struct _GMainLoop
{
GMainContext *context;
gboolean is_running; /* (atomic) */
gint ref_count; /* (atomic) */
};
struct _GIdleSource
{
GSource source;
gboolean one_shot;
};
struct _GTimeoutSource
{
GSource source;
/* Measured in seconds if 'seconds' is TRUE, or milliseconds otherwise. */
guint interval;
gboolean seconds;
gboolean one_shot;
};
struct _GChildWatchSource
{
GSource source;
GPid pid;
/* @poll is always used on Windows.
* On Unix, poll.fd will be negative if PIDFD is unavailable. */
GPollFD poll;
#ifndef G_OS_WIN32
gboolean child_maybe_exited; /* (atomic) */
#endif /* G_OS_WIN32 */
};
struct _GUnixSignalWatchSource
{
GSource source;
int signum;
gboolean pending; /* (atomic) */
};
struct _GPollRec
{
GPollFD *fd;
GPollRec *prev;
GPollRec *next;
gint priority;
};
struct _GSourcePrivate
{
GSList *child_sources;
GSource *parent_source;
gint64 ready_time;
/* This is currently only used on UNIX, but we always declare it (and
* let it remain empty on Windows) to avoid #ifdef all over the place.
*/
GSList *fds;
GSourceDisposeFunc dispose;
gboolean static_name;
};
typedef struct _GSourceIter
{
GMainContext *context;
gboolean may_modify;
GList *current_list;
GSource *source;
} GSourceIter;
#define LOCK_CONTEXT(context) g_mutex_lock (&context->mutex)
#define UNLOCK_CONTEXT(context) g_mutex_unlock (&context->mutex)
#define G_THREAD_SELF g_thread_self ()
#define SOURCE_DESTROYED(source) (((source)->flags & G_HOOK_FLAG_ACTIVE) == 0)
#define SOURCE_BLOCKED(source) (((source)->flags & G_SOURCE_BLOCKED) != 0)
/* Forward declarations */
static void g_source_unref_internal (GSource *source,
GMainContext *context,
gboolean have_lock);
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_child_source_remove_internal (GSource *child_source,
GMainContext *context);
static gboolean g_main_context_acquire_unlocked (GMainContext *context);
static void g_main_context_release_unlocked (GMainContext *context);
static gboolean g_main_context_prepare_unlocked (GMainContext *context,
gint *priority);
static gint g_main_context_query_unlocked (GMainContext *context,
gint max_priority,
gint *timeout,
GPollFD *fds,
gint n_fds);
static gboolean g_main_context_check_unlocked (GMainContext *context,
gint max_priority,
GPollFD *fds,
gint n_fds);
static void g_main_context_dispatch_unlocked (GMainContext *context);
static void g_main_context_poll_unlocked (GMainContext *context,
int timeout,
int priority,
GPollFD *fds,
int n_fds);
static void g_main_context_add_poll_unlocked (GMainContext *context,
gint priority,
GPollFD *fd);
static void g_main_context_remove_poll_unlocked (GMainContext *context,
GPollFD *fd);
static void g_source_iter_init (GSourceIter *iter,
GMainContext *context,
gboolean may_modify);
static gboolean g_source_iter_next (GSourceIter *iter,
GSource **source);
static void g_source_iter_clear (GSourceIter *iter);
static gboolean g_timeout_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data);
static gboolean g_child_watch_prepare (GSource *source,
gint *timeout);
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);
#ifndef G_OS_WIN32
static void unref_unix_signal_handler_unlocked (int signum);
#endif
#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);
static gboolean g_idle_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data);
static void block_source (GSource *source);
static GMainContext *glib_worker_context;
#ifndef G_OS_WIN32
/* UNIX signals work by marking one of these variables then waking the
* worker context to check on them and dispatch accordingly.
*
* Both variables must be accessed using atomic primitives, unless those atomic
* primitives are implemented using fallback mutexes (as those aren’t safe in
* an interrupt context).
*
* If using atomic primitives, the variables must be of type `int` (so they’re
* the right size for the atomic primitives). Otherwise, use `sig_atomic_t` if
* it’s available, which is guaranteed to be async-signal-safe (but it’s *not*
* guaranteed to be thread-safe, which is why we use atomic primitives if
* possible).
*
* Typically, `sig_atomic_t` is a typedef to `int`, but that’s not the case on
* FreeBSD, so we can’t use it unconditionally if it’s defined.
*/
#if (defined(G_ATOMIC_LOCK_FREE) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)) || !defined(HAVE_SIG_ATOMIC_T)
static volatile int unix_signal_pending[NSIG];
static volatile int any_unix_signal_pending;
#else
static volatile sig_atomic_t unix_signal_pending[NSIG];
static volatile sig_atomic_t any_unix_signal_pending;
#endif
/* Guards all the data below */
G_LOCK_DEFINE_STATIC (unix_signal_lock);
static guint unix_signal_refcount[NSIG];
static GSList *unix_signal_watches;
static GSList *unix_child_watches;
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,
NULL, NULL
};
#endif /* !G_OS_WIN32 */
G_LOCK_DEFINE_STATIC (main_context_list);
static GSList *main_context_list = NULL;
GSourceFuncs g_timeout_funcs =
{
NULL, /* prepare */
NULL, /* check */
g_timeout_dispatch,
NULL, NULL, NULL
};
GSourceFuncs g_child_watch_funcs =
{
g_child_watch_prepare,
g_child_watch_check,
g_child_watch_dispatch,
g_child_watch_finalize,
NULL, NULL
};
GSourceFuncs g_idle_funcs =
{
g_idle_prepare,
g_idle_check,
g_idle_dispatch,
NULL, NULL, NULL
};
/**
* g_main_context_ref:
* @context: (not nullable): a #GMainContext
*
* Increases the reference count on a #GMainContext object by one.
*
* Returns: the @context that was passed in (since 2.6)
**/
GMainContext *
g_main_context_ref (GMainContext *context)
{
int old_ref_count;
g_return_val_if_fail (context != NULL, NULL);
old_ref_count = g_atomic_int_add (&context->ref_count, 1);
g_return_val_if_fail (old_ref_count > 0, NULL);
return context;
}
static inline void
poll_rec_list_free (GMainContext *context,
GPollRec *list)
{
g_slice_free_chain (GPollRec, list, next);
}
/**
* g_main_context_unref:
* @context: (not nullable): a #GMainContext
*
* Decreases the reference count on a #GMainContext object by one. If
* the result is zero, free the context and free all associated memory.
**/
void
g_main_context_unref (GMainContext *context)
{
GSourceIter iter;
GSource *source;
GList *sl_iter;
GSList *s_iter, *remaining_sources = NULL;
GSourceList *list;
guint i;
g_return_if_fail (context != NULL);
g_return_if_fail (g_atomic_int_get (&context->ref_count) > 0);
if (!g_atomic_int_dec_and_test (&context->ref_count))
return;
G_LOCK (main_context_list);
main_context_list = g_slist_remove (main_context_list, context);
G_UNLOCK (main_context_list);
/* Free pending dispatches */
for (i = 0; i < context->pending_dispatches->len; i++)
g_source_unref_internal (context->pending_dispatches->pdata[i], context, FALSE);
/* g_source_iter_next() assumes the context is locked. */
LOCK_CONTEXT (context);
/* First collect all remaining sources from the sources lists and store a
* new reference in a separate list. Also set the context of the sources
* to NULL so that they can't access a partially destroyed context anymore.
*
* We have to do this first so that we have a strong reference to all
* sources and destroying them below does not also free them, and so that
* none of the sources can access the context from their finalize/dispose
* functions. */
g_source_iter_init (&iter, context, FALSE);
while (g_source_iter_next (&iter, &source))
{
source->context = NULL;
remaining_sources = g_slist_prepend (remaining_sources, g_source_ref (source));
}
g_source_iter_clear (&iter);
/* Next destroy all sources. As we still hold a reference to all of them,
* this won't cause any of them to be freed yet and especially prevents any
* source that unrefs another source from its finalize function to be freed.
*/
for (s_iter = remaining_sources; s_iter; s_iter = s_iter->next)
{
source = s_iter->data;
g_source_destroy_internal (source, context, TRUE);
}
sl_iter = context->source_lists.head;
while (sl_iter != NULL)
{
list = sl_iter->data;
sl_iter = sl_iter->next;
g_slice_free (GSourceList, list);
}
g_hash_table_destroy (context->sources);
UNLOCK_CONTEXT (context);
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);
g_cond_clear (&context->cond);
g_free (context);
/* And now finally get rid of our references to the sources. This will cause
* them to be freed unless something else still has a reference to them. Due
* to setting the context pointers in the sources to NULL above, this won't
* ever access the context or the internal linked list inside the GSource.
* We already removed the sources completely from the context above. */
for (s_iter = remaining_sources; s_iter; s_iter = s_iter->next)
{
source = s_iter->data;
g_source_unref_internal (source, NULL, FALSE);
}
g_slist_free (remaining_sources);
}
/* Helper function used by mainloop/overflow test.
*/
GMainContext *
g_main_context_new_with_next_id (guint next_id)
{
GMainContext *ret = g_main_context_new ();
ret->next_id = next_id;
return ret;
}
/**
* g_main_context_new:
*
* Creates a new #GMainContext structure.
*
* Returns: the new #GMainContext
**/
GMainContext *
g_main_context_new (void)
{
return g_main_context_new_with_flags (G_MAIN_CONTEXT_FLAGS_NONE);
}
/**
* g_main_context_new_with_flags:
* @flags: a bitwise-OR combination of #GMainContextFlags flags that can only be
* set at creation time.
*
* Creates a new #GMainContext structure.
*
* Returns: (transfer full): the new #GMainContext
*
* Since: 2.72
*/
GMainContext *
g_main_context_new_with_flags (GMainContextFlags flags)
{
static gsize initialised;
GMainContext *context;
if (g_once_init_enter (&initialised))
{
#ifdef G_MAIN_POLL_DEBUG
if (g_getenv ("G_MAIN_POLL_DEBUG") != NULL)
_g_main_poll_debug = TRUE;
#endif
g_once_init_leave (&initialised, TRUE);
}
context = g_new0 (GMainContext, 1);
TRACE (GLIB_MAIN_CONTEXT_NEW (context));
g_mutex_init (&context->mutex);
g_cond_init (&context->cond);
context->sources = g_hash_table_new (g_uint_hash, g_uint_equal);
context->owner = NULL;
context->flags = flags;
context->waiters = NULL;
context->ref_count = 1;
context->next_id = 1;
context->poll_func = g_poll;
context->cached_poll_array = NULL;
context->cached_poll_array_size = 0;
context->pending_dispatches = g_ptr_array_new ();
context->time_is_fresh = FALSE;
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);
#ifdef G_MAIN_POLL_DEBUG
if (_g_main_poll_debug)
g_print ("created context=%p\n", context);
#endif
G_UNLOCK (main_context_list);
return context;
}
/**
* g_main_context_default:
*
* Returns the global-default main context. This is the main context
* used for main loop functions when a main loop is not explicitly
* specified, and corresponds to the "main" main loop. See also
* g_main_context_get_thread_default().
*
* Returns: (transfer none): the global-default main context.
**/
GMainContext *
g_main_context_default (void)
{
static GMainContext *default_main_context = NULL;
if (g_once_init_enter_pointer (&default_main_context))
{
GMainContext *context;
context = g_main_context_new ();
TRACE (GLIB_MAIN_CONTEXT_DEFAULT (context));
#ifdef G_MAIN_POLL_DEBUG
if (_g_main_poll_debug)
g_print ("global-default main context=%p\n", context);
#endif
g_once_init_leave_pointer (&default_main_context, context);
}
return default_main_context;
}
static void
free_context (gpointer data)
{
GMainContext *context = data;
TRACE (GLIB_MAIN_CONTEXT_FREE (context));
g_main_context_release (context);
if (context)
g_main_context_unref (context);
}
static void
free_context_stack (gpointer data)
{
g_queue_free_full((GQueue *) data, (GDestroyNotify) free_context);
}
static GPrivate thread_context_stack = G_PRIVATE_INIT (free_context_stack);
/**
* g_main_context_push_thread_default:
* @context: (nullable): a #GMainContext, or %NULL for the global-default
* main context
*
* Acquires @context and sets it as the thread-default context for the
* current thread. This will cause certain asynchronous operations
* (such as most [gio][gio]-based I/O) which are
* started in this thread to run under @context and deliver their
* results to its main loop, rather than running under the global
* default main context in the main thread. Note that calling this function
* changes the context returned by g_main_context_get_thread_default(),
* not the one returned by g_main_context_default(), so it does not affect
* the context used by functions like g_idle_add().
*
* Normally you would call this function shortly after creating a new
* thread, passing it a #GMainContext which will be run by a
* #GMainLoop in that thread, to set a new default context for all
* async operations in that thread. In this case you may not need to
* ever call g_main_context_pop_thread_default(), assuming you want the
* new #GMainContext to be the default for the whole lifecycle of the
* thread.
*
* If you don't have control over how the new thread was created (e.g.
* in the new thread isn't newly created, or if the thread life
* cycle is managed by a #GThreadPool), it is always suggested to wrap
* the logic that needs to use the new #GMainContext inside a
* g_main_context_push_thread_default() / g_main_context_pop_thread_default()
* pair, otherwise threads that are re-used will end up never explicitly
* releasing the #GMainContext reference they hold.
*
* In some cases you may want to schedule a single operation in a
* non-default context, or temporarily use a non-default context in
* the main thread. In that case, you can wrap the call to the
* asynchronous operation inside a
* g_main_context_push_thread_default() /
* g_main_context_pop_thread_default() pair, but it is up to you to
* ensure that no other asynchronous operations accidentally get
* started while the non-default context is active.
*
* Beware that libraries that predate this function may not correctly
* handle being used from a thread with a thread-default context. Eg,
* see g_file_supports_thread_contexts().
*
* Since: 2.22
**/
void
g_main_context_push_thread_default (GMainContext *context)
{
GQueue *stack;
gboolean acquired_context;
acquired_context = g_main_context_acquire (context);
g_return_if_fail (acquired_context);
if (context == g_main_context_default ())
context = NULL;
else if (context)
g_main_context_ref (context);
stack = g_private_get (&thread_context_stack);
if (!stack)
{
stack = g_queue_new ();
g_private_set (&thread_context_stack, stack);
}
g_queue_push_head (stack, context);
TRACE (GLIB_MAIN_CONTEXT_PUSH_THREAD_DEFAULT (context));
}
/**
* g_main_context_pop_thread_default:
* @context: (nullable): a #GMainContext, or %NULL for the global-default
* main context
*
* Pops @context off the thread-default context stack (verifying that
* it was on the top of the stack).
*
* Since: 2.22
**/
void
g_main_context_pop_thread_default (GMainContext *context)
{
GQueue *stack;
if (context == g_main_context_default ())
context = NULL;
stack = g_private_get (&thread_context_stack);
g_return_if_fail (stack != NULL);
g_return_if_fail (g_queue_peek_head (stack) == context);
TRACE (GLIB_MAIN_CONTEXT_POP_THREAD_DEFAULT (context));
g_queue_pop_head (stack);
g_main_context_release (context);
if (context)
g_main_context_unref (context);
}
/**
* g_main_context_get_thread_default:
*
* 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 or
* g_main_context_ref_thread_default() to get a #GMainContext to add
* their #GSources to. (Note that even in single-threaded
* programs applications may sometimes want to temporarily push a
* non-default context, so it is not safe to assume that this will
* always return %NULL if you are running in the default thread.)
*
* If you need to hold a reference on the context, use
* g_main_context_ref_thread_default() instead.
*
* Returns: (transfer none) (nullable): the thread-default #GMainContext, or
* %NULL if the thread-default context is the global-default main context.
*
* Since: 2.22
**/
GMainContext *
g_main_context_get_thread_default (void)
{
GQueue *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 */
/**
* g_source_new:
* @source_funcs: structure containing functions that implement
* the sources behavior.
* @struct_size: size of the #GSource structure to create.
*
* Creates a new #GSource structure. The size is specified to
* allow creating structures derived from #GSource that contain
* additional data. The size passed in must be at least
* `sizeof (GSource)`.
*
* 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.
*
* Returns: the newly-created #GSource.
**/
GSource *
g_source_new (GSourceFuncs *source_funcs,
guint struct_size)
{
GSource *source;
g_return_val_if_fail (source_funcs != NULL, NULL);
g_return_val_if_fail (struct_size >= sizeof (GSource), NULL);
source = (GSource*) g_malloc0 (struct_size);
source->priv = g_slice_new0 (GSourcePrivate);
source->source_funcs = source_funcs;
source->ref_count = 1;
source->priority = G_PRIORITY_DEFAULT;
source->flags = G_HOOK_FLAG_ACTIVE;
source->priv->ready_time = -1;
/* NULL/0 initialization for all other fields */
TRACE (GLIB_SOURCE_NEW (source, source_funcs->prepare, source_funcs->check,
source_funcs->dispatch, source_funcs->finalize,
struct_size));
return source;
}
/**
* g_source_set_dispose_function:
* @source: A #GSource to set the dispose function on
* @dispose: #GSourceDisposeFunc to set on the source
*
* Set @dispose as dispose function on @source. @dispose will be called once
* the reference count of @source reaches 0 but before any of the state of the
* source is freed, especially before the finalize function is called.
*
* This means that at this point @source is still a valid #GSource and it is
* allow for the reference count to increase again until @dispose returns.
*
* The dispose function can be used to clear any "weak" references to the
* @source in other data structures in a thread-safe way where it is possible
* for another thread to increase the reference count of @source again while
* it is being freed.
*
* The finalize function can not be used for this purpose as at that point
* @source is already partially freed and not valid anymore.
*
* This should only ever be called from #GSource implementations.
*
* Since: 2.64
**/
void
g_source_set_dispose_function (GSource *source,
GSourceDisposeFunc dispose)
{
g_return_if_fail (source != NULL);
g_return_if_fail (source->priv->dispose == NULL);
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
source->priv->dispose = dispose;
}
/* Holds context's lock */
static void
g_source_iter_init (GSourceIter *iter,
GMainContext *context,
gboolean may_modify)
{
iter->context = context;
iter->current_list = NULL;
iter->source = NULL;
iter->may_modify = may_modify;
}
/* Holds context's lock */
static gboolean
g_source_iter_next (GSourceIter *iter, GSource **source)
{
GSource *next_source;
if (iter->source)
next_source = iter->source->next;
else
next_source = NULL;
if (!next_source)
{
if (iter->current_list)
iter->current_list = iter->current_list->next;
else
iter->current_list = iter->context->source_lists.head;
if (iter->current_list)
{
GSourceList *source_list = iter->current_list->data;
next_source = source_list->head;
}
}
/* Note: unreffing iter->source could potentially cause its
* GSourceList to be removed from source_lists (if iter->source is
* the only source in its list, and it is destroyed), so we have to
* keep it reffed until after we advance iter->current_list, above.
*
* Also we first have to ref the next source before unreffing the
* previous one as unreffing the previous source can potentially
* free the next one.
*/
if (next_source && iter->may_modify)
g_source_ref (next_source);
if (iter->source && iter->may_modify)
g_source_unref_internal (iter->source, iter->context, TRUE);
iter->source = next_source;
*source = iter->source;
return *source != NULL;
}
/* Holds context's lock. Only necessary to call if you broke out of
* the g_source_iter_next() loop early.
*/
static void
g_source_iter_clear (GSourceIter *iter)
{
if (iter->source && iter->may_modify)
{
g_source_unref_internal (iter->source, iter->context, TRUE);
iter->source = NULL;
}
}
/* Holds context's lock
*/
static GSourceList *
find_source_list_for_priority (GMainContext *context,
gint priority,
gboolean create)
{
GList *iter;
GSourceList *source_list;
for (iter = context->source_lists.head; iter; iter = iter->next)
{
source_list = iter->data;
if (source_list->priority == priority)
return source_list;
if (source_list->priority > priority)
{
if (!create)
return NULL;
source_list = g_slice_new0 (GSourceList);
source_list->link.data = source_list;
source_list->priority = priority;
g_queue_insert_before_link (&context->source_lists,
iter,
&source_list->link);
return source_list;
}
}
if (!create)
return NULL;
source_list = g_slice_new0 (GSourceList);
source_list->link.data = source_list;
source_list->priority = priority;
g_queue_push_tail_link (&context->source_lists, &source_list->link);
return source_list;
}
/* Holds context's lock
*/
static void
source_add_to_context (GSource *source,
GMainContext *context)
{
GSourceList *source_list;
GSource *prev, *next;
source_list = find_source_list_for_priority (context, source->priority, TRUE);
if (source->priv->parent_source)
{
g_assert (source_list->head != NULL);
/* Put the source immediately before its parent */
prev = source->priv->parent_source->prev;
next = source->priv->parent_source;
}
else
{
prev = source_list->tail;
next = NULL;
}
source->next = next;
if (next)
next->prev = source;
else
source_list->tail = source;
source->prev = prev;
if (prev)
prev->next = source;
else
source_list->head = source;
}
/* Holds context's lock
*/
static void
source_remove_from_context (GSource *source,
GMainContext *context)
{
GSourceList *source_list;
source_list = find_source_list_for_priority (context, source->priority, FALSE);
g_return_if_fail (source_list != NULL);
if (source->prev)
source->prev->next = source->next;
else
source_list->head = source->next;
if (source->next)
source->next->prev = source->prev;
else
source_list->tail = source->prev;
source->prev = NULL;
source->next = NULL;
if (source_list->head == NULL)
{
g_queue_unlink (&context->source_lists, &source_list->link);
g_slice_free (GSourceList, source_list);
}
}
static guint
g_source_attach_unlocked (GSource *source,
GMainContext *context,
gboolean do_wakeup)
{
GSList *tmp_list;
guint id;
/* The counter may have wrapped, so we must ensure that we do not
* reuse the source id of an existing source.
*/
do
id = context->next_id++;
while (id == 0 || g_hash_table_contains (context->sources, &id));
source->context = context;
source->source_id = id;
g_source_ref (source);
g_hash_table_add (context->sources, &source->source_id);
source_add_to_context (source, context);
if (!SOURCE_BLOCKED (source))
{
tmp_list = source->poll_fds;
while (tmp_list)
{
g_main_context_add_poll_unlocked (context, source->priority, tmp_list->data);
tmp_list = tmp_list->next;
}
for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
g_main_context_add_poll_unlocked (context, source->priority, tmp_list->data);
}
tmp_list = source->priv->child_sources;
while (tmp_list)
{
g_source_attach_unlocked (tmp_list->data, context, FALSE);
tmp_list = tmp_list->next;
}
/* If another thread has acquired the context, wake it up since it
* might be in poll() right now.
*/
if (do_wakeup &&
(context->flags & G_MAIN_CONTEXT_FLAGS_OWNERLESS_POLLING ||
(context->owner && context->owner != G_THREAD_SELF)))
{
g_wakeup_signal (context->wakeup);
}
g_trace_mark (G_TRACE_CURRENT_TIME, 0,
"GLib", "g_source_attach",
"%s to context %p",
(g_source_get_name (source) != NULL) ? g_source_get_name (source) : "(unnamed)",
context);
return source->source_id;
}
/**
* g_source_attach:
* @source: a #GSource
* @context: (nullable): a #GMainContext (if %NULL, the global-default
* main context will be used)
*
* Adds a #GSource to a @context so that it will be executed within
* that context. Remove it by calling g_source_destroy().
*
* This function is safe to call from any thread, regardless of which thread
* the @context is running in.
*
* Returns: the ID (greater than 0) for the source within the
* #GMainContext.
**/
guint
g_source_attach (GSource *source,
GMainContext *context)
{
guint result = 0;
g_return_val_if_fail (source != NULL, 0);
g_return_val_if_fail (g_atomic_int_get (&source->ref_count) > 0, 0);
g_return_val_if_fail (source->context == NULL, 0);
g_return_val_if_fail (!SOURCE_DESTROYED (source), 0);
if (!context)
context = g_main_context_default ();
LOCK_CONTEXT (context);
result = g_source_attach_unlocked (source, context, TRUE);
TRACE (GLIB_MAIN_SOURCE_ATTACH (g_source_get_name (source), source, context,
result));
UNLOCK_CONTEXT (context);
return result;
}
static void
g_source_destroy_internal (GSource *source,
GMainContext *context,
gboolean have_lock)
{
TRACE (GLIB_MAIN_SOURCE_DESTROY (g_source_get_name (source), source,
context));
if (!have_lock)
LOCK_CONTEXT (context);
if (!SOURCE_DESTROYED (source))
{
GSList *tmp_list;
gpointer old_cb_data;
GSourceCallbackFuncs *old_cb_funcs;
source->flags &= ~G_HOOK_FLAG_ACTIVE;
old_cb_data = source->callback_data;
old_cb_funcs = source->callback_funcs;
source->callback_data = NULL;
source->callback_funcs = NULL;
if (old_cb_funcs)
{
UNLOCK_CONTEXT (context);
old_cb_funcs->unref (old_cb_data);
LOCK_CONTEXT (context);
}
if (!SOURCE_BLOCKED (source))
{
tmp_list = source->poll_fds;
while (tmp_list)
{
g_main_context_remove_poll_unlocked (context, tmp_list->data);
tmp_list = tmp_list->next;
}
for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
g_main_context_remove_poll_unlocked (context, tmp_list->data);
}
while (source->priv->child_sources)
g_child_source_remove_internal (source->priv->child_sources->data, context);
if (source->priv->parent_source)
g_child_source_remove_internal (source, context);
g_source_unref_internal (source, context, TRUE);
}
if (!have_lock)
UNLOCK_CONTEXT (context);
}
/**
* g_source_destroy:
* @source: a #GSource
*
* Removes a source from its #GMainContext, if any, and mark it as
* destroyed. The source cannot be subsequently added to another
* context. It is safe to call this on sources which have already been
* removed from their context.
*
* This does not unref the #GSource: if you still hold a reference, use
* g_source_unref() to drop it.
*
* This function is safe to call from any thread, regardless of which thread
* the #GMainContext is running in.
*
* If the source is currently attached to a #GMainContext, destroying it
* will effectively unset the callback similar to calling g_source_set_callback().
* This can mean, that the data's #GDestroyNotify gets called right away.
*/
void
g_source_destroy (GSource *source)
{
GMainContext *context;
g_return_if_fail (source != NULL);
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
context = source->context;
if (context)
g_source_destroy_internal (source, context, FALSE);
else
source->flags &= ~G_HOOK_FLAG_ACTIVE;
}
/**
* g_source_get_id:
* @source: a #GSource
*
* Returns the numeric ID for a particular source. The ID of a source
* is a positive integer which is unique within a particular main loop
* context. The reverse
* mapping from ID to source is done by g_main_context_find_source_by_id().
*
* You can only call this function while the source is associated to a
* #GMainContext instance; calling this function before g_source_attach()
* or after g_source_destroy() yields undefined behavior. The ID returned
* is unique within the #GMainContext instance passed to g_source_attach().
*
* Returns: the ID (greater than 0) for the source
**/
guint
g_source_get_id (GSource *source)
{
guint result;
g_return_val_if_fail (source != NULL, 0);
g_return_val_if_fail (g_atomic_int_get (&source->ref_count) > 0, 0);
g_return_val_if_fail (source->context != NULL, 0);
LOCK_CONTEXT (source->context);
result = source->source_id;
UNLOCK_CONTEXT (source->context);
return result;
}
/**
* g_source_get_context:
* @source: a #GSource
*
* Gets the #GMainContext with which the source is associated.
*
* You can call this on a source that has been destroyed, provided
* that the #GMainContext it was attached to still exists (in which
* case it will return that #GMainContext). In particular, you can
* always call this function on the source returned from
* g_main_current_source(). But calling this function on a source
* whose #GMainContext has been destroyed is an error.
*
* Returns: (transfer none) (nullable): the #GMainContext with which the
* source is associated, or %NULL if the context has not
* yet been added to a source.
**/
GMainContext *
g_source_get_context (GSource *source)
{
g_return_val_if_fail (source != NULL, NULL);
g_return_val_if_fail (g_atomic_int_get (&source->ref_count) > 0, NULL);
g_return_val_if_fail (source->context != NULL || !SOURCE_DESTROYED (source), NULL);
return source->context;
}
/**
* g_source_add_poll:
* @source:a #GSource
* @fd: a #GPollFD structure holding information about a file
* descriptor to watch.
*
* Adds a file descriptor to the set of file descriptors polled for
* this source. This is usually combined with g_source_new() to add an
* event source. The event source's check function will typically test
* the @revents field in the #GPollFD struct and return %TRUE if events need
* to be processed.
*
* This API is only intended to be used by implementations of #GSource.
* Do not call this API on a #GSource that you did not create.
*
* Using this API forces the linear scanning of event sources on each
* main loop iteration. Newly-written event sources should try to use
* g_source_add_unix_fd() instead of this API.
**/
void
g_source_add_poll (GSource *source,
GPollFD *fd)
{
GMainContext *context;
g_return_if_fail (source != NULL);
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
g_return_if_fail (fd != NULL);
g_return_if_fail (!SOURCE_DESTROYED (source));
context = source->context;
if (context)
LOCK_CONTEXT (context);
source->poll_fds = g_slist_prepend (source->poll_fds, fd);
if (context)
{
if (!SOURCE_BLOCKED (source))
g_main_context_add_poll_unlocked (context, source->priority, fd);
UNLOCK_CONTEXT (context);
}
}
/**
* g_source_remove_poll:
* @source:a #GSource
* @fd: a #GPollFD structure previously passed to g_source_add_poll().
*
* Removes a file descriptor from the set of file descriptors polled for
* this source.
*
* This API is only intended to be used by implementations of #GSource.
* Do not call this API on a #GSource that you did not create.
**/
void
g_source_remove_poll (GSource *source,
GPollFD *fd)
{
GMainContext *context;
g_return_if_fail (source != NULL);
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
g_return_if_fail (fd != NULL);
g_return_if_fail (!SOURCE_DESTROYED (source));
context = source->context;
if (context)
LOCK_CONTEXT (context);
source->poll_fds = g_slist_remove (source->poll_fds, fd);
if (context)
{
if (!SOURCE_BLOCKED (source))
g_main_context_remove_poll_unlocked (context, fd);
UNLOCK_CONTEXT (context);
}
}
/**
* 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.
*
* This API is only intended to be used by implementations of #GSource.
* Do not call this API on a #GSource that you did not create.
*
* 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 (g_atomic_int_get (&source->ref_count) > 0);
g_return_if_fail (child_source != NULL);
g_return_if_fail (g_atomic_int_get (&child_source->ref_count) > 0);
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->parent_source == NULL);
context = source->context;
if (context)
LOCK_CONTEXT (context);
TRACE (GLIB_SOURCE_ADD_CHILD_SOURCE (source, child_source));
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, NULL, source->priority);
if (SOURCE_BLOCKED (source))
block_source (child_source);
if (context)
{
g_source_attach_unlocked (child_source, context, TRUE);
UNLOCK_CONTEXT (context);
}
}
static void
g_child_source_remove_internal (GSource *child_source,
GMainContext *context)
{
GSource *parent_source = child_source->priv->parent_source;
parent_source->priv->child_sources =
g_slist_remove (parent_source->priv->child_sources, child_source);
child_source->priv->parent_source = NULL;
g_source_destroy_internal (child_source, context, TRUE);
g_source_unref_internal (child_source, context, TRUE);
}
/**
* g_source_remove_child_source:
* @source:a #GSource
* @child_source: a #GSource previously passed to
* g_source_add_child_source().
*
* Detaches @child_source from @source and destroys it.
*
* This API is only intended to be used by implementations of #GSource.
* Do not call this API on a #GSource that you did not create.
*
* 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 (g_atomic_int_get (&source->ref_count) > 0);
g_return_if_fail (child_source != NULL);
g_return_if_fail (g_atomic_int_get (&child_source->ref_count) > 0);
g_return_if_fail (child_source->priv->parent_source == source);
g_return_if_fail (!SOURCE_DESTROYED (source));
g_return_if_fail (!SOURCE_DESTROYED (child_source));
context = source->context;
if (context)
LOCK_CONTEXT (context);
g_child_source_remove_internal (child_source, context);
if (context)
UNLOCK_CONTEXT (context);
}
static void
g_source_callback_ref (gpointer cb_data)
{
GSourceCallback *callback = cb_data;
g_atomic_int_inc (&callback->ref_count);
}
static void
g_source_callback_unref (gpointer cb_data)
{
GSourceCallback *callback = cb_data;
if (g_atomic_int_dec_and_test (&callback->ref_count))
{
if (callback->notify)
callback->notify (callback->data);
g_free (callback);
}
}
static void
g_source_callback_get (gpointer cb_data,
GSource *source,
GSourceFunc *func,
gpointer *data)
{
GSourceCallback *callback = cb_data;
*func = callback->func;
*data = callback->data;
}
static GSourceCallbackFuncs g_source_callback_funcs = {
g_source_callback_ref,
g_source_callback_unref,
g_source_callback_get,
};
/**
* g_source_set_callback_indirect:
* @source: the source
* @callback_data: pointer to callback data "object"
* @callback_funcs: functions for reference counting @callback_data
* and getting the callback and data
*
* Sets the callback function storing the data as a refcounted callback
* "object". This is used internally. Note that calling
* g_source_set_callback_indirect() assumes
* an initial reference count on @callback_data, and thus
* @callback_funcs->unref will eventually be called once more
* than @callback_funcs->ref.
*
* It is safe to call this function multiple times on a source which has already
* been attached to a context. The changes will take effect for the next time
* the source is dispatched after this call returns.
**/
void
g_source_set_callback_indirect (GSource *source,
gpointer callback_data,
GSourceCallbackFuncs *callback_funcs)
{
GMainContext *context;
gpointer old_cb_data;
GSourceCallbackFuncs *old_cb_funcs;
g_return_if_fail (source != NULL);
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
g_return_if_fail (callback_funcs != NULL || callback_data == NULL);
context = source->context;
if (context)
LOCK_CONTEXT (context);
if (callback_funcs != &g_source_callback_funcs)
{
TRACE (GLIB_SOURCE_SET_CALLBACK_INDIRECT (source, callback_data,
callback_funcs->ref,
callback_funcs->unref,
callback_funcs->get));
}
old_cb_data = source->callback_data;
old_cb_funcs = source->callback_funcs;
source->callback_data = callback_data;
source->callback_funcs = callback_funcs;
if (context)
UNLOCK_CONTEXT (context);
if (old_cb_funcs)
old_cb_funcs->unref (old_cb_data);
}
/**
* g_source_set_callback:
* @source: the source
* @func: a callback function
* @data: the data to pass to callback function
* @notify: (nullable): a function to call when @data is no longer in use, or %NULL.
*
* Sets the callback function for a source. The callback for a source is
* called from the source's dispatch function.
*
* The exact type of @func depends on the type of source; ie. you
* should not count on @func being called with @data as its first
* parameter. Cast @func with G_SOURCE_FUNC() to avoid warnings about
* incompatible function types.
*
* See [memory management of sources][mainloop-memory-management] for details
* on how to handle memory management of @data.
*
* Typically, you won't use this function. Instead use functions specific
* to the type of source you are using, such as g_idle_add() or g_timeout_add().
*
* It is safe to call this function multiple times on a source which has already
* been attached to a context. The changes will take effect for the next time
* the source is dispatched after this call returns.
*
* Note that g_source_destroy() for a currently attached source has the effect
* of also unsetting the callback.
**/
void
g_source_set_callback (GSource *source,
GSourceFunc func,
gpointer data,
GDestroyNotify notify)
{
GSourceCallback *new_callback;
g_return_if_fail (source != NULL);
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
TRACE (GLIB_SOURCE_SET_CALLBACK (source, func, data, notify));
new_callback = g_new (GSourceCallback, 1);
new_callback->ref_count = 1;
new_callback->func = func;
new_callback->data = data;
new_callback->notify = notify;
g_source_set_callback_indirect (source, new_callback, &g_source_callback_funcs);
}
/**
* g_source_set_funcs:
* @source: a #GSource
* @funcs: the new #GSourceFuncs
*
* Sets the source functions (can be used to override
* default implementations) of an unattached source.
*
* Since: 2.12
*/
void
g_source_set_funcs (GSource *source,
GSourceFuncs *funcs)
{
g_return_if_fail (source != NULL);
g_return_if_fail (source->context == NULL);
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
g_return_if_fail (funcs != NULL);
source->source_funcs = funcs;
}
static void
g_source_set_priority_unlocked (GSource *source,
GMainContext *context,
gint priority)
{
GSList *tmp_list;
g_return_if_fail (source->priv->parent_source == NULL ||
source->priv->parent_source->priority == priority);
TRACE (GLIB_SOURCE_SET_PRIORITY (source, context, priority));
if (context)
{
/* Remove the source from the context's source and then
* add it back after so it is sorted in the correct place
*/
source_remove_from_context (source, source->context);
}
source->priority = priority;
if (context)
{
source_add_to_context (source, source->context);
if (!SOURCE_BLOCKED (source))
{
tmp_list = source->poll_fds;
while (tmp_list)
{
g_main_context_remove_poll_unlocked (context, tmp_list->data);
g_main_context_add_poll_unlocked (context, priority, tmp_list->data);
tmp_list = tmp_list->next;
}
for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
{
g_main_context_remove_poll_unlocked (context, tmp_list->data);
g_main_context_add_poll_unlocked (context, priority, tmp_list->data);
}
}
}
if (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.
*
* A child source always has the same priority as its parent. It is not
* permitted to change the priority of a source once it has been added
* as a child of another source.
**/
void
g_source_set_priority (GSource *source,
gint priority)
{
GMainContext *context;
g_return_if_fail (source != NULL);
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
g_return_if_fail (source->priv->parent_source == NULL);
context = source->context;
if (context)
LOCK_CONTEXT (context);
g_source_set_priority_unlocked (source, context, priority);
if (context)
UNLOCK_CONTEXT (context);
}
/**
* g_source_get_priority:
* @source: a #GSource
*
* Gets the priority of a source.
*
* Returns: the priority of the source
**/
gint
g_source_get_priority (GSource *source)
{
g_return_val_if_fail (source != NULL, 0);
g_return_val_if_fail (g_atomic_int_get (&source->ref_count) > 0, 0);
return source->priority;
}
/**
* g_source_set_ready_time:
* @source: a #GSource
* @ready_time: the monotonic time at which the source will be ready,
* 0 for "immediately", -1 for "never"
*
* Sets a #GSource to be dispatched when the given monotonic time is
* reached (or passed). If the monotonic time is in the past (as it
* always will be if @ready_time is 0) then the source will be
* dispatched immediately.
*
* If @ready_time is -1 then the source is never woken up on the basis
* of the passage of time.
*
* Dispatching the source does not reset the ready time. You should do
* so yourself, from the source dispatch function.
*
* Note that if you have a pair of sources where the ready time of one
* suggests that it will be delivered first but the priority for the
* other suggests that it would be delivered first, and the ready time
* for both sources is reached during the same main context iteration,
* then the order of dispatch is undefined.
*
* It is a no-op to call this function on a #GSource which has already been
* destroyed with g_source_destroy().
*
* This API is only intended to be used by implementations of #GSource.
* Do not call this API on a #GSource that you did not create.
*
* Since: 2.36
**/
void
g_source_set_ready_time (GSource *source,
gint64 ready_time)
{
GMainContext *context;
g_return_if_fail (source != NULL);
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
context = source->context;
if (context)
LOCK_CONTEXT (context);
if (source->priv->ready_time == ready_time)
{
if (context)
UNLOCK_CONTEXT (context);
return;
}
source->priv->ready_time = ready_time;
TRACE (GLIB_SOURCE_SET_READY_TIME (source, ready_time));
if (context)
{
/* Quite likely that we need to change the timeout on the poll */
if (!SOURCE_BLOCKED (source))
g_wakeup_signal (context->wakeup);
UNLOCK_CONTEXT (context);
}
}
/**
* g_source_get_ready_time:
* @source: a #GSource
*
* Gets the "ready time" of @source, as set by
* g_source_set_ready_time().
*
* Any time before or equal to the current monotonic time (including 0)
* is an indication that the source will fire immediately.
*
* Returns: the monotonic ready time, -1 for "never"
**/
gint64
g_source_get_ready_time (GSource *source)
{
g_return_val_if_fail (source != NULL, -1);
g_return_val_if_fail (g_atomic_int_get (&source->ref_count) > 0, -1);
return source->priv->ready_time;
}
/**
* g_source_set_can_recurse:
* @source: a #GSource
* @can_recurse: whether recursion is allowed for this source
*
* Sets whether a source can be called recursively. If @can_recurse is
* %TRUE, then while the source is being dispatched then this source
* will be processed normally. Otherwise, all processing of this
* source is blocked until the dispatch function returns.
**/
void
g_source_set_can_recurse (GSource *source,
gboolean can_recurse)
{
GMainContext *context;
g_return_if_fail (source != NULL);
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
context = source->context;
if (context)
LOCK_CONTEXT (context);
if (can_recurse)
source->flags |= G_SOURCE_CAN_RECURSE;
else
source->flags &= ~G_SOURCE_CAN_RECURSE;
if (context)
UNLOCK_CONTEXT (context);
}
/**
* g_source_get_can_recurse:
* @source: a #GSource
*
* Checks whether a source is allowed to be called recursively.
* see g_source_set_can_recurse().
*
* Returns: whether recursion is allowed.
**/
gboolean
g_source_get_can_recurse (GSource *source)
{
g_return_val_if_fail (source != NULL, FALSE);
g_return_val_if_fail (g_atomic_int_get (&source->ref_count) > 0, FALSE);
return (source->flags & G_SOURCE_CAN_RECURSE) != 0;
}
static void
g_source_set_name_full (GSource *source,
const char *name,
gboolean is_static)
{
GMainContext *context;
g_return_if_fail (source != NULL);
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
context = source->context;
if (context)
LOCK_CONTEXT (context);
TRACE (GLIB_SOURCE_SET_NAME (source, name));
/* setting back to NULL is allowed, just because it's
* weird if get_name can return NULL but you can't
* set that.
*/
if (!source->priv->static_name)
g_free (source->name);
if (is_static)
source->name = (char *)name;
else
source->name = g_strdup (name);
source->priv->static_name = is_static;
if (context)
UNLOCK_CONTEXT (context);
}
/**
* g_source_set_name:
* @source: a #GSource
* @name: debug name for the source
*
* Sets a name for the source, used in debugging and profiling.
* The name defaults to #NULL.
*
* The source name should describe in a human-readable way
* what the source does. For example, "X11 event queue"
* or "GTK repaint idle handler" or whatever it is.
*
* It is permitted to call this function multiple times, but is not
* recommended due to the potential performance impact. For example,
* one could change the name in the "check" function of a #GSourceFuncs
* to include details like the event type in the source name.
*
* Use caution if changing the name while another thread may be
* accessing it with g_source_get_name(); that function does not copy
* the value, and changing the value will free it while the other thread
* may be attempting to use it.
*
* Also see g_source_set_static_name().
*
* Since: 2.26
**/
void
g_source_set_name (GSource *source,
const char *name)
{
g_source_set_name_full (source, name, FALSE);
}
/**
* g_source_set_static_name:
* @source: a #GSource
* @name: debug name for the source
*
* A variant of g_source_set_name() that does not
* duplicate the @name, and can only be used with
* string literals.
*
* Since: 2.70
*/
void
g_source_set_static_name (GSource *source,
const char *name)
{
g_source_set_name_full (source, name, TRUE);
}
/**
* g_source_get_name:
* @source: a #GSource
*
* Gets a name for the source, used in debugging and profiling. The
* name may be #NULL if it has never been set with g_source_set_name().
*
* Returns: (nullable): the name of the source
*
* Since: 2.26
**/
const char *
g_source_get_name (GSource *source)
{
g_return_val_if_fail (source != NULL, NULL);
g_return_val_if_fail (g_atomic_int_get (&source->ref_count) > 0, NULL);
return source->name;
}
/**
* g_source_set_name_by_id:
* @tag: a #GSource ID
* @name: debug name for the source
*
* Sets the name of a source using its ID.
*
* This is a convenience utility to set source names from the return
* value of g_idle_add(), g_timeout_add(), etc.
*
* It is a programmer error to attempt to set the name of a non-existent
* source.
*
* More specifically: source IDs can be reissued after a source has been
* destroyed and therefore it is never valid to use this function with a
* source ID which may have already been removed. An example is when
* scheduling an idle to run in another thread with g_idle_add(): the
* idle may already have run and been removed by the time this function
* is called on its (now invalid) source ID. This source ID may have
* been reissued, leading to the operation being performed against the
* wrong source.
*
* Since: 2.26
**/
void
g_source_set_name_by_id (guint tag,
const char *name)
{
GSource *source;
g_return_if_fail (tag > 0);
source = g_main_context_find_source_by_id (NULL, tag);
if (source == NULL)
return;
g_source_set_name (source, name);
}
/**
* g_source_ref:
* @source: a #GSource
*
* Increases the reference count on a source by one.
*
* Returns: @source
**/
GSource *
g_source_ref (GSource *source)
{
g_return_val_if_fail (source != NULL, NULL);
/* We allow ref_count == 0 here to allow the dispose function to resurrect
* the GSource if needed */
g_return_val_if_fail (g_atomic_int_get (&source->ref_count) >= 0, NULL);
g_atomic_int_inc (&source->ref_count);
return source;
}
/* g_source_unref() but possible to call within context lock
*/
static void
g_source_unref_internal (GSource *source,
GMainContext *context,
gboolean have_lock)
{
gpointer old_cb_data = NULL;
GSourceCallbackFuncs *old_cb_funcs = NULL;
g_return_if_fail (source != NULL);
if (!have_lock && context)
LOCK_CONTEXT (context);
if (g_atomic_int_dec_and_test (&source->ref_count))
{
/* If there's a dispose function, call this first */
if (source->priv->dispose)
{
/* Temporarily increase the ref count again so that GSource methods
* can be called from dispose(). */
g_atomic_int_inc (&source->ref_count);
if (context)
UNLOCK_CONTEXT (context);
source->priv->dispose (source);
if (context)
LOCK_CONTEXT (context);
/* Now the reference count might be bigger than 0 again, in which
* case we simply return from here before freeing the source */
if (!g_atomic_int_dec_and_test (&source->ref_count))
{
if (!have_lock && context)
UNLOCK_CONTEXT (context);
return;
}
}
TRACE (GLIB_SOURCE_BEFORE_FREE (source, context,
source->source_funcs->finalize));
old_cb_data = source->callback_data;
old_cb_funcs = source->callback_funcs;
source->callback_data = NULL;
source->callback_funcs = NULL;
if (context)
{
if (!SOURCE_DESTROYED (source))
g_warning (G_STRLOC ": ref_count == 0, but source was still attached to a context!");
source_remove_from_context (source, context);
g_hash_table_remove (context->sources, &source->source_id);
}
if (source->source_funcs->finalize)
{
gint old_ref_count;
/* Temporarily increase the ref count again so that GSource methods
* can be called from finalize(). */
g_atomic_int_inc (&source->ref_count);
if (context)
UNLOCK_CONTEXT (context);
source->source_funcs->finalize (source);
if (context)
LOCK_CONTEXT (context);
old_ref_count = g_atomic_int_add (&source->ref_count, -1);
g_warn_if_fail (old_ref_count == 1);
}
if (old_cb_funcs)
{
gint old_ref_count;
/* Temporarily increase the ref count again so that GSource methods
* can be called from callback_funcs.unref(). */
g_atomic_int_inc (&source->ref_count);
if (context)
UNLOCK_CONTEXT (context);
old_cb_funcs->unref (old_cb_data);
if (context)
LOCK_CONTEXT (context);
old_ref_count = g_atomic_int_add (&source->ref_count, -1);
g_warn_if_fail (old_ref_count == 1);
}
if (!source->priv->static_name)
g_free (source->name);
source->name = NULL;
g_slist_free (source->poll_fds);
source->poll_fds = NULL;
g_slist_free_full (source->priv->fds, g_free);
while (source->priv->child_sources)
{
GSource *child_source = source->priv->child_sources->data;
source->priv->child_sources =
g_slist_remove (source->priv->child_sources, child_source);
child_source->priv->parent_source = NULL;
g_source_unref_internal (child_source, context, TRUE);
}
g_slice_free (GSourcePrivate, source->priv);
source->priv = NULL;
g_free (source);
}
if (!have_lock && context)
UNLOCK_CONTEXT (context);
}
/**
* g_source_unref:
* @source: a #GSource
*
* Decreases the reference count of a source by one. If the
* resulting reference count is zero the source and associated
* memory will be destroyed.
**/
void
g_source_unref (GSource *source)
{
g_return_if_fail (source != NULL);
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
g_source_unref_internal (source, source->context, FALSE);
}
/**
* g_main_context_find_source_by_id:
* @context: (nullable): a #GMainContext (if %NULL, the global-default
* main context will be used)
* @source_id: the source ID, as returned by g_source_get_id().
*
* Finds a #GSource given a pair of context and ID.
*
* It is a programmer error to attempt to look up a non-existent source.
*
* More specifically: source IDs can be reissued after a source has been
* destroyed and therefore it is never valid to use this function with a
* source ID which may have already been removed. An example is when
* scheduling an idle to run in another thread with g_idle_add(): the
* idle may already have run and been removed by the time this function
* is called on its (now invalid) source ID. This source ID may have
* been reissued, leading to the operation being performed against the
* wrong source.
*
* Returns: (transfer none): the #GSource
**/
GSource *
g_main_context_find_source_by_id (GMainContext *context,
guint source_id)
{
GSource *source = NULL;
gconstpointer ptr;
g_return_val_if_fail (source_id > 0, NULL);
if (context == NULL)
context = g_main_context_default ();
LOCK_CONTEXT (context);
ptr = g_hash_table_lookup (context->sources, &source_id);
if (ptr)
{
source = G_CONTAINER_OF (ptr, GSource, source_id);
if (SOURCE_DESTROYED (source))
source = NULL;
}
UNLOCK_CONTEXT (context);
return source;
}
/**
* g_main_context_find_source_by_funcs_user_data:
* @context: (nullable): a #GMainContext (if %NULL, the global-default
* main context will be used).
* @funcs: the @source_funcs passed to g_source_new().
* @user_data: the user data from the callback.
*
* Finds a source with the given source functions and user data. If
* multiple sources exist with the same source function and user data,
* the first one found will be returned.
*
* Returns: (transfer none): the source, if one was found, otherwise %NULL
**/
GSource *
g_main_context_find_source_by_funcs_user_data (GMainContext *context,
GSourceFuncs *funcs,
gpointer user_data)
{
GSourceIter iter;
GSource *source;
g_return_val_if_fail (funcs != NULL, NULL);
if (context == NULL)
context = g_main_context_default ();
LOCK_CONTEXT (context);
g_source_iter_init (&iter, context, FALSE);
while (g_source_iter_next (&iter, &source))
{
if (!SOURCE_DESTROYED (source) &&
source->source_funcs == funcs &&
source->callback_funcs)
{
GSourceFunc callback;
gpointer callback_data;
source->callback_funcs->get (source->callback_data, source, &callback, &callback_data);
if (callback_data == user_data)
break;
}
}
g_source_iter_clear (&iter);
UNLOCK_CONTEXT (context);
return source;
}
/**
* g_main_context_find_source_by_user_data:
* @context: (nullable): a #GMainContext (if %NULL, the global-default
* main context will be used)
* @user_data: the user_data for the callback.
*
* Finds a source with the given user data for the callback. If
* multiple sources exist with the same user data, the first
* one found will be returned.
*
* Returns: (transfer none): the source, if one was found, otherwise %NULL
**/
GSource *
g_main_context_find_source_by_user_data (GMainContext *context,
gpointer user_data)
{
GSourceIter iter;
GSource *source;
if (context == NULL)
context = g_main_context_default ();
LOCK_CONTEXT (context);
g_source_iter_init (&iter, context, FALSE);
while (g_source_iter_next (&iter, &source))
{
if (!SOURCE_DESTROYED (source) &&
source->callback_funcs)
{
GSourceFunc callback;
gpointer callback_data = NULL;
source->callback_funcs->get (source->callback_data, source, &callback, &callback_data);
if (callback_data == user_data)
break;
}
}
g_source_iter_clear (&iter);
UNLOCK_CONTEXT (context);
return source;
}
/**
* g_source_remove:
* @tag: the ID of the source to remove.
*
* Removes the source with the given ID from the default main context. You must
* use g_source_destroy() for sources added to a non-default main context.
*
* The ID of a #GSource is given by g_source_get_id(), or will be
* returned by the functions g_source_attach(), g_idle_add(),
* g_idle_add_full(), g_timeout_add(), g_timeout_add_full(),
* g_child_watch_add(), g_child_watch_add_full(), g_io_add_watch(), and
* g_io_add_watch_full().
*
* It is a programmer error to attempt to remove a non-existent source.
*
* More specifically: source IDs can be reissued after a source has been
* destroyed and therefore it is never valid to use this function with a
* source ID which may have already been removed. An example is when
* scheduling an idle to run in another thread with g_idle_add(): the
* idle may already have run and been removed by the time this function
* is called on its (now invalid) source ID. This source ID may have
* been reissued, leading to the operation being performed against the
* wrong source.
*
* Returns: %TRUE if the source was found and removed.
**/
gboolean
g_source_remove (guint tag)
{
GSource *source;
g_return_val_if_fail (tag > 0, FALSE);
source = g_main_context_find_source_by_id (NULL, tag);
if (source)
g_source_destroy (source);
else
g_critical ("Source ID %u was not found when attempting to remove it", tag);
return source != NULL;
}
/**
* g_source_remove_by_user_data:
* @user_data: the user_data for the callback.
*
* Removes a source from the default main loop context given the user
* data for the callback. If multiple sources exist with the same user
* data, only one will be destroyed.
*
* Returns: %TRUE if a source was found and removed.
**/
gboolean
g_source_remove_by_user_data (gpointer user_data)
{
GSource *source;
source = g_main_context_find_source_by_user_data (NULL, user_data);
if (source)
{
g_source_destroy (source);
return TRUE;
}
else
return FALSE;
}
/**
* g_source_remove_by_funcs_user_data:
* @funcs: The @source_funcs passed to g_source_new()
* @user_data: the user data for the callback
*
* Removes a source from the default main loop context given the
* source functions and user data. If multiple sources exist with the
* same source functions and user data, only one will be destroyed.
*
* Returns: %TRUE if a source was found and removed.
**/
gboolean
g_source_remove_by_funcs_user_data (GSourceFuncs *funcs,
gpointer user_data)
{
GSource *source;
g_return_val_if_fail (funcs != NULL, FALSE);
source = g_main_context_find_source_by_funcs_user_data (NULL, funcs, user_data);
if (source)
{
g_source_destroy (source);
return TRUE;
}
else
return FALSE;
}
/**
* g_clear_handle_id: (skip)
* @tag_ptr: (not nullable): a pointer to the handler ID
* @clear_func: (not nullable): the function to call to clear the handler
*
* Clears a numeric handler, such as a #GSource ID.
*
* @tag_ptr must be a valid pointer to the variable holding the handler.
*
* If the ID is zero then this function does nothing.
* Otherwise, clear_func() is called with the ID as a parameter, and the tag is
* set to zero.
*
* A macro is also included that allows this function to be used without
* pointer casts.
*
* Since: 2.56
*/
#undef g_clear_handle_id
void
g_clear_handle_id (guint *tag_ptr,
GClearHandleFunc clear_func)
{
guint _handle_id;
_handle_id = *tag_ptr;
if (_handle_id > 0)
{
*tag_ptr = 0;
clear_func (_handle_id);
}
}
#ifdef G_OS_UNIX
/**
* g_source_add_unix_fd:
* @source: a #GSource
* @fd: the fd to monitor
* @events: an event mask
*
* Monitors @fd for the IO events in @events.
*
* The tag returned by this function can be used to remove or modify the
* monitoring of the fd using g_source_remove_unix_fd() or
* g_source_modify_unix_fd().
*
* It is not necessary to remove the fd before destroying the source; it
* will be cleaned up automatically.
*
* This API is only intended to be used by implementations of #GSource.
* Do not call this API on a #GSource that you did not create.
*
* As the name suggests, this function is not available on Windows.
*
* Returns: (not nullable): an opaque tag
*
* Since: 2.36
**/
gpointer
g_source_add_unix_fd (GSource *source,
gint fd,
GIOCondition events)
{
GMainContext *context;
GPollFD *poll_fd;
g_return_val_if_fail (source != NULL, NULL);
g_return_val_if_fail (g_atomic_int_get (&source->ref_count) > 0, NULL);
g_return_val_if_fail (!SOURCE_DESTROYED (source), NULL);
poll_fd = g_new (GPollFD, 1);
poll_fd->fd = fd;
poll_fd->events = events;
poll_fd->revents = 0;
context = source->context;
if (context)
LOCK_CONTEXT (context);
source->priv->fds = g_slist_prepend (source->priv->fds, poll_fd);
if (context)
{
if (!SOURCE_BLOCKED (source))
g_main_context_add_poll_unlocked (context, source->priority, poll_fd);
UNLOCK_CONTEXT (context);
}
return poll_fd;
}
/**
* g_source_modify_unix_fd:
* @source: a #GSource
* @tag: (not nullable): the tag from g_source_add_unix_fd()
* @new_events: the new event mask to watch
*
* Updates the event mask to watch for the fd identified by @tag.
*
* @tag is the tag returned from g_source_add_unix_fd().
*
* If you want to remove a fd, don't set its event mask to zero.
* Instead, call g_source_remove_unix_fd().
*
* This API is only intended to be used by implementations of #GSource.
* Do not call this API on a #GSource that you did not create.
*
* As the name suggests, this function is not available on Windows.
*
* Since: 2.36
**/
void
g_source_modify_unix_fd (GSource *source,
gpointer tag,
GIOCondition new_events)
{
GMainContext *context;
GPollFD *poll_fd;
g_return_if_fail (source != NULL);
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
g_return_if_fail (g_slist_find (source->priv->fds, tag));
context = source->context;
poll_fd = tag;
poll_fd->events = new_events;
if (context)
g_main_context_wakeup (context);
}
/**
* g_source_remove_unix_fd:
* @source: a #GSource
* @tag: (not nullable): the tag from g_source_add_unix_fd()
*
* Reverses the effect of a previous call to g_source_add_unix_fd().
*
* You only need to call this if you want to remove an fd from being
* watched while keeping the same source around. In the normal case you
* will just want to destroy the source.
*
* This API is only intended to be used by implementations of #GSource.
* Do not call this API on a #GSource that you did not create.
*
* As the name suggests, this function is not available on Windows.
*
* Since: 2.36
**/
void
g_source_remove_unix_fd (GSource *source,
gpointer tag)
{
GMainContext *context;
GPollFD *poll_fd;
g_return_if_fail (source != NULL);
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
g_return_if_fail (g_slist_find (source->priv->fds, tag));
context = source->context;
poll_fd = tag;
if (context)
LOCK_CONTEXT (context);
source->priv->fds = g_slist_remove (source->priv->fds, poll_fd);
if (context)
{
if (!SOURCE_BLOCKED (source))
g_main_context_remove_poll_unlocked (context, poll_fd);
UNLOCK_CONTEXT (context);
}
g_free (poll_fd);
}
/**
* g_source_query_unix_fd:
* @source: a #GSource
* @tag: (not nullable): the tag from g_source_add_unix_fd()
*
* Queries the events reported for the fd corresponding to @tag on
* @source during the last poll.
*
* The return value of this function is only defined when the function
* is called from the check or dispatch functions for @source.
*
* This API is only intended to be used by implementations of #GSource.
* Do not call this API on a #GSource that you did not create.
*
* As the name suggests, this function is not available on Windows.
*
* Returns: the conditions reported on the fd
*
* Since: 2.36
**/
GIOCondition
g_source_query_unix_fd (GSource *source,
gpointer tag)
{
GPollFD *poll_fd;
g_return_val_if_fail (source != NULL, 0);
g_return_val_if_fail (g_atomic_int_get (&source->ref_count) > 0, 0);
g_return_val_if_fail (g_slist_find (source->priv->fds, tag), 0);
poll_fd = tag;
return poll_fd->revents;
}
#endif /* G_OS_UNIX */
/**
* g_get_current_time:
* @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.
*
* Deprecated: 2.62: #GTimeVal is not year-2038-safe. Use g_get_real_time()
* instead.
**/
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
void
g_get_current_time (GTimeVal *result)
{
gint64 tv;
g_return_if_fail (result != NULL);
tv = g_get_real_time ();
result->tv_sec = tv / 1000000;
result->tv_usec = tv % 1000000;
}
G_GNUC_END_IGNORE_DEPRECATIONS
/**
* 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.
*
* 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)
{
#ifndef G_OS_WIN32
struct timeval r;
/* this is required on alpha, there the timeval structs are ints
* not longs and a cast only would fail horribly */
gettimeofday (&r, NULL);
return (((gint64) r.tv_sec) * 1000000) + r.tv_usec;
#else
FILETIME ft;
guint64 time64;
GetSystemTimeAsFileTime (&ft);
memmove (&time64, &ft, sizeof (FILETIME));
/* Convert from 100s of nanoseconds since 1601-01-01
* to Unix epoch. This is Y2038 safe.
*/
time64 -= G_GINT64_CONSTANT (116444736000000000);
time64 /= 10;
return time64;
#endif
}
/**
* g_get_monotonic_time:
*
* Queries the system monotonic time.
*
* The monotonic clock will always increase and doesn't suffer
* discontinuities when the user (or NTP) changes the system time. It
* may or may not continue to tick during times where the machine is
* suspended.
*
* We try to use the clock that corresponds as closely as possible to
* the passage of time as measured by system calls such as poll() but it
* may not always be possible to do this.
*
* Returns: the monotonic time, in microseconds
*
* Since: 2.28
**/
#if defined (G_OS_WIN32)
/* NOTE:
* time_usec = ticks_since_boot * usec_per_sec / ticks_per_sec
*
* Doing (ticks_since_boot * usec_per_sec) before the division can overflow 64 bits
* (ticks_since_boot / ticks_per_sec) and then multiply would not be accurate enough.
* So for now we calculate (usec_per_sec / ticks_per_sec) and use floating point
*/
static gdouble g_monotonic_usec_per_tick = 0;
void
g_clock_win32_init (void)
{
LARGE_INTEGER freq;
if (!QueryPerformanceFrequency (&freq) || freq.QuadPart == 0)
{
/* The documentation says that this should never happen */
g_assert_not_reached ();
return;
}
g_monotonic_usec_per_tick = (gdouble)G_USEC_PER_SEC / freq.QuadPart;
}
gint64
g_get_monotonic_time (void)
{
if (G_LIKELY (g_monotonic_usec_per_tick != 0))
{
LARGE_INTEGER ticks;
if (QueryPerformanceCounter (&ticks))
return (gint64)(ticks.QuadPart * g_monotonic_usec_per_tick);
g_warning ("QueryPerformanceCounter Failed (%lu)", GetLastError ());
g_monotonic_usec_per_tick = 0;
}
return 0;
}
#elif defined(HAVE_MACH_MACH_TIME_H) /* Mac OS */
gint64
g_get_monotonic_time (void)
{
mach_timebase_info_data_t timebase_info;
guint64 val;
/* we get nanoseconds from mach_absolute_time() using timebase_info */
mach_timebase_info (&timebase_info);
val = mach_absolute_time ();
if (timebase_info.numer != timebase_info.denom)
{
#ifdef HAVE_UINT128_T
val = ((__uint128_t) val * (__uint128_t) timebase_info.numer) / timebase_info.denom / 1000;
#else
guint64 t_high, t_low;
guint64 result_high, result_low;
/* 64 bit x 32 bit / 32 bit with 96-bit intermediate
* algorithm lifted from qemu */
t_low = (val & 0xffffffffLL) * (guint64) timebase_info.numer;
t_high = (val >> 32) * (guint64) timebase_info.numer;
t_high += (t_low >> 32);
result_high = t_high / (guint64) timebase_info.denom;
result_low = (((t_high % (guint64) timebase_info.denom) << 32) +
(t_low & 0xffffffff)) /
(guint64) timebase_info.denom;
val = ((result_high << 32) | result_low) / 1000;
#endif
}
else
{
/* nanoseconds to microseconds */
val = val / 1000;
}
return val;
}
#else
gint64
g_get_monotonic_time (void)
{
struct timespec ts;
gint result;
result = clock_gettime (CLOCK_MONOTONIC, &ts);
if G_UNLIKELY (result != 0)
g_error ("GLib requires working CLOCK_MONOTONIC");
return (((gint64) ts.tv_sec) * 1000000) + (ts.tv_nsec / 1000);
}
#endif
static void
g_main_dispatch_free (gpointer dispatch)
{
g_free (dispatch);
}
/* Running the main loop */
static GMainDispatch *
get_dispatch (void)
{
static GPrivate depth_private = G_PRIVATE_INIT (g_main_dispatch_free);
GMainDispatch *dispatch;
dispatch = g_private_get (&depth_private);
if (!dispatch)
dispatch = g_private_set_alloc0 (&depth_private, sizeof (GMainDispatch));
return dispatch;
}
/**
* g_main_depth:
*
* Returns the depth of the stack of calls to
* g_main_context_dispatch() on any #GMainContext in the current thread.
* 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_iteration(),
* it returns 2. And so forth.
*
* This function is useful in a situation like the following:
* Imagine an extremely simple "garbage collected" system.
*
* |[<!-- language="C" -->
* static GList *free_list;
*
* gpointer
* allocate_memory (gsize size)
* {
* gpointer result = g_malloc (size);
* free_list = g_list_prepend (free_list, result);
* return result;
* }
*
* void
* free_allocated_memory (void)
* {
* GList *l;
* for (l = free_list; l; l = l->next);
* g_free (l->data);
* g_list_free (free_list);
* free_list = NULL;
* }
*
* [...]
*
* while (TRUE);
* {
* g_main_context_iteration (NULL, TRUE);
* free_allocated_memory();
* }
* ]|
*
* This works from an application, however, if you want to do the same
* thing from a library, it gets more difficult, since you no longer
* control the main loop. You might think you can simply use an idle
* function to make the call to free_allocated_memory(), but that
* doesn't work, since the idle function could be called from a
* recursive callback. This can be fixed by using g_main_depth()
*
* |[<!-- language="C" -->
* gpointer
* allocate_memory (gsize size)
* {
* FreeListBlock *block = g_new (FreeListBlock, 1);
* block->mem = g_malloc (size);
* block->depth = g_main_depth ();
* free_list = g_list_prepend (free_list, block);
* return block->mem;
* }
*
* void
* free_allocated_memory (void)
* {
* GList *l;
*
* int depth = g_main_depth ();
* for (l = free_list; l; );
* {
* GList *next = l->next;
* FreeListBlock *block = l->data;
* if (block->depth > depth)
* {
* g_free (block->mem);
* g_free (block);
* free_list = g_list_delete_link (free_list, l);
* }
*
* l = next;
* }
* }
* ]|
*
* There is a temptation to use g_main_depth() to solve
* problems with reentrancy. For instance, while waiting for data
* to be received from the network in response to a menu item,
* the menu item might be selected again. It might seem that
* one could make the menu item's callback return immediately
* and do nothing if g_main_depth() returns a value greater than 1.
* However, this should be avoided since the user then sees selecting
* the menu item do nothing. Furthermore, you'll find yourself adding
* these checks all over your code, since there are doubtless many,
* many things that the user could do. Instead, you can use the
* following techniques:
*
* 1. Use gtk_widget_set_sensitive() or modal dialogs to prevent
* the user from interacting with elements while the main
* loop is recursing.
*
* 2. Avoid main loop recursion in situations where you can't handle
* arbitrary callbacks. Instead, structure your code so that you
* simply return to the main loop and then get called again when
* there is more work to do.
*
* Returns: The main loop recursion level in the current thread
*/
int
g_main_depth (void)
{
GMainDispatch *dispatch = get_dispatch ();
return dispatch->depth;
}
/**
* g_main_current_source:
*
* Returns the currently firing source for this thread.
*
* Returns: (transfer none) (nullable): The currently firing source or %NULL.
*
* Since: 2.12
*/
GSource *
g_main_current_source (void)
{
GMainDispatch *dispatch = get_dispatch ();
return dispatch->source;
}
/**
* g_source_is_destroyed:
* @source: a #GSource
*
* Returns whether @source has been destroyed.
*
* This is important when you operate upon your objects
* from within idle handlers, but may have freed the object
* before the dispatch of your idle handler.
*
* |[<!-- language="C" -->
* static gboolean
* idle_callback (gpointer data)
* {
* SomeWidget *self = data;
*
* g_mutex_lock (&self->idle_id_mutex);
* // do stuff with self
* self->idle_id = 0;
* g_mutex_unlock (&self->idle_id_mutex);
*
* return G_SOURCE_REMOVE;
* }
*
* static void
* some_widget_do_stuff_later (SomeWidget *self)
* {
* g_mutex_lock (&self->idle_id_mutex);
* self->idle_id = g_idle_add (idle_callback, self);
* g_mutex_unlock (&self->idle_id_mutex);
* }
*
* static void
* some_widget_init (SomeWidget *self)
* {
* g_mutex_init (&self->idle_id_mutex);
*
* // ...
* }
*
* static void
* some_widget_finalize (GObject *object)
* {
* SomeWidget *self = SOME_WIDGET (object);
*
* if (self->idle_id)
* g_source_remove (self->idle_id);
*
* g_mutex_clear (&self->idle_id_mutex);
*
* G_OBJECT_CLASS (parent_class)->finalize (object);
* }
* ]|
*
* This will fail in a multi-threaded application if the
* widget is destroyed before the idle handler fires due
* to the use after free in the callback. A solution, to
* this particular problem, is to check to if the source
* has already been destroy within the callback.
*
* |[<!-- language="C" -->
* static gboolean
* idle_callback (gpointer data)
* {
* SomeWidget *self = data;
*
* g_mutex_lock (&self->idle_id_mutex);
* if (!g_source_is_destroyed (g_main_current_source ()))
* {
* // do stuff with self
* }
* g_mutex_unlock (&self->idle_id_mutex);
*
* return FALSE;
* }
* ]|
*
* Calls to this function from a thread other than the one acquired by the
* #GMainContext the #GSource is attached to are typically redundant, as the
* source could be destroyed immediately after this function returns. However,
* once a source is destroyed it cannot be un-destroyed, so this function can be
* used for opportunistic checks from any thread.
*
* Returns: %TRUE if the source has been destroyed
*
* Since: 2.12
*/
gboolean
g_source_is_destroyed (GSource *source)
{
g_return_val_if_fail (source != NULL, TRUE);
g_return_val_if_fail (g_atomic_int_get (&source->ref_count) > 0, TRUE);
return SOURCE_DESTROYED (source);
}
/* Temporarily remove all this source's file descriptors from the
* poll(), so that if data comes available for one of the file descriptors
* we don't continually spin in the poll()
*/
/* HOLDS: source->context's lock */
static void
block_source (GSource *source)
{
GSList *tmp_list;
g_return_if_fail (!SOURCE_BLOCKED (source));
source->flags |= G_SOURCE_BLOCKED;
if (source->context)
{
tmp_list = source->poll_fds;
while (tmp_list)
{
g_main_context_remove_poll_unlocked (source->context, tmp_list->data);
tmp_list = tmp_list->next;
}
for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
g_main_context_remove_poll_unlocked (source->context, tmp_list->data);
}
if (source->priv && source->priv->child_sources)
{
tmp_list = source->priv->child_sources;
while (tmp_list)
{
block_source (tmp_list->data);
tmp_list = tmp_list->next;
}
}
}
/* HOLDS: source->context's lock */
static void
unblock_source (GSource *source)
{
GSList *tmp_list;
g_return_if_fail (SOURCE_BLOCKED (source)); /* Source already unblocked */
g_return_if_fail (!SOURCE_DESTROYED (source));
source->flags &= ~G_SOURCE_BLOCKED;
tmp_list = source->poll_fds;
while (tmp_list)
{
g_main_context_add_poll_unlocked (source->context, source->priority, tmp_list->data);
tmp_list = tmp_list->next;
}
for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
g_main_context_add_poll_unlocked (source->context, source->priority, tmp_list->data);
if (source->priv && source->priv->child_sources)
{
tmp_list = source->priv->child_sources;
while (tmp_list)
{
unblock_source (tmp_list->data);
tmp_list = tmp_list->next;
}
}
}
/* HOLDS: context's lock */
static void
g_main_dispatch (GMainContext *context)
{
GMainDispatch *current = get_dispatch ();
guint i;
for (i = 0; i < context->pending_dispatches->len; i++)
{
GSource *source = context->pending_dispatches->pdata[i];
context->pending_dispatches->pdata[i] = NULL;
g_assert (source);
source->flags &= ~G_SOURCE_READY;
if (!SOURCE_DESTROYED (source))
{
gboolean was_in_call;
gpointer user_data = NULL;
GSourceFunc callback = NULL;
GSourceCallbackFuncs *cb_funcs;
gpointer cb_data;
gboolean need_destroy;
gboolean (*dispatch) (GSource *,
GSourceFunc,
gpointer);
GSource *prev_source;
gint64 begin_time_nsec G_GNUC_UNUSED;
dispatch = source->source_funcs->dispatch;
cb_funcs = source->callback_funcs;
cb_data = source->callback_data;
if (cb_funcs)
cb_funcs->ref (cb_data);
if ((source->flags & G_SOURCE_CAN_RECURSE) == 0)
block_source (source);
was_in_call = source->flags & G_HOOK_FLAG_IN_CALL;
source->flags |= G_HOOK_FLAG_IN_CALL;
if (cb_funcs)
cb_funcs->get (cb_data, source, &callback, &user_data);
UNLOCK_CONTEXT (context);
/* These operations are safe because 'current' is thread-local
* and not modified from anywhere but this function.
*/
prev_source = current->source;
current->source = source;
current->depth++;
begin_time_nsec = G_TRACE_CURRENT_TIME;
TRACE (GLIB_MAIN_BEFORE_DISPATCH (g_source_get_name (source), source,
dispatch, callback, user_data));
need_destroy = !(* dispatch) (source, callback, user_data);
TRACE (GLIB_MAIN_AFTER_DISPATCH (g_source_get_name (source), source,
dispatch, need_destroy));
g_trace_mark (begin_time_nsec, G_TRACE_CURRENT_TIME - begin_time_nsec,
"GLib", "GSource.dispatch",
"%s ⇒ %s",
(g_source_get_name (source) != NULL) ? g_source_get_name (source) : "(unnamed)",
need_destroy ? "destroy" : "keep");
current->source = prev_source;
current->depth--;
if (cb_funcs)
cb_funcs->unref (cb_data);
LOCK_CONTEXT (context);
if (!was_in_call)
source->flags &= ~G_HOOK_FLAG_IN_CALL;
if (SOURCE_BLOCKED (source) && !SOURCE_DESTROYED (source))
unblock_source (source);
/* Note: this depends on the fact that we can't switch
* sources from one main context to another
*/
if (need_destroy && !SOURCE_DESTROYED (source))
{
g_assert (source->context == context);
g_source_destroy_internal (source, context, TRUE);
}
}
g_source_unref_internal (source, context, TRUE);
}
g_ptr_array_set_size (context->pending_dispatches, 0);
}
/**
* g_main_context_acquire:
* @context: (nullable): a #GMainContext (if %NULL, the global-default
* main context will be used)
*
* Tries to become the owner of the specified context.
* If some other thread is the owner of the context,
* returns %FALSE immediately. Ownership is properly
* recursive: the owner can require ownership again
* and will release ownership when g_main_context_release()
* is called as many times as g_main_context_acquire().
*
* You must be the owner of a context before you
* can call g_main_context_prepare(), g_main_context_query(),
* g_main_context_check(), g_main_context_dispatch(), g_main_context_release().
*
* Since 2.76 @context can be %NULL to use the global-default
* main context.
*
* Returns: %TRUE if the operation succeeded, and
* this thread is now the owner of @context.
**/
gboolean
g_main_context_acquire (GMainContext *context)
{
gboolean result = FALSE;
if (context == NULL)
context = g_main_context_default ();
LOCK_CONTEXT (context);
result = g_main_context_acquire_unlocked (context);
UNLOCK_CONTEXT (context);
return result;
}
static gboolean
g_main_context_acquire_unlocked (GMainContext *context)
{
GThread *self = G_THREAD_SELF;
if (!context->owner)
{
context->owner = self;
g_assert (context->owner_count == 0);
TRACE (GLIB_MAIN_CONTEXT_ACQUIRE (context, TRUE /* success */));
}
if (context->owner == self)
{
context->owner_count++;
return TRUE;
}
else
{
TRACE (GLIB_MAIN_CONTEXT_ACQUIRE (context, FALSE /* failure */));
return FALSE;
}
}
/**
* g_main_context_release:
* @context: (nullable): a #GMainContext (if %NULL, the global-default
* main context will be used)
*
* Releases ownership of a context previously acquired by this thread
* with g_main_context_acquire(). If the context was acquired multiple
* times, the ownership will be released only when g_main_context_release()
* is called as many times as it was acquired.
*
* You must have successfully acquired the context with
* g_main_context_acquire() before you may call this function.
**/
void
g_main_context_release (GMainContext *context)
{
if (context == NULL)
context = g_main_context_default ();
LOCK_CONTEXT (context);
g_main_context_release_unlocked (context);
UNLOCK_CONTEXT (context);
}
static void
g_main_context_release_unlocked (GMainContext *context)
{
/* NOTE: We should also have the following assert here:
* g_return_if_fail (context->owner == G_THREAD_SELF);
* However, this breaks NetworkManager, which has been (non-compliantly but
* apparently safely) releasing a #GMainContext from a thread which didn’t
* acquire it.
* Breaking that would be quite disruptive, so we won’t do that now. However,
* GLib reserves the right to add that assertion in future, if doing so would
* allow for optimisations or refactorings. By that point, NetworkManager will
* have to have reworked its use of #GMainContext.
*
* See: https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3513
*/
g_return_if_fail (context->owner_count > 0);
context->owner_count--;
if (context->owner_count == 0)
{
TRACE (GLIB_MAIN_CONTEXT_RELEASE (context));
context->owner = NULL;
if (context->waiters)
{
GMainWaiter *waiter = context->waiters->data;
gboolean loop_internal_waiter = (waiter->mutex == &context->mutex);
context->waiters = g_slist_delete_link (context->waiters,
context->waiters);
if (!loop_internal_waiter)
g_mutex_lock (waiter->mutex);
g_cond_signal (waiter->cond);
if (!loop_internal_waiter)
g_mutex_unlock (waiter->mutex);
}
}
}
static gboolean
g_main_context_wait_internal (GMainContext *context,
GCond *cond,
GMutex *mutex)
{
gboolean result = FALSE;
GThread *self = G_THREAD_SELF;
gboolean loop_internal_waiter;
loop_internal_waiter = (mutex == &context->mutex);
if (!loop_internal_waiter)
LOCK_CONTEXT (context);
if (context->owner && context->owner != self)
{
GMainWaiter waiter;
waiter.cond = cond;
waiter.mutex = mutex;
context->waiters = g_slist_append (context->waiters, &waiter);
if (!loop_internal_waiter)
UNLOCK_CONTEXT (context);
g_cond_wait (cond, mutex);
if (!loop_internal_waiter)
LOCK_CONTEXT (context);
context->waiters = g_slist_remove (context->waiters, &waiter);
}
if (!context->owner)
{
context->owner = self;
g_assert (context->owner_count == 0);
}
if (context->owner == self)
{
context->owner_count++;
result = TRUE;
}
if (!loop_internal_waiter)
UNLOCK_CONTEXT (context);
return result;
}
/**
* g_main_context_wait:
* @context: (nullable): a #GMainContext (if %NULL, the global-default
* main context will be used)
* @cond: a condition variable
* @mutex: a mutex, currently held
*
* Tries to become the owner of the specified context,
* as with g_main_context_acquire(). But if another thread
* is the owner, atomically drop @mutex and wait on @cond until
* that owner releases ownership or until @cond is signaled, then
* try again (once) to become the owner.