Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Threading problems with GTK

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.

like image 862
Jon Gjengset Avatar asked Sep 05 '13 23:09

Jon Gjengset


People also ask

Is GTK multithreaded?

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.

Is GTK single threaded?

GTK is also single threaded and not MT-safe. This means, that you must not call any GTK functions from other threads, as it will lead to undefined behaviour.

What is XInitThreads?

The XInitThreads function initializes Xlib support for concurrent threads. This function must be the first Xlib function a multi-threaded program calls, and it must complete before any other Xlib call is made. This function returns a nonzero status if initialization was successful; otherwise, it returns zero.


2 Answers

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.

like image 159
Wiley Avatar answered Oct 08 '22 13:10

Wiley


I'm not sure if bare pthreads are guaranteed to work with GTK. You should use the GThread wrappers.

I think what the problem may be is that g_main_context_invoke() is adding set_icon() as an idle function. (It seems that that is what goes on behind the scenes, but I'm not sure.) Idle functions added using GLib's API, despite being executed on the main thread, need to hold the GDK lock. If you use the gdk_threads_add_idle() API (which is not deprecated) to invoke set_icon(), then everything should work properly with threading.

(Although this is just a wild guess.)

like image 39
ptomato Avatar answered Oct 08 '22 13:10

ptomato