forked from AuroraMiddleware/gtk
920147ee8f
Thu Jul 12 18:12:04 2007 Tim Janik <timj@imendio.com> * gdk/tmpl/threads.sgml: clarify section about gdk_threads_enter/ gdk_threads_leave to be reworded in terms of events and to mention availability of gdk_threads_add_idle_full(). svn path=/trunk/; revision=18457
360 lines
8.4 KiB
Plaintext
360 lines
8.4 KiB
Plaintext
<!-- ##### SECTION Title ##### -->
|
|
Threads
|
|
|
|
<!-- ##### SECTION Short_Description ##### -->
|
|
Functions for using GDK in multi-threaded programs
|
|
|
|
<!-- ##### SECTION Long_Description ##### -->
|
|
<para>
|
|
For thread safety, GDK relies on the thread primitives in GLib,
|
|
and on the thread-safe GLib main loop.
|
|
</para>
|
|
<para>
|
|
GLib is completely thread safe (all global data is automatically
|
|
locked), but individual data structure instances are not automatically
|
|
locked for performance reasons. So e.g. you must coordinate
|
|
accesses to the same #GHashTable from multiple threads.
|
|
</para>
|
|
<para>
|
|
GTK+ is "thread aware" but not thread safe — it provides a
|
|
global lock controlled by gdk_threads_enter()/gdk_threads_leave()
|
|
which protects all use of GTK+. That is, only one thread can use GTK+
|
|
at any given time.
|
|
</para>
|
|
<para>
|
|
Unfortunately the above holds with the X11 backend only. With the
|
|
Win32 backend, GDK calls should not be attempted from multiple threads
|
|
at all.
|
|
</para>
|
|
<para>
|
|
You must call g_thread_init() and gdk_threads_init() before executing
|
|
any other GTK+ or GDK functions in a threaded GTK+ program.
|
|
</para>
|
|
<para>
|
|
Idles, timeouts, and input functions from GLib, such as g_idle_add(), are
|
|
executed outside of the main GTK+ lock.
|
|
So, if you need to call GTK+ inside of such a callback, you must surround
|
|
the callback with a gdk_threads_enter()/gdk_threads_leave() pair or use
|
|
gdk_threads_add_idle_full() which does this for you.
|
|
However, event dispatching from the mainloop is still executed within
|
|
the main GTK+ lock, so callback functions connected to event signals
|
|
like GtkWidget::button-press-event, do not need thread protection.
|
|
</para>
|
|
<para>
|
|
In particular, this means, if you are writing widgets that might
|
|
be used in threaded programs, you <emphasis>must</emphasis> surround
|
|
timeouts and idle functions in this matter.
|
|
</para>
|
|
<para>
|
|
As always, you must also surround any calls to GTK+ not made within
|
|
a signal handler with a gdk_threads_enter()/gdk_threads_leave() pair.
|
|
</para>
|
|
|
|
<para>
|
|
Before calling gdk_threads_leave() from a thread other
|
|
than your main thread, you probably want to call gdk_flush()
|
|
to send all pending commands to the windowing system.
|
|
(The reason you don't need to do this from the main thread
|
|
is that GDK always automatically flushes pending commands
|
|
when it runs out of incoming events to process and has
|
|
to sleep while waiting for more events.)
|
|
</para>
|
|
|
|
<para>A minimal main program for a threaded GTK+ application
|
|
looks like:</para>
|
|
|
|
<informalexample>
|
|
<programlisting role="C">
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *window;
|
|
|
|
g_thread_init (NULL);
|
|
gdk_threads_init (<!-- -->);
|
|
gdk_threads_enter (<!-- -->);
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
window = create_window (<!-- -->);
|
|
gtk_widget_show (window);
|
|
|
|
gtk_main (<!-- -->);
|
|
gdk_threads_leave (<!-- -->);
|
|
|
|
return 0;
|
|
}
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>
|
|
Callbacks require a bit of attention. Callbacks from GTK+ signals
|
|
are made within the GTK+ lock. However callbacks from GLib (timeouts,
|
|
IO callbacks, and idle functions) are made outside of the GTK+
|
|
lock. So, within a signal handler you do not need to call
|
|
gdk_threads_enter(), but within the other types of callbacks, you
|
|
do.
|
|
</para>
|
|
|
|
<para>Erik Mouw contributed the following code example to
|
|
illustrate how to use threads within GTK+ programs.
|
|
</para>
|
|
|
|
<informalexample>
|
|
<programlisting role="C">
|
|
/*-------------------------------------------------------------------------
|
|
* Filename: gtk-thread.c
|
|
* Version: 0.99.1
|
|
* Copyright: Copyright (C) 1999, Erik Mouw
|
|
* Author: Erik Mouw <J.A.K.Mouw@its.tudelft.nl>
|
|
* Description: GTK threads example.
|
|
* Created at: Sun Oct 17 21:27:09 1999
|
|
* Modified by: Erik Mouw <J.A.K.Mouw@its.tudelft.nl>
|
|
* Modified at: Sun Oct 24 17:21:41 1999
|
|
*-----------------------------------------------------------------------*/
|
|
/*
|
|
* Compile with:
|
|
*
|
|
* cc -o gtk-thread gtk-thread.c `gtk-config --cflags --libs gthread`
|
|
*
|
|
* Thanks to Sebastian Wilhelmi and Owen Taylor for pointing out some
|
|
* bugs.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <gtk/gtk.h>
|
|
#include <glib.h>
|
|
#include <pthread.h>
|
|
|
|
#define YES_IT_IS (1)
|
|
#define NO_IT_IS_NOT (0)
|
|
|
|
typedef struct
|
|
{
|
|
GtkWidget *label;
|
|
int what;
|
|
} yes_or_no_args;
|
|
|
|
G_LOCK_DEFINE_STATIC (yes_or_no);
|
|
static volatile int yes_or_no = YES_IT_IS;
|
|
|
|
void destroy (GtkWidget *widget, gpointer data)
|
|
{
|
|
gtk_main_quit (<!-- -->);
|
|
}
|
|
|
|
void *argument_thread (void *args)
|
|
{
|
|
yes_or_no_args *data = (yes_or_no_args *)args;
|
|
gboolean say_something;
|
|
|
|
for (;;)
|
|
{
|
|
/* sleep a while */
|
|
sleep(rand(<!-- -->) / (RAND_MAX / 3) + 1);
|
|
|
|
/* lock the yes_or_no_variable */
|
|
G_LOCK(yes_or_no);
|
|
|
|
/* do we have to say something? */
|
|
say_something = (yes_or_no != data->what);
|
|
|
|
if(say_something)
|
|
{
|
|
/* set the variable */
|
|
yes_or_no = data->what;
|
|
}
|
|
|
|
/* Unlock the yes_or_no variable */
|
|
G_UNLOCK (yes_or_no);
|
|
|
|
if (say_something)
|
|
{
|
|
/* get GTK thread lock */
|
|
gdk_threads_enter (<!-- -->);
|
|
|
|
/* set label text */
|
|
if(data->what == YES_IT_IS)
|
|
gtk_label_set_text (GTK_LABEL (data->label), "O yes, it is!");
|
|
else
|
|
gtk_label_set_text (GTK_LABEL (data->label), "O no, it isn't!");
|
|
|
|
/* release GTK thread lock */
|
|
gdk_threads_leave (<!-- -->);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *window;
|
|
GtkWidget *label;
|
|
yes_or_no_args yes_args, no_args;
|
|
pthread_t no_tid, yes_tid;
|
|
|
|
/* init threads */
|
|
g_thread_init (NULL);
|
|
gdk_threads_init (<!-- -->);
|
|
gdk_threads_enter (<!-- -->);
|
|
|
|
/* init gtk */
|
|
gtk_init(&argc, &argv);
|
|
|
|
/* init random number generator */
|
|
srand ((unsigned int) time (NULL));
|
|
|
|
/* create a window */
|
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (window), "destroy",
|
|
GTK_SIGNAL_FUNC (destroy), NULL);
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
|
|
|
|
/* create a label */
|
|
label = gtk_label_new ("And now for something completely different ...");
|
|
gtk_container_add (GTK_CONTAINER (window), label);
|
|
|
|
/* show everything */
|
|
gtk_widget_show (label);
|
|
gtk_widget_show (window);
|
|
|
|
/* create the threads */
|
|
yes_args.label = label;
|
|
yes_args.what = YES_IT_IS;
|
|
pthread_create (&yes_tid, NULL, argument_thread, &yes_args);
|
|
|
|
no_args.label = label;
|
|
no_args.what = NO_IT_IS_NOT;
|
|
pthread_create (&no_tid, NULL, argument_thread, &no_args);
|
|
|
|
/* enter the GTK main loop */
|
|
gtk_main (<!-- -->);
|
|
gdk_threads_leave (<!-- -->);
|
|
|
|
return 0;
|
|
}
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<!-- ##### SECTION See_Also ##### -->
|
|
<para>
|
|
|
|
</para>
|
|
|
|
<!-- ##### SECTION Stability_Level ##### -->
|
|
|
|
|
|
<!-- ##### MACRO GDK_THREADS_ENTER ##### -->
|
|
<para>
|
|
This macro marks the beginning of a critical section in which GDK and
|
|
GTK+ functions can be called safely and without causing race
|
|
conditions. Only one thread at a time can be in such a critial
|
|
section. The macro expands to a no-op if #G_THREADS_ENABLED has not
|
|
been defined. Typically gdk_threads_enter() should be used instead of
|
|
this macro.
|
|
</para>
|
|
|
|
|
|
|
|
<!-- ##### MACRO GDK_THREADS_LEAVE ##### -->
|
|
<para>
|
|
This macro marks the end of a critical section
|
|
begun with #GDK_THREADS_ENTER.
|
|
</para>
|
|
|
|
|
|
|
|
<!-- ##### FUNCTION gdk_threads_init ##### -->
|
|
<para>
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<!-- ##### FUNCTION gdk_threads_enter ##### -->
|
|
<para>
|
|
This macro marks the beginning of a critical section in which GDK and
|
|
GTK+ functions can be called safely and without causing race
|
|
conditions. Only one thread at a time can be in such a critial
|
|
section.
|
|
</para>
|
|
|
|
|
|
|
|
<!-- ##### FUNCTION gdk_threads_leave ##### -->
|
|
<para>
|
|
Leaves a critical region begun with gdk_threads_enter().
|
|
</para>
|
|
|
|
|
|
|
|
<!-- ##### VARIABLE gdk_threads_mutex ##### -->
|
|
<para>
|
|
The #GMutex used to implement the critical region for
|
|
gdk_threads_enter()/gdk_threads_leave().
|
|
</para>
|
|
|
|
|
|
<!-- ##### FUNCTION gdk_threads_set_lock_functions ##### -->
|
|
<para>
|
|
|
|
</para>
|
|
|
|
@enter_fn:
|
|
@leave_fn:
|
|
|
|
|
|
<!-- ##### FUNCTION gdk_threads_add_idle ##### -->
|
|
<para>
|
|
|
|
</para>
|
|
|
|
@function:
|
|
@data:
|
|
@Returns:
|
|
|
|
|
|
<!-- ##### FUNCTION gdk_threads_add_idle_full ##### -->
|
|
<para>
|
|
|
|
</para>
|
|
|
|
@priority:
|
|
@function:
|
|
@data:
|
|
@notify:
|
|
@Returns:
|
|
|
|
|
|
<!-- ##### FUNCTION gdk_threads_add_timeout ##### -->
|
|
<para>
|
|
|
|
</para>
|
|
|
|
@interval:
|
|
@function:
|
|
@data:
|
|
@Returns:
|
|
|
|
|
|
<!-- ##### FUNCTION gdk_threads_add_timeout_full ##### -->
|
|
<para>
|
|
|
|
</para>
|
|
|
|
@priority:
|
|
@interval:
|
|
@function:
|
|
@data:
|
|
@notify:
|
|
@Returns:
|
|
|
|
|