Add g_main_context_push_thread_default() etc

This allows applications to use async methods from other threads, or
in multiple independent main loops.

http://bugzilla.gnome.org/show_bug.cgi?id=579984
This commit is contained in:
Dan Winship 2009-06-04 21:59:15 -04:00
parent 90381ecdbd
commit 4363f1932f
5 changed files with 177 additions and 4 deletions

View File

@ -462,6 +462,11 @@ g_main_depth
g_main_current_source
g_main_set_poll_func
<SUBSECTION>
g_main_context_get_thread_default
g_main_context_push_thread_default
g_main_context_pop_thread_default
<SUBSECTION>
g_timeout_source_new
g_timeout_source_new_seconds

View File

@ -545,6 +545,30 @@ for the default main context.
@Deprecated: 2.2: Use g_main_context_set_poll_func() instead.
<!-- ##### FUNCTION g_main_context_get_thread_default ##### -->
<para>
</para>
@Returns:
<!-- ##### FUNCTION g_main_context_push_thread_default ##### -->
<para>
</para>
@context:
<!-- ##### FUNCTION g_main_context_pop_thread_default ##### -->
<para>
</para>
@context:
<!-- ##### FUNCTION g_timeout_source_new ##### -->
<para>

View File

@ -622,11 +622,14 @@ g_main_context_find_source_by_funcs_user_data
g_main_context_find_source_by_id
g_main_context_find_source_by_user_data
g_main_context_get_poll_func
g_main_context_get_thread_default
g_main_context_is_owner
g_main_context_iteration
g_main_context_new
g_main_context_pending
g_main_context_pop_thread_default
g_main_context_prepare
g_main_context_push_thread_default
g_main_context_query
g_main_context_ref
g_main_context_release

View File

@ -529,11 +529,12 @@ g_main_context_new (void)
/**
* g_main_context_default:
*
* Returns the default main context. This is the main context used
* for main loop functions when a main loop is not explicitly
* specified.
* 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().
*
* Return value: the default main context.
* Return value: the global default main context.
**/
GMainContext *
g_main_context_default (void)
@ -556,6 +557,141 @@ g_main_context_default (void)
return default_main_context;
}
static GStaticPrivate thread_context_stack = G_STATIC_PRIVATE_INIT;
static void
free_context_stack (gpointer data)
{
GQueue *stack = data;
GMainContext *context;
while (!g_queue_is_empty (stack))
{
context = g_queue_pop_head (stack);
g_main_context_release (context);
if (context)
g_main_context_unref (context);
}
g_queue_free (stack);
}
/**
* g_main_context_push_thread_default:
* @context: a #GMainContext, or %NULL for the global default context
*
* Acquires @context and sets it as the thread-default context for the
* current thread. This will cause certain asynchronous operations
* (such as most <link linkend="gio">gio</link>-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 context in the main thread. Note that calling this function
* changes the context returned by
* g_main_context_get_thread_default(), <emphasis>not</emphasis> the
* one returned by g_main_context_default(), so it does not affect the
* context used by functions like g_idle_add().
*
* 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 don't need to
* ever call g_main_context_pop_thread_default().) In some cases
* however, 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_static_private_get (&thread_context_stack);
if (!stack)
{
stack = g_queue_new ();
g_static_private_set (&thread_context_stack, stack,
free_context_stack);
}
g_queue_push_head (stack, context);
}
/**
* g_main_context_pop_thread_default:
* @context: a #GMainContext object, or %NULL
*
* 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_static_private_get (&thread_context_stack);
g_return_if_fail (stack != NULL);
g_return_if_fail (g_queue_peek_head (stack) == 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 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.)
*
* Returns: the thread-default #GMainContext, or %NULL if the
* thread-default context is the global default context.
*
* Since: 2.22
**/
GMainContext *
g_main_context_get_thread_default (void)
{
GQueue *stack;
stack = g_static_private_get (&thread_context_stack);
if (stack)
return g_queue_peek_head (stack);
else
return NULL;
}
/* Hooks for adding to the main loop */
/**

View File

@ -158,6 +158,11 @@ void g_main_context_remove_poll (GMainContext *context,
gint g_main_depth (void);
GSource *g_main_current_source (void);
/* GMainContexts for other threads
*/
void g_main_context_push_thread_default (GMainContext *context);
void g_main_context_pop_thread_default (GMainContext *context);
GMainContext *g_main_context_get_thread_default (void);
/* GMainLoop: */