|
Packit |
1470ea |
|
|
Packit |
1470ea |
<page xmlns="http://projectmallard.org/1.0/" xmlns:its="http://www.w3.org/2005/11/its" xmlns:xi="http://www.w3.org/2003/XInclude" type="topic" id="main-contexts" xml:lang="es">
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<info>
|
|
Packit |
1470ea |
<link type="guide" xref="index#specific-how-tos"/>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<credit type="author copyright">
|
|
Packit |
1470ea |
<name>Philip Withnall</name>
|
|
Packit |
1470ea |
<email its:translate="no">philip.withnall@collabora.co.uk</email>
|
|
Packit |
1470ea |
<years>2014–2015</years>
|
|
Packit |
1470ea |
</credit>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<include xmlns="http://www.w3.org/2001/XInclude" href="cc-by-sa-3-0.xml"/>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<desc>
|
|
Packit |
1470ea |
GLib main contexts, invoking functions in other threads, and the event
|
|
Packit |
1470ea |
loop
|
|
Packit |
1470ea |
</desc>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
|
|
Packit |
1470ea |
<mal:name>Daniel Mustieles</mal:name>
|
|
Packit |
1470ea |
<mal:email>daniel.mustieles@gmail.com</mal:email>
|
|
Packit |
1470ea |
<mal:years>2016</mal:years>
|
|
Packit |
1470ea |
</mal:credit>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
|
|
Packit |
1470ea |
<mal:name>Javier Mazorra</mal:name>
|
|
Packit |
1470ea |
<mal:email>mazi.debian@gmail.com</mal:email>
|
|
Packit |
1470ea |
<mal:years>2016</mal:years>
|
|
Packit |
1470ea |
</mal:credit>
|
|
Packit |
1470ea |
</info>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<title>GLib Main Contexts</title>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<synopsis>
|
|
Packit |
1470ea |
<title>Resumen</title>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<list>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
Use
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-main-context-invoke-full">g_main_context_invoke_full() </link>
|
|
Packit |
1470ea |
to invoke functions in other threads, assuming every thread has a thread
|
|
Packit |
1470ea |
default main context which runs throughout the lifetime of that thread
|
|
Packit |
1470ea |
(<link xref="#g-main-context-invoke-full"/>)
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
Use
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/gio/stable/GTask.html">GTask </link>
|
|
Packit |
1470ea |
to run a function in the background without caring about the specific
|
|
Packit |
1470ea |
thread used (<link xref="#gtask"/>)
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
Liberally use assertions to check which context executes each function,
|
|
Packit |
1470ea |
and add these assertions when first writing the code
|
|
Packit |
1470ea |
(<link xref="#checking-threading"/>)
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
Explicitly document contexts a function is expected to be called in, a
|
|
Packit |
1470ea |
callback will be invoked in, or a signal will be emitted in
|
|
Packit |
1470ea |
(<link xref="#using-gmaincontext-in-a-library"/>)
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
Beware of g_idle_add() and similar functions which
|
|
Packit |
1470ea |
implicitly use the global-default main context
|
|
Packit |
1470ea |
(<link xref="#implicit-use-of-the-global-default-main-context"/>)
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
</list>
|
|
Packit |
1470ea |
</synopsis>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<section id="what-is-gmaincontext">
|
|
Packit |
1470ea |
<title>¿Qué es GMainContext ?</title>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#GMainContext">GMainContext </link>
|
|
Packit |
1470ea |
is a generalized implementation of an
|
|
Packit |
1470ea |
<link href="http://en.wikipedia.org/wiki/Event_loop">event loop</link>,
|
|
Packit |
1470ea |
useful for implementing polled file I/O or event-based widget systems
|
|
Packit |
1470ea |
(such as GTK+). It is at the core of almost every GLib application. To
|
|
Packit |
1470ea |
understand GMainContext requires understanding
|
|
Packit |
1470ea |
<link href="man:poll(2)">poll()</link> and polled I/O.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
A GMainContext has a set of
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#GSource">GSource </link>s
|
|
Packit |
1470ea |
which are ‘attached’ to it, each of which can be thought of as an expected
|
|
Packit |
1470ea |
event with an associated callback function which will be invoked when that
|
|
Packit |
1470ea |
event is received; or equivalently as a set of file descriptors (FDs) to
|
|
Packit |
1470ea |
check. An event could be a timeout or data being received on a socket, for
|
|
Packit |
1470ea |
example. One iteration of the event loop will:
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<list type="enumerated">
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
Prepare sources, determining if any of them are ready to dispatch
|
|
Packit |
1470ea |
immediately.
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
Poll the sources, blocking the current thread until an event is received
|
|
Packit |
1470ea |
for one of the sources.
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
Check which of the sources received an event (several could have).
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
Dispatch callbacks from those sources.
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
</list>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
This is
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#mainloop-states">explained
|
|
Packit |
1470ea |
very well</link> in the
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#GSourceFuncs">GLib
|
|
Packit |
1470ea |
documentation</link>.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
At its core, GMainContext is just a poll() loop,
|
|
Packit |
1470ea |
with the preparation, check and dispatch stages of the loop corresponding
|
|
Packit |
1470ea |
to the normal preamble and postamble in a typical poll() loop
|
|
Packit |
1470ea |
implementation, such as listing 1 from
|
|
Packit |
1470ea |
<link href="http://www.linux-mag.com/id/357/">this article</link>.
|
|
Packit |
1470ea |
Typically, some complexity is needed in non-trivial
|
|
Packit |
1470ea |
poll() -using applications to track the lists of FDs which are
|
|
Packit |
1470ea |
being polled. Additionally, GMainContext adds a lot of useful
|
|
Packit |
1470ea |
functionality which vanilla poll() doesn’t support. Most
|
|
Packit |
1470ea |
importantly, it adds thread safety.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
GMainContext is completely thread safe, meaning that a
|
|
Packit |
1470ea |
GSource can be created in one thread and attached to a
|
|
Packit |
1470ea |
GMainContext running in another thread. (See
|
|
Packit |
1470ea |
also: <link xref="threading"/>.) A typical use for this might be to allow
|
|
Packit |
1470ea |
worker threads to control which sockets are being listened to by a
|
|
Packit |
1470ea |
GMainContext in a central I/O thread. Each
|
|
Packit |
1470ea |
GMainContext is ‘acquired’ by a thread for each iteration
|
|
Packit |
1470ea |
it’s put through. Other threads cannot iterate a GMainContext
|
|
Packit |
1470ea |
without acquiring it, which guarantees that a GSource and its
|
|
Packit |
1470ea |
FDs will only be polled by one thread at once (since each
|
|
Packit |
1470ea |
GSource is attached to at most one
|
|
Packit |
1470ea |
GMainContext ). A GMainContext can be swapped
|
|
Packit |
1470ea |
between threads across iterations, but this is expensive.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
GMainContext is used instead of poll() mostly
|
|
Packit |
1470ea |
for convenience, as it transparently handles dynamically managing
|
|
Packit |
1470ea |
the array of FDs to pass to poll() , especially when operating
|
|
Packit |
1470ea |
over multiple threads. This is done by encapsulating FDs in
|
|
Packit |
1470ea |
GSource s, which decide whether those FDs should be passed to
|
|
Packit |
1470ea |
the poll() call on each ‘prepare’ stage of the main context
|
|
Packit |
1470ea |
iteration.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
</section>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<section id="what-is-gmainloop">
|
|
Packit |
1470ea |
<title>¿Qué es GMainLoop ?</title>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#GMainLoop">GMainLoop </link>
|
|
Packit |
1470ea |
is essentially the following few lines of code, once reference counting
|
|
Packit |
1470ea |
and locking have been removed (from
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-main-loop-run">g_main_loop_run() </link>):
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
loop->is_running = TRUE;
|
|
Packit |
1470ea |
while (loop->is_running)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
g_main_context_iteration (context, TRUE);
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Plus a fourth line in
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-main-loop-quit">g_main_loop_quit() </link>
|
|
Packit |
1470ea |
which sets loop->is_running = FALSE and which will cause
|
|
Packit |
1470ea |
the loop to terminate once the current main context iteration ends.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Hence, GMainLoop is a convenient, thread-safe way of running
|
|
Packit |
1470ea |
a GMainContext to process events until a desired exit
|
|
Packit |
1470ea |
condition is met, at which point g_main_loop_quit() should be
|
|
Packit |
1470ea |
called. Typically, in a UI program, this will be the user clicking ‘exit’.
|
|
Packit |
1470ea |
In a socket handling program, this might be the final socket closing.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
It is important not to confuse main contexts with main loops. Main
|
|
Packit |
1470ea |
contexts do the bulk of the work: preparing source lists, waiting for
|
|
Packit |
1470ea |
events, and dispatching callbacks. A main loop simply iterates a context.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
</section>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<section id="default-contexts">
|
|
Packit |
1470ea |
<title>Contextos predeterminados</title>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
One of the important features of GMainContext is its support
|
|
Packit |
1470ea |
for ‘default’ contexts. There are two levels of default context: the
|
|
Packit |
1470ea |
thread-default, and the global-default. The global-default (accessed using
|
|
Packit |
1470ea |
g_main_context_default() ) is run by GTK+ when
|
|
Packit |
1470ea |
gtk_main() is called. It’s also used for timeouts
|
|
Packit |
1470ea |
(g_timeout_add() ) and idle callbacks
|
|
Packit |
1470ea |
(g_idle_add() ) — these won’t be dispatched unless the default
|
|
Packit |
1470ea |
context is running! (See:
|
|
Packit |
1470ea |
<link xref="#implicit-use-of-the-global-default-main-context"/>.)
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Thread-default contexts are a later addition to GLib (since version 2.22),
|
|
Packit |
1470ea |
and are generally used for I/O operations which need to run and dispatch
|
|
Packit |
1470ea |
callbacks in a thread. By calling
|
|
Packit |
1470ea |
g_main_context_push_thread_default() before starting an I/O
|
|
Packit |
1470ea |
operation, the thread-default context is set and the I/O operation can add
|
|
Packit |
1470ea |
its sources to that context. The context can then be run in a new main
|
|
Packit |
1470ea |
loop in an I/O thread, causing the callbacks to be dispatched on that
|
|
Packit |
1470ea |
thread’s stack rather than on the stack of the thread running the
|
|
Packit |
1470ea |
global-default main context. This allows I/O operations to be run entirely
|
|
Packit |
1470ea |
in a separate thread without explicitly passing a specific
|
|
Packit |
1470ea |
GMainContext pointer around everywhere.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Conversely, by starting a long-running operation with a specific
|
|
Packit |
1470ea |
thread-default context set, the calling code can guarantee that the
|
|
Packit |
1470ea |
operation’s callbacks will be emitted in that context, even if the
|
|
Packit |
1470ea |
operation itself runs in a worker thread. This is the principle behind
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/gio/stable/GTask.html">GTask </link>:
|
|
Packit |
1470ea |
when a new GTask is created, it stores a reference to the
|
|
Packit |
1470ea |
current thread-default context, and dispatches its completion callback in
|
|
Packit |
1470ea |
that context, even if the task itself is run using
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/gio/stable/GTask.html#g-task-run-in-thread">g_task_run_in_thread() </link>.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<example>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
For example, the code below will run a GTask which performs
|
|
Packit |
1470ea |
two writes in parallel from a thread. The callbacks for the writes will
|
|
Packit |
1470ea |
be dispatched in the worker thread, whereas the callback from the task
|
|
Packit |
1470ea |
as a whole will be dispatched in the interesting_context .
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
typedef struct {
|
|
Packit |
1470ea |
GMainLoop *main_loop;
|
|
Packit |
1470ea |
guint n_remaining;
|
|
Packit |
1470ea |
} WriteData;
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* This is always called in the same thread as thread_cb() because
|
|
Packit |
1470ea |
* it’s always dispatched in the @worker_context. */
|
|
Packit |
1470ea |
static void
|
|
Packit |
1470ea |
write_cb (GObject *source_object,
|
|
Packit |
1470ea |
GAsyncResult *result,
|
|
Packit |
1470ea |
gpointer user_data)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
WriteData *data = user_data;
|
|
Packit |
1470ea |
GOutputStream *stream = G_OUTPUT_STREAM (source_object);
|
|
Packit |
1470ea |
GError *error = NULL;
|
|
Packit |
1470ea |
gssize len;
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Finish the write. */
|
|
Packit |
1470ea |
len = g_output_stream_write_finish (stream, result, &error);
|
|
Packit |
1470ea |
if (error != NULL)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
g_error ("Error: %s", error->message);
|
|
Packit |
1470ea |
g_error_free (error);
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Check whether all parallel operations have finished. */
|
|
Packit |
1470ea |
write_data->n_remaining--;
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
if (write_data->n_remaining == 0)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
g_main_loop_quit (write_data->main_loop);
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* This is called in a new thread. */
|
|
Packit |
1470ea |
static void
|
|
Packit |
1470ea |
thread_cb (GTask *task,
|
|
Packit |
1470ea |
gpointer source_object,
|
|
Packit |
1470ea |
gpointer task_data,
|
|
Packit |
1470ea |
GCancellable *cancellable)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
/* These streams come from somewhere else in the program: */
|
|
Packit |
1470ea |
GOutputStream *output_stream1, *output_stream;
|
|
Packit |
1470ea |
GMainContext *worker_context;
|
|
Packit |
1470ea |
GBytes *data;
|
|
Packit |
1470ea |
const guint8 *buf;
|
|
Packit |
1470ea |
gsize len;
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Set up a worker context for the writes’ callbacks. */
|
|
Packit |
1470ea |
worker_context = g_main_context_new ();
|
|
Packit |
1470ea |
g_main_context_push_thread_default (worker_context);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Set up the writes. */
|
|
Packit |
1470ea |
write_data.n_remaining = 2;
|
|
Packit |
1470ea |
write_data.main_loop = g_main_loop_new (worker_context, FALSE);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
data = g_task_get_task_data (task);
|
|
Packit |
1470ea |
buf = g_bytes_get_data (data, &len);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
g_output_stream_write_async (output_stream1, buf, len,
|
|
Packit |
1470ea |
G_PRIORITY_DEFAULT, NULL, write_cb,
|
|
Packit |
1470ea |
&write_data);
|
|
Packit |
1470ea |
g_output_stream_write_async (output_stream2, buf, len,
|
|
Packit |
1470ea |
G_PRIORITY_DEFAULT, NULL, write_cb,
|
|
Packit |
1470ea |
&write_data);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Run the main loop until both writes have finished. */
|
|
Packit |
1470ea |
g_main_loop_run (write_data.main_loop);
|
|
Packit |
1470ea |
g_task_return_boolean (task, TRUE); /* ignore errors */
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
g_main_loop_unref (write_data.main_loop);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
g_main_context_pop_thread_default (worker_context);
|
|
Packit |
1470ea |
g_main_context_unref (worker_context);
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* This can be called from any thread. Its @callback will always be
|
|
Packit |
1470ea |
* dispatched in the thread which currently owns
|
|
Packit |
1470ea |
* @interesting_context. */
|
|
Packit |
1470ea |
void
|
|
Packit |
1470ea |
parallel_writes_async (GBytes *data,
|
|
Packit |
1470ea |
GMainContext *interesting_context,
|
|
Packit |
1470ea |
GCancellable *cancellable,
|
|
Packit |
1470ea |
GAsyncReadyCallback callback,
|
|
Packit |
1470ea |
gpointer user_data)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
GTask *task;
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
g_main_context_push_thread_default (interesting_context);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
task = g_task_new (NULL, cancellable, callback, user_data);
|
|
Packit |
1470ea |
g_task_set_task_data (task, data,
|
|
Packit |
1470ea |
(GDestroyNotify) g_bytes_unref);
|
|
Packit |
1470ea |
g_task_run_in_thread (task, thread_cb);
|
|
Packit |
1470ea |
g_object_unref (task);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
g_main_context_pop_thread_default (interesting_context);
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
</example>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<section id="implicit-use-of-the-global-default-main-context">
|
|
Packit |
1470ea |
<title>Implicit Use of the Global-Default Main Context</title>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Several functions implicitly add sources to the global-default main
|
|
Packit |
1470ea |
context. They should not be used in threaded code. Instead, use
|
|
Packit |
1470ea |
g_source_attach() with the GSource created by
|
|
Packit |
1470ea |
the replacement function from the table below.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Implicit use of the global-default main context means the callback
|
|
Packit |
1470ea |
functions are invoked in the main thread, typically resulting in work
|
|
Packit |
1470ea |
being brought back from a worker thread into the main thread.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
No use
|
|
Packit |
1470ea |
Use en su lugar
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
g_timeout_add()
|
|
Packit |
1470ea |
g_timeout_source_new()
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
g_idle_add()
|
|
Packit |
1470ea |
g_idle_source_new()
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
g_child_watch_add()
|
|
Packit |
1470ea |
g_child_watch_source_new()
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<example>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
So to delay some computation in a worker thread, use the following
|
|
Packit |
1470ea |
code:
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
static guint
|
|
Packit |
1470ea |
schedule_computation (guint delay_seconds)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
GSource *source = NULL;
|
|
Packit |
1470ea |
GMainContext *context;
|
|
Packit |
1470ea |
guint id;
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Get the calling context. */
|
|
Packit |
1470ea |
context = g_main_context_get_thread_default ();
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
source = g_timeout_source_new_seconds (delay_seconds);
|
|
Packit |
1470ea |
g_source_set_callback (source, do_computation, NULL, NULL);
|
|
Packit |
1470ea |
id = g_source_attach (source, context);
|
|
Packit |
1470ea |
g_source_unref (source);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* The ID can be used with the same @context to
|
|
Packit |
1470ea |
* cancel the scheduled computation if needed. */
|
|
Packit |
1470ea |
return id;
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
static void
|
|
Packit |
1470ea |
do_computation (gpointer user_data)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
/* … */
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
</example>
|
|
Packit |
1470ea |
</section>
|
|
Packit |
1470ea |
</section>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<section id="using-gmaincontext-in-a-library">
|
|
Packit |
1470ea |
<title>Usar GMainContext en una biblioteca</title>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
At a high level, library code must not make changes to main contexts which
|
|
Packit |
1470ea |
could affect the execution of an application using the library, for
|
|
Packit |
1470ea |
example by changing when the application’s GSource s are
|
|
Packit |
1470ea |
dispatched. There are various best practices which can be followed to aid
|
|
Packit |
1470ea |
this.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Never iterate a context created outside the library, including the
|
|
Packit |
1470ea |
global-default or thread-default contexts. Otherwise,
|
|
Packit |
1470ea |
GSource s created in the application may be dispatched
|
|
Packit |
1470ea |
when the application is not expecting it, causing
|
|
Packit |
1470ea |
<link href="http://en.wikipedia.org/wiki/Reentrancy_%28computing%29">re-entrancy
|
|
Packit |
1470ea |
problems</link> for the application code.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Always remove GSource s from a main context before dropping
|
|
Packit |
1470ea |
the library’s last reference to the context, especially if it may have
|
|
Packit |
1470ea |
been exposed to the application (for example, as a thread-default).
|
|
Packit |
1470ea |
Otherwise the application may keep a reference to the main context and
|
|
Packit |
1470ea |
continue iterating it after the library has returned, potentially causing
|
|
Packit |
1470ea |
unexpected source dispatches in the library. This is equivalent to not
|
|
Packit |
1470ea |
assuming that dropping the library’s last reference to a main context will
|
|
Packit |
1470ea |
finalize that context.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
If the library is designed to be used from multiple threads, or in a
|
|
Packit |
1470ea |
context-aware fashion, always document which context each callback will be
|
|
Packit |
1470ea |
dispatched in. For example, “callbacks will always be dispatched in the
|
|
Packit |
1470ea |
context which is the thread-default at the time of the object’s
|
|
Packit |
1470ea |
construction”. Developers using the library’s API need to know this
|
|
Packit |
1470ea |
information.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Use g_main_context_invoke() to ensure callbacks are
|
|
Packit |
1470ea |
dispatched in the right context. It’s much easier than manually using
|
|
Packit |
1470ea |
g_idle_source_new() to transfer work between contexts.
|
|
Packit |
1470ea |
(See: <link xref="#ensuring-functions-are-called-in-the-right-context"/>.)
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Libraries should never use g_main_context_default() (or,
|
|
Packit |
1470ea |
equivalently, pass NULL to a GMainContext -typed
|
|
Packit |
1470ea |
parameter). Always store and explicitly use a specific
|
|
Packit |
1470ea |
GMainContext , even if it often points to some default
|
|
Packit |
1470ea |
context. This makes the code easier to split out into threads in future,
|
|
Packit |
1470ea |
if needed, without causing hard-to-debug problems caused by callbacks
|
|
Packit |
1470ea |
being invoked in the wrong context.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Always write things asynchronously internally (using
|
|
Packit |
1470ea |
<link xref="#gtask">GTask </link> where appropriate), and keep
|
|
Packit |
1470ea |
synchronous wrappers at the very top level of an API, where they can be
|
|
Packit |
1470ea |
implemented by calling g_main_context_iteration() on a
|
|
Packit |
1470ea |
specific GMainContext . Again, this makes future refactoring
|
|
Packit |
1470ea |
easier. This is demonstrated in the above example: the thread uses
|
|
Packit |
1470ea |
g_output_stream_write_async() rather than
|
|
Packit |
1470ea |
g_output_stream_write() .
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Always match pushes and pops of the thread-default main context:
|
|
Packit |
1470ea |
g_main_context_push_thread_default() and
|
|
Packit |
1470ea |
g_main_context_pop_thread_default() .
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
</section>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<section id="ensuring-functions-are-called-in-the-right-context">
|
|
Packit |
1470ea |
<title>Ensuring Functions are Called in the Right Context</title>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
The ‘right context’ is the thread-default main context of the thread
|
|
Packit |
1470ea |
the function should be executing in. This assumes the typical case
|
|
Packit |
1470ea |
that every thread has a single main context running in a main
|
|
Packit |
1470ea |
loop. A main context effectively provides a work or
|
|
Packit |
1470ea |
<link href="http://en.wikipedia.org/wiki/Message_queue">message
|
|
Packit |
1470ea |
queue</link> for the thread — something which the thread can
|
|
Packit |
1470ea |
periodically check to determine if there is work pending from
|
|
Packit |
1470ea |
another thread. Putting a message on this queue – invoking a function in
|
|
Packit |
1470ea |
another main context – will result in it eventually being dispatched in
|
|
Packit |
1470ea |
that thread.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<example>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
For example, if an application does a long and CPU-intensive computation
|
|
Packit |
1470ea |
it should schedule this in a background thread so that UI updates in the
|
|
Packit |
1470ea |
main thread are not blocked. The results of the computation, however,
|
|
Packit |
1470ea |
might need to be displayed in the UI, so some UI update function must be
|
|
Packit |
1470ea |
called in the main thread once the computation’s complete.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Furthermore, if the computation function can be limited to a single
|
|
Packit |
1470ea |
thread, it becomes easy to eliminate the need for locking a lot of the
|
|
Packit |
1470ea |
data it accesses. This assumes that other threads are implemented
|
|
Packit |
1470ea |
similarly and hence most data is only accessed by a single thread, with
|
|
Packit |
1470ea |
threads communicating by
|
|
Packit |
1470ea |
<link href="http://en.wikipedia.org/wiki/Message_passing">message
|
|
Packit |
1470ea |
passing</link>. This allows each thread to update its data at its
|
|
Packit |
1470ea |
leisure, which significantly simplifies locking.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
</example>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
For some functions, there might be no reason to care which context they’re
|
|
Packit |
1470ea |
executed in, perhaps because they’re asynchronous and hence do not block
|
|
Packit |
1470ea |
the context. However, it is still advisable to be explicit about which
|
|
Packit |
1470ea |
context is used, since those functions may emit signals or invoke
|
|
Packit |
1470ea |
callbacks, and for reasons of thread safety it’s necessary to know which
|
|
Packit |
1470ea |
threads those signal handlers or callbacks are going to be invoked in.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<example>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
For example, the progress callback in
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/gio/stable/GFile.html#g-file-copy-async">g_file_copy_async() </link>
|
|
Packit |
1470ea |
is documented as being called in the thread-default main context
|
|
Packit |
1470ea |
at the time of the initial call.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
</example>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<section id="invocation-core-principle">
|
|
Packit |
1470ea |
<title>Principles of Invocation</title>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
The core principle of invoking a function in a specific context is
|
|
Packit |
1470ea |
simple, and is walked through below to explain the concepts. In practice
|
|
Packit |
1470ea |
the <link xref="#g-main-context-invoke-full">convenience method,
|
|
Packit |
1470ea |
g_main_context_invoke_full() </link> should be used instead.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
A
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#GSource">GSource </link>
|
|
Packit |
1470ea |
has to be added to the target GMainContext , which will invoke
|
|
Packit |
1470ea |
the function when it’s dispatched. This GSource should almost
|
|
Packit |
1470ea |
always be an idle source created with
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-idle-source-new">g_idle_source_new() </link>,
|
|
Packit |
1470ea |
but this doesn’t have to be the case. It could be a timeout source
|
|
Packit |
1470ea |
so that the function is executed after a delay, for example.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
The GSource will be
|
|
Packit |
1470ea |
<link xref="#what-is-gmaincontext">dispatched as soon as it’s ready</link>,
|
|
Packit |
1470ea |
calling the function on the thread’s stack. In the case of an idle source,
|
|
Packit |
1470ea |
this will be as soon as all sources at a higher priority have been
|
|
Packit |
1470ea |
dispatched — this can be tweaked using the idle source’s priority
|
|
Packit |
1470ea |
parameter with
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-source-set-priority">g_source_set_priority() </link>.
|
|
Packit |
1470ea |
The source will typically then be destroyed so the function is only
|
|
Packit |
1470ea |
executed once (though again, this doesn’t have to be the case).
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Data can be passed between threads as the user_data passed to
|
|
Packit |
1470ea |
the GSource ’s callback. This is set on the source using
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-source-set-callback">g_source_set_callback() </link>,
|
|
Packit |
1470ea |
along with the callback function to invoke. Only a single pointer
|
|
Packit |
1470ea |
is provided, so if multiple data fields need passing, they must be wrapped
|
|
Packit |
1470ea |
in an allocated structure.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<example>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
The example below demonstrates the underlying principles, but there are
|
|
Packit |
1470ea |
convenience methods explained below which simplify things.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Main function for the background thread, thread1. */
|
|
Packit |
1470ea |
static gpointer
|
|
Packit |
1470ea |
thread1_main (gpointer user_data)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
GMainContext *thread1_main_context = user_data;
|
|
Packit |
1470ea |
GMainLoop *main_loop;
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Set up the thread’s context and run it forever. */
|
|
Packit |
1470ea |
g_main_context_push_thread_default (thread1_main_context);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
main_loop = g_main_loop_new (thread1_main_context, FALSE);
|
|
Packit |
1470ea |
g_main_loop_run (main_loop);
|
|
Packit |
1470ea |
g_main_loop_unref (main_loop);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
g_main_context_pop_thread_default (thread1_main_context);
|
|
Packit |
1470ea |
g_main_context_unref (thread1_main_context);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
return NULL;
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* A data closure structure to carry multiple variables between
|
|
Packit |
1470ea |
* threads. */
|
|
Packit |
1470ea |
typedef struct {
|
|
Packit |
1470ea |
gchar *some_string; /* owned */
|
|
Packit |
1470ea |
guint some_int;
|
|
Packit |
1470ea |
GObject *some_object; /* owned */
|
|
Packit |
1470ea |
} MyFuncData;
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
static void
|
|
Packit |
1470ea |
my_func_data_free (MyFuncData *data)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
g_free (data->some_string);
|
|
Packit |
1470ea |
g_clear_object (&data->some_object);
|
|
Packit |
1470ea |
g_free (data);
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
static void
|
|
Packit |
1470ea |
my_func (const gchar *some_string,
|
|
Packit |
1470ea |
guint some_int,
|
|
Packit |
1470ea |
GObject *some_object)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
/* Do something long and CPU intensive! */
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Convert an idle callback into a call to my_func(). */
|
|
Packit |
1470ea |
static gboolean
|
|
Packit |
1470ea |
my_func_idle (gpointer user_data)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
MyFuncData *data = user_data;
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
my_func (data->some_string, data->some_int, data->some_object);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
return G_SOURCE_REMOVE;
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Function to be called in the main thread to schedule a call to
|
|
Packit |
1470ea |
* my_func() in thread1, passing the given parameters along. */
|
|
Packit |
1470ea |
static void
|
|
Packit |
1470ea |
invoke_my_func (GMainContext *thread1_main_context,
|
|
Packit |
1470ea |
const gchar *some_string,
|
|
Packit |
1470ea |
guint some_int,
|
|
Packit |
1470ea |
GObject *some_object)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
GSource *idle_source;
|
|
Packit |
1470ea |
MyFuncData *data;
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Create a data closure to pass all the desired variables
|
|
Packit |
1470ea |
* between threads. */
|
|
Packit |
1470ea |
data = g_new0 (MyFuncData, 1);
|
|
Packit |
1470ea |
data->some_string = g_strdup (some_string);
|
|
Packit |
1470ea |
data->some_int = some_int;
|
|
Packit |
1470ea |
data->some_object = g_object_ref (some_object);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Create a new idle source, set my_func() as the callback with
|
|
Packit |
1470ea |
* some data to be passed between threads, bump up the priority
|
|
Packit |
1470ea |
* and schedule it by attaching it to thread1’s context. */
|
|
Packit |
1470ea |
idle_source = g_idle_source_new ();
|
|
Packit |
1470ea |
g_source_set_callback (idle_source, my_func_idle, data,
|
|
Packit |
1470ea |
(GDestroyNotify) my_func_data_free);
|
|
Packit |
1470ea |
g_source_set_priority (idle_source, G_PRIORITY_DEFAULT);
|
|
Packit |
1470ea |
g_source_attach (idle_source, thread1_main_context);
|
|
Packit |
1470ea |
g_source_unref (idle_source);
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Main function for the main thread. */
|
|
Packit |
1470ea |
static void
|
|
Packit |
1470ea |
main (void)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
GThread *thread1;
|
|
Packit |
1470ea |
GMainContext *thread1_main_context;
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Spawn a background thread and pass it a reference to its
|
|
Packit |
1470ea |
* GMainContext. Retain a reference for use in this thread
|
|
Packit |
1470ea |
* too. */
|
|
Packit |
1470ea |
thread1_main_context = g_main_context_new ();
|
|
Packit |
1470ea |
g_thread_new ("thread1", thread1_main,
|
|
Packit |
1470ea |
g_main_context_ref (thread1_main_context));
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Maybe set up your UI here, for example. */
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Invoke my_func() in the other thread. */
|
|
Packit |
1470ea |
invoke_my_func (thread1_main_context,
|
|
Packit |
1470ea |
"some data which needs passing between threads",
|
|
Packit |
1470ea |
123456, some_object);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Continue doing other work. */
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
This invocation is uni-directional: it calls
|
|
Packit |
1470ea |
my_func() in thread1 , but there’s no way to
|
|
Packit |
1470ea |
return a value to the main thread. To do that, the same principle needs
|
|
Packit |
1470ea |
to be used again, invoking a callback function in the main thread. It’s
|
|
Packit |
1470ea |
a straightforward extension which isn’t covered here.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
To maintain thread safety, data which is potentially accessed by
|
|
Packit |
1470ea |
multiple threads must make those accesses mutually exclusive using a
|
|
Packit |
1470ea |
<link href="http://en.wikipedia.org/wiki/Mutual_exclusion">mutex</link>.
|
|
Packit |
1470ea |
Data potentially accessed by multiple threads:
|
|
Packit |
1470ea |
thread1_main_context , passed in the fork call to
|
|
Packit |
1470ea |
thread1_main ; and some_object , a reference to
|
|
Packit |
1470ea |
which is passed in the data closure. Critically, GLib guarantees that
|
|
Packit |
1470ea |
GMainContext is thread safe, so sharing
|
|
Packit |
1470ea |
thread1_main_context between threads is safe. The example
|
|
Packit |
1470ea |
assumes that other code accessing some_object is thread
|
|
Packit |
1470ea |
safe.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Note that some_string and some_int cannot be
|
|
Packit |
1470ea |
accessed from both threads, because copies of them are passed
|
|
Packit |
1470ea |
to thread1 , rather than the originals. This is a standard
|
|
Packit |
1470ea |
technique for making cross-thread calls thread safe without requiring
|
|
Packit |
1470ea |
locking. It also avoids the problem of synchronizing freeing
|
|
Packit |
1470ea |
some_string .
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Similarly, a reference to some_object is transferred to
|
|
Packit |
1470ea |
thread1 , which works around the issue of synchronizing
|
|
Packit |
1470ea |
destruction of the object (see <link xref="memory-management"/>).
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
g_idle_source_new() is used rather than the simpler
|
|
Packit |
1470ea |
g_idle_add() so the GMainContext to attach to
|
|
Packit |
1470ea |
can be specified.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
</example>
|
|
Packit |
1470ea |
</section>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<section id="g-main-context-invoke-full">
|
|
Packit |
1470ea |
<title>
|
|
Packit |
1470ea |
Convenience Method: g_main_context_invoke_full()
|
|
Packit |
1470ea |
</title>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
This is simplified greatly by the convenience method,
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-main-context-invoke-full">g_main_context_invoke_full() </link>.
|
|
Packit |
1470ea |
It invokes a callback so that the specified GMainContext is
|
|
Packit |
1470ea |
owned during the invocation. Owning a main context is almost always
|
|
Packit |
1470ea |
equivalent to running it, and hence the function is invoked in the
|
|
Packit |
1470ea |
thread for which the specified context is the thread-default.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
g_main_context_invoke() can be used instead if the user
|
|
Packit |
1470ea |
data does not need to be freed by a GDestroyNotify callback
|
|
Packit |
1470ea |
after the invocation returns.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<example>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Modifying the earlier example, the invoke_my_func()
|
|
Packit |
1470ea |
function can be replaced by the following:
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
static void
|
|
Packit |
1470ea |
invoke_my_func (GMainContext *thread1_main_context,
|
|
Packit |
1470ea |
const gchar *some_string,
|
|
Packit |
1470ea |
guint some_int,
|
|
Packit |
1470ea |
GObject *some_object)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
MyFuncData *data;
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Create a data closure to pass all the desired variables
|
|
Packit |
1470ea |
* between threads. */
|
|
Packit |
1470ea |
data = g_new0 (MyFuncData, 1);
|
|
Packit |
1470ea |
data->some_string = g_strdup (some_string);
|
|
Packit |
1470ea |
data->some_int = some_int;
|
|
Packit |
1470ea |
data->some_object = g_object_ref (some_object);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Invoke the function. */
|
|
Packit |
1470ea |
g_main_context_invoke_full (thread1_main_context,
|
|
Packit |
1470ea |
G_PRIORITY_DEFAULT, my_func_idle,
|
|
Packit |
1470ea |
data,
|
|
Packit |
1470ea |
(GDestroyNotify) my_func_data_free);
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Consider what happens if invoke_my_func() were called
|
|
Packit |
1470ea |
from thread1 , rather than from the main thread. With the
|
|
Packit |
1470ea |
original implementation, the idle source would be added to
|
|
Packit |
1470ea |
thread1 ’s context and dispatched on the context’s next
|
|
Packit |
1470ea |
iteration (assuming no pending dispatches with higher priorities).
|
|
Packit |
1470ea |
With the improved implementation,
|
|
Packit |
1470ea |
g_main_context_invoke_full() will notice that the
|
|
Packit |
1470ea |
specified context is already owned by the thread (or ownership can be
|
|
Packit |
1470ea |
acquired by it), and will call my_func_idle() directly,
|
|
Packit |
1470ea |
rather than attaching a source to the context and delaying the
|
|
Packit |
1470ea |
invocation to the next context iteration.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
This subtle behavior difference doesn’t matter in most cases, but is
|
|
Packit |
1470ea |
worth bearing in mind since it can affect blocking behavior
|
|
Packit |
1470ea |
(invoke_my_func() would go from taking negligible time,
|
|
Packit |
1470ea |
to taking the same amount of time as my_func() before
|
|
Packit |
1470ea |
returning).
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
</example>
|
|
Packit |
1470ea |
</section>
|
|
Packit |
1470ea |
</section>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<section id="checking-threading">
|
|
Packit |
1470ea |
<title>Comprobar los hilos</title>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
It is useful to document which thread each function should be called in,
|
|
Packit |
1470ea |
in the form of an assertion:
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
g_assert (g_main_context_is_owner (expected_main_context));
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
If that’s put at the top of each function, any assertion failure will
|
|
Packit |
1470ea |
highlight a case where a function has been called from the wrong
|
|
Packit |
1470ea |
thread. It is much easier to write these assertions when initially
|
|
Packit |
1470ea |
developing code, rather than debugging race conditions which can easily
|
|
Packit |
1470ea |
result from a function being called in the wrong thread.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
This technique can also be applied to signal emissions and callbacks,
|
|
Packit |
1470ea |
improving type safety as well as asserting the right context is used. Note
|
|
Packit |
1470ea |
that signal emission via
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/gobject/stable/gobject-Signals.html#g-signal-emit">g_signal_emit() </link>
|
|
Packit |
1470ea |
is synchronous, and doesn’t involve a main context at all.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<example>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
For example, instead of using the following when emitting a signal:
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
guint param1; /* arbitrary example parameters */
|
|
Packit |
1470ea |
gchar *param2;
|
|
Packit |
1470ea |
guint retval = 0;
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
g_signal_emit_by_name (my_object, "some-signal",
|
|
Packit |
1470ea |
param1, param2, &retval);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Se puede usar lo siguiente:
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
static guint
|
|
Packit |
1470ea |
emit_some_signal (GObject *my_object,
|
|
Packit |
1470ea |
guint param1,
|
|
Packit |
1470ea |
const gchar *param2)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
guint retval = 0;
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
g_assert (g_main_context_is_owner (expected_main_context));
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
g_signal_emit_by_name (my_object, "some-signal",
|
|
Packit |
1470ea |
param1, param2, &retval);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
return retval;
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
</example>
|
|
Packit |
1470ea |
</section>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<section id="gtask">
|
|
Packit |
1470ea |
<title>GTask </title>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/gio/stable/GTask.html">GTask </link>
|
|
Packit |
1470ea |
provides a slightly different approach to invoking functions in other
|
|
Packit |
1470ea |
threads, which is more suited to the case where a function should be
|
|
Packit |
1470ea |
executed in some background thread, but not a specific one.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
GTask takes a data closure and a function to execute, and
|
|
Packit |
1470ea |
provides ways to return the result from this function. It handles
|
|
Packit |
1470ea |
everything necessary to run that function in an arbitrary thread belonging
|
|
Packit |
1470ea |
to some thread pool internal to GLib.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<example>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
By combining
|
|
Packit |
1470ea |
<link xref="#g-main-context-invoke-full">g_main_context_invoke_full() </link>
|
|
Packit |
1470ea |
and GTask , it is possible to run a task in a specific context
|
|
Packit |
1470ea |
and effortlessly return its result to the current context:
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* This will be invoked in thread1. */
|
|
Packit |
1470ea |
static gboolean
|
|
Packit |
1470ea |
my_func_idle (gpointer user_data)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
GTask *task = G_TASK (user_data);
|
|
Packit |
1470ea |
MyFuncData *data;
|
|
Packit |
1470ea |
gboolean retval;
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Call my_func() and propagate its returned boolean to
|
|
Packit |
1470ea |
* the main thread. */
|
|
Packit |
1470ea |
data = g_task_get_task_data (task);
|
|
Packit |
1470ea |
retval = my_func (data->some_string, data->some_int,
|
|
Packit |
1470ea |
data->some_object);
|
|
Packit |
1470ea |
g_task_return_boolean (task, retval);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
return G_SOURCE_REMOVE;
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Whichever thread this is invoked in, the @callback will be
|
|
Packit |
1470ea |
* invoked in, once my_func() has finished and returned a result. */
|
|
Packit |
1470ea |
static void
|
|
Packit |
1470ea |
invoke_my_func_with_result (GMainContext *thread1_main_context,
|
|
Packit |
1470ea |
const gchar *some_string,
|
|
Packit |
1470ea |
guint some_int,
|
|
Packit |
1470ea |
GObject *some_object,
|
|
Packit |
1470ea |
GAsyncReadyCallback callback,
|
|
Packit |
1470ea |
gpointer user_data)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
MyFuncData *data;
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Create a data closure to pass all the desired variables
|
|
Packit |
1470ea |
* between threads. */
|
|
Packit |
1470ea |
data = g_new0 (MyFuncData, 1);
|
|
Packit |
1470ea |
data->some_string = g_strdup (some_string);
|
|
Packit |
1470ea |
data->some_int = some_int;
|
|
Packit |
1470ea |
data->some_object = g_object_ref (some_object);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Create a GTask to handle returning the result to the current
|
|
Packit |
1470ea |
* thread-default main context. */
|
|
Packit |
1470ea |
task = g_task_new (NULL, NULL, callback, user_data);
|
|
Packit |
1470ea |
g_task_set_task_data (task, data,
|
|
Packit |
1470ea |
(GDestroyNotify) my_func_data_free);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Invoke the function. */
|
|
Packit |
1470ea |
g_main_context_invoke_full (thread1_main_context,
|
|
Packit |
1470ea |
G_PRIORITY_DEFAULT, my_func_idle,
|
|
Packit |
1470ea |
task,
|
|
Packit |
1470ea |
(GDestroyNotify) g_object_unref);
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
</example>
|
|
Packit |
1470ea |
</section>
|
|
Packit |
1470ea |
</page>
|