I'm working on port some of my existing software from macOS and Windows to Linux. The basics of the software is a mutlithreaded server running behind a GUI that shows stats, generates new windows on specific events, etc.
On macOS, we have DispatchQueue.main.sync
. On Windows / C# / WPF, we have Dispatcher.Invoke
. We specifically use these calls in circumstances where we need the calling thread to be blocked while the UI thread is being updated. Is there something similar for GTK+?
I have been using gdk_thread_add_idle_full
for the async portions of the code where I don't need to block, but for the synchronous version, it has been a little more complex. My current solution is a simple object containing a GMutex
/ GCond
pair. The object is "waitable" on the calling thread, and "completeable" on the UI thread. Passing this object to gdk_thread_add_idle_full
gives me the desired effect, but I was curious if there is a more succinct option.
The server library is as-is, so there isn't room to modify that code to behave better. I'm looking for a solution on the toolkit's side. Also, the GTK+ work is all being done in C.
I don’t think there’s a more succinct option, and using a GMutex
/GCond
pair seems reasonable for synchronising your worker thread with the reply from the UI thread.
The downside of using a GMutex
/GCond
pair is that it means you can’t iterate a GMainContext
on your worker thread in the meantime. If that’s not how your application is constructed, then that’s fine.
If it is, then you might instead want to take an approach where you pass a reference to the worker thread’s GMainContext
to the UI thread in the gdk_threads_add_idle_full()
call. Once the UI operation is complete, the code in the UI thread would call g_idle_source_new()
/g_source_attach()
on the worker thread’s GMainContext
to schedule a callback to be executed in the worker thread. This callback would update state to mark the operation as complete and returned.
In this approach, gdk_threads_add_idle_full()
and g_idle_source_new()
/g_source_attach()
are both essentially message passing functions from one thread to another. In your approach, the GMutex
/GCond
is acting as a synchronisation fence instead.
This second approach would mean your worker thread can get on with other useful work while waiting for the UI, rather than blocking. But that might not be relevant to how your application is constructed.