diff options
author | Javier Jardón <jjardon@gnome.org> | 2010-11-15 19:37:28 +0100 |
---|---|---|
committer | Tristan Van Berkom <tristan.van.berkom@gmail.com> | 2010-11-16 16:20:48 +0900 |
commit | dd2db852ccfcfe11481f636c9d1e3bc8712e06af (patch) | |
tree | 82336944d843304404409a77b4edf5fe21001352 /gdk | |
parent | 7524d5fcbec3e9155c18b3e45bbac23527ab68d0 (diff) | |
download | gtk+-dd2db852ccfcfe11481f636c9d1e3bc8712e06af.tar.gz |
docs: Move documentation to inline comments: gdkthreads
Diffstat (limited to 'gdk')
-rw-r--r-- | gdk/gdk.c | 245 | ||||
-rw-r--r-- | gdk/gdkthreads.h | 17 |
2 files changed, 261 insertions, 1 deletions
@@ -399,6 +399,244 @@ gdk_init (int *argc, char ***argv) } } + + +/** + * SECTION:threads + * @Short_description: Functions for using GDK in multi-threaded programs + * @Title: Threads + * + * For thread safety, GDK relies on the thread primitives in GLib, + * and on the thread-safe GLib main loop. + * + * 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. + * + * 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. + * + * Unfortunately the above holds with the X11 backend only. With the + * Win32 backend, GDK calls should not be attempted from multiple threads + * at all. + * + * You must call g_thread_init() and gdk_threads_init() before executing + * any other GTK+ or GDK functions in a threaded GTK+ program. + * + * 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. + * + * 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. + * + * 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. + * + * 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.) + * + * A minimal main program for a threaded GTK+ application + * looks like: + * <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> + * + * 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. + * + * Erik Mouw contributed the following code example to + * illustrate how to use threads within GTK+ programs. + * <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); + * + * g_signal_connect (window, "destroy", G_CALLBACK (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> + */ + + +/** + * gdk_threads_enter: + * + * 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. + */ void gdk_threads_enter (void) { @@ -406,6 +644,11 @@ gdk_threads_enter (void) (*gdk_threads_lock) (); } +/** + * gdk_threads_leave: + * + * Leaves a critical region begun with gdk_threads_enter(). + */ void gdk_threads_leave (void) { @@ -429,7 +672,7 @@ gdk_threads_impl_unlock (void) /** * gdk_threads_init: - * + * * Initializes GDK so that it can be used from multiple threads * in conjunction with gdk_threads_enter() and gdk_threads_leave(). * g_thread_init() must be called previous to this function. diff --git a/gdk/gdkthreads.h b/gdk/gdkthreads.h index 743e6ec6fa..ad2f09d5d9 100644 --- a/gdk/gdkthreads.h +++ b/gdk/gdkthreads.h @@ -64,7 +64,24 @@ guint gdk_threads_add_timeout_seconds (guint interval, GSourceFunc function, gpointer data); +/** + * GDK_THREADS_ENTER: + * + * 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. + */ #define GDK_THREADS_ENTER() gdk_threads_enter() + +/** + * GDK_THREADS_LEAVE: + * + * This macro marks the end of a critical section + * begun with #GDK_THREADS_ENTER. + */ #define GDK_THREADS_LEAVE() gdk_threads_leave() G_END_DECLS |