Threading problems with GTK

Jon Gjengset picture Jon Gjengset · Sep 6, 2013 · Viewed 22.2k times · Source

I'm building a fairly simple C application using GTK, but have to perform some blocking IO which will trigger updates to the GUI. In order to do this, I start a new pthread right before gtk_main() as such:

/* global variables */
GMainContext *mainc;

/* local variables */
FILE *fifo;
pthread_t reader;

/* main() */
mainc = g_main_context_default();
pthread_create(&reader, NULL, watch_fifo, argv[argc-1]);
gtk_main();

When the pthread reads some data, it updates the GUI like so:

g_main_context_invoke(mainc, set_icon, param);

Where set_icon is

gboolean set_icon(gpointer data)
{
    char *p = (char*)data;
    gtk_status_icon_set_from_icon_name(icon, p);
    return FALSE;
}

This all works most of the time, but every now and again I get this curious error message:

[xcb] Unknown sequence number while processing queue
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
mktrayicon: xcb_io.c:274: poll_for_event: Assertion `!xcb_xlib_threads_sequence_lost' failed.

I thought the whole point of using g_main_context_invoke was to avoid issues with threads? Doing a bit of Googling, I came across gdk_threads_init, gdk_threads_enter and friends, but they all seem to be deprecated? I know the GTK documentation says that all GUI updaes should be performed on the main thread, but this does not combine all that well with blocking IO, and I'd prefer not to have to construct some complex communication mechanism between the threads.

And so, my question is, how should I correctly deal with this?

EDIT: The full code can be seen here EDIT2: As an update based on @ptomato's answer, I've moved to GThreads and using gdk_threads_add_idle() as seen in this commit, but the problem is still present.

Answer

Wiley picture Wiley · Sep 9, 2013

Call XInitThreads(). This should be done before gtk_init, that will stop the messages!

Something like this:

    #include <X11/Xlib.h>
    ...  
    XInitThreads();
    ...
    gtk_init(&argc, &argv);

I don't remember seeing these messages before GLIB 2.32, when g_thread_init()/gdk_threads_init() were used.

You might want to check out g_thread_pool_new and g_thread_pool_push. From thread, use g_main_context_invoke to execute in main loop or just wrap thread between gdk_threads_enter()/gdk_threads_leave()

I do not use a tray so I can not easily check this. I think you are correct about gdk_threads_add_idle using locks to protect GTK/GDK API. There is nothing obvious to me that would cause these messages to appear. The function description for gtk_status_icon_new_from_icon_name states that "If the current icon theme is changed, the icon will be updated appropriately. Which to me, implies your code is not the only code that will access the X display, which could potentially be the problem.

There is also some related info regarding XInitThreads() at

What is the downside of XInitThreads()?

Note that while GDK uses locks for the display, GTK/GDK do not ever call XInitThreads.

On a side note: What's protecting the global variable "onclick", which is passed to execl after a fork(), The child will not inherit the parent's memory locks, and GLib mainloop is incompatible with fork(). Maybe you could copy the string to local variable.