5.2 Is GTK+ thread safe? How do I write multi-threaded GTK+
applications? 

The GLib library can be used in a thread-safe mode by calling g_thread_init() before making any other
GLib calls. In this mode GLib automatically locks all internal data structures as needed. This does not
mean that two threads can simultaneously access, for example, a single hash table, but they can access
two different hash tables simultaneously. If two different threads need to access the same hash table,
the application is responsible for locking itself. 

When GLib is intialized to be thread-safe, GTK+ is thread aware. There is a single global lock that you
must acquire with gdk_threads_enter() before making any GDK calls, and release with
gdk_threads_leave() afterwards. 

A minimal main program for a threaded GTK+ application looks like: 

int
main (int argc, char *argv[])
{
  GtkWidget *window;

  g_thread_init(NULL);
  gtk_init(&argc, &argv);

  window = create_window();
  gtk_widget_show(window);

  gdk_threads_enter();
  gtk_main();
  gdk_threads_leave();

  return(0);
}

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. 

     /*-------------------------------------------------------------------------
      * 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);

       /* 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 */
       gdk_threads_enter();
       gtk_main();
       gdk_threads_leave();

       return(0);
     }

5.3 Why does this strange 'x io error' occur when I fork() in my
GTK+ app? 

This is not really a GTK+ problem, and the problem is not related to fork() either. If the 'x io error'
occurs then you probably use the exit() function in order to exit from the child process. 

When GDK opens an X display, it creates a socket file descriptor. When you use the exit() function, you
implicitly close all the open file descriptors, and the underlying X library really doesn't like this. 

The right function to use here is _exit(). 

Erik Mouw contributed the following code example to illustrate handling fork() and exit(). 

     /*-------------------------------------------------------------------------
      * Filename:      gtk-fork.c
      * Version:       0.99.1
      * Copyright:     Copyright (C) 1999, Erik Mouw
      * Author:        Erik Mouw <J.A.K.Mouw@its.tudelft.nl>
      * Description:   GTK+ fork example
      * Created at:    Thu Sep 23 21:37:55 1999
      * Modified by:   Erik Mouw <J.A.K.Mouw@its.tudelft.nl>
      * Modified at:   Thu Sep 23 22:39:39 1999
      *-----------------------------------------------------------------------*/
     /*
      * Compile with:
      *
      * cc -o gtk-fork gtk-fork.c `gtk-config --cflags --libs`
      *
      */

     #include <stdio.h>
     #include <stdlib.h>
     #include <signal.h>
     #include <sys/types.h>
     #include <sys/wait.h>
     #include <unistd.h>
     #include <gtk/gtk.h>

     void sigchld_handler(int num)
     {
       sigset_t set, oldset;
       pid_t pid;
       int status, exitstatus;

       /* block other incoming SIGCHLD signals */
       sigemptyset(&set);
       sigaddset(&set, SIGCHLD);
       sigprocmask(SIG_BLOCK, &set, &oldset);

       /* wait for child */
       while((pid = waitpid((pid_t)-1, &status, WNOHANG)) > 0)
         {
           if(WIFEXITED(status))
             {
               exitstatus = WEXITSTATUS(status);

               fprintf(stderr, 
                       "Parent: child exited, pid = %d, exit status = %d\n", 
                       (int)pid, exitstatus);
             }
           else if(WIFSIGNALED(status))
             {
               exitstatus = WTERMSIG(status);

               fprintf(stderr,
                       "Parent: child terminated by signal %d, pid = %d\n",
                       exitstatus, (int)pid);
             }
           else if(WIFSTOPPED(status))
             {
               exitstatus = WSTOPSIG(status);

               fprintf(stderr,
                       "Parent: child stopped by signal %d, pid = %d\n",
                       exitstatus, (int)pid);
             }
           else
             {
               fprintf(stderr,
                       "Parent: child exited magically, pid = %d\n",
                       (int)pid);
             }
         }

       /* re-install the signal handler (some systems need this) */
       signal(SIGCHLD, sigchld_handler);
       
       /* and unblock it */
       sigemptyset(&set);
       sigaddset(&set, SIGCHLD);
       sigprocmask(SIG_UNBLOCK, &set, &oldset);
     }

     gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
     {
       return(FALSE);
     }

     void destroy(GtkWidget *widget, gpointer data)
     {
       gtk_main_quit();
     }

     void fork_me(GtkWidget *widget, gpointer data)
     {
       pid_t pid;

       pid = fork();

       if(pid == -1)
         {
           /* ouch, fork() failed */
           perror("fork");
           exit(-1);
         }
       else if(pid == 0)
         {
           /* child */
           fprintf(stderr, "Child: pid = %d\n", (int)getpid());

           execlp("ls", "ls", "-CF", "/", NULL);
           
           /* if exec() returns, there is something wrong */
           perror("execlp");

           /* exit child. note the use of _exit() instead of exit() */
           _exit(-1);
         }
       else
         {
           /* parent */
           fprintf(stderr, "Parent: forked a child with pid = %d\n", (int)pid);
         }
     }

     int main(int argc, char *argv[])
     {
       GtkWidget *window;
       GtkWidget *button;

       gtk_init(&argc, &argv);

       /* the basic stuff: make a window and set callbacks for destroy and
        * delete events 
        */
       window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

       gtk_signal_connect(GTK_OBJECT (window), "delete_event",
                          GTK_SIGNAL_FUNC(delete_event), NULL);
               
       gtk_signal_connect(GTK_OBJECT (window), "destroy",
                          GTK_SIGNAL_FUNC(destroy), NULL);

     #if (GTK_MAJOR_VERSION == 1) && (GTK_MINOR_VERSION == 0)
       gtk_container_border_width(GTK_CONTAINER (window), 10);
     #else  
       gtk_container_set_border_width(GTK_CONTAINER (window), 10);
     #endif

       /* add a button to do something usefull */
       button = gtk_button_new_with_label("Fork me!");
               
       gtk_signal_connect(GTK_OBJECT (button), "clicked",
                          GTK_SIGNAL_FUNC(fork_me), NULL);

       gtk_container_add(GTK_CONTAINER(window), button);
               
       /* show everything */
       gtk_widget_show (button);
       gtk_widget_show (window);


       /* install a signal handler for SIGCHLD signals */
       signal(SIGCHLD, sigchld_handler);

       
       /* main loop */
       gtk_main ();

       exit(0);         
     }


Simple Program to List a Directory
----------------------------------

   Here's a simple program that prints the names of the files in the
current working directory:

     #include <stddef.h>
     #include <stdio.h>
     #include <sys/types.h>
     #include <dirent.h>
     
     int
     main (void)
     {
       DIR *dp;
       struct dirent *ep;
     
       dp = opendir ("./");
       if (dp != NULL)
         {
           while (ep = readdir (dp))
             puts (ep->d_name);
           (void) closedir (dp);
         }
       else
         puts ("Couldn't open the directory.");
     
       return 0;
     }


// also useful scandir()

Simple Program to List a Directory, Mark II
-------------------------------------------

   Here is a revised version of the directory lister found above (*note
Simple Directory Lister::).  Using the `scandir' function we can avoid
using the functions which directly work with the directory contents.
After the call the found entries are available for direct used.

     #include <stdio.h>
     #include <dirent.h>
     
     static int
     one (struct dirent *unused)
     {
       return 1;
     }
     
     int
     main (void)
     {
       struct dirent **eps;
       int n;
     
       n = scandir ("./", &eps, one, alphasort);
       if (n >= 0)
         {
           int cnt;
           for (cnt = 0; cnt < n; ++cnt)
             puts (eps[cnt]->d_name);
         }
       else
         perror ("Couldn't open the directory");
     
       return 0;
     }

   Please note the simple selector function for this example.  Since we
want to see all directory entries we always return `1'.

