One of the libraries I would like to work with is the GTK toolkit, in order to develop some applications for Linux.

One important aspect when it comes to GUI programming is multi-threading, I have rarely had to work with threads as I was mostly a JavaScript developer but this needs to change in the GUI world, as there is usually a central event loop constantly needing to process events, even if your app is doing nothing it’s actually doing a lot, it needs to keep processing window events and such, make a mistake and your app lags and maybe even the operating system will start warning that the app is not responding and may need to be force closed. We don’t want that!

So how on earth are we going to ever apply tasks like doing a network request or compress a file or any tasks that could take a while, we don’t want to block the main event loop, we want the application to remain butter smooth and keep responding to any button presses and what not, the app needs to feel alive, yet it’s got work to do.

The answer to this comes to threading, threads is a long topic in computer science but it essentially allows context-switching, like your computer could switch between threads every once in a while giving all of them a fair share of time to do their work, the program responsible for doing this context switching is known as the ‘scheduler’ and first and foremost your operating system would have it’s own threading mechanisms and its own scheduler.

Typically in modern computers with multiple cores, multi-threaded applications could have the scheduler assign each thread a dedicated core of the CPU, allowing them to really run in parallel, at the same time instead of faking it with just switching contexts real fast giving the illusion that everything is happening at once.

Now this threading mechanism can vary a lot depending on the programming language you use, languages with a virtual machine could have their own threading mechanisms that are NOT managed by the operating system, hence they have their own scheduler and likely won’t be able to benefit from multi-core processors. This is mainly done out of concern for portability, they want their virtual machines to work on any kind of machine, consistently.

Threads in Gtk

The Gtk toolkit uses GLib under the hood for a lot of its event loop management and threading stuff.

In this post I try to explore threading and get it working.

I decided to use Vala for this experiment to save us some of the headaches of C and be able to quickly prototype and explore it.

I created a Vala template app from GNOME builder which gives us a basic working application to begin with, let’s go.

When to create threads?

Threads are normally spawned on demand when work is needed, in this project the Vala template came with an option ‘preferences’ which is supposed to be your application’s settings menu but in the template it comes blank and does nothing, here’s the action:

private void on_preferences_action () {
    message ("app.preferences action activated");
}

So I thought this is the perfect spot to experiment with threads, i can trigger this action and watch what my threading code will do.

Spawning a thread

Next I had to find how threads can be created in GLib, the traditional function is g_thread_new which is neatly wrapped into a GLib.Thread class in Vala so let’s just construct one.

Hold on, why is it written GLib.Thread<T> ? what is the generic type for? took me a moment to wonder, but I realized threads can also actually ‘return’ data! But if threads are running independently of the current context, where is that result ever gonna go? I believe the answer is, the results are discarded, but Threads can be joined which as the name says joins the thread with the current one, essentially bringing it to the current context. This also means the join call will block, waiting for the results and then you can have it.

In our case we didn’t need the results, so I initially inserted void so here’s how I constructed it:

var thread = new GLib.Thread("thread-example", () => {
  message ("Hello, World!");
});

And we are done! The first argument is the name of the thread, the second argument specifies the function to execute.

Run the app and hit the preferences button and you will see the message logged in your console.

Blocking the thread

So it works but so what? we could’ve just printed that message without a thread, the point for threads is that it will allow us to execute code without blocking the main event loop, our user interface that is. So we need to simulate some heavy work going on, that’s going to take some time to complete.

Let’s just sleep for now, that’ll block the thread. Sleeping a thread can be done via g_usleep in Vala that is wrapped as a static method of Thread directly so that’ll be Thread.usleep

If you directly add this on top of your preferences action without the thread

Thread.usleep (5000000);

You will observe the GUI just hang, you feel like the application is bugged, it’s a depressing moment, you feel terrible, after 5 seconds it finally goes back to normal, phew, you swear you will never open the preferences page again and you go spam the developer to fix their application. That my friend is what happens if you do heavy work on the main thread and block the GUI.

Now put that sleep code inside the thread now, just before the message, and watch! The GUI is butter smooth, everything works, yet the thread is still working and after 5 seconds we get our results, the message printed out, we did work that would stall our thread for 5 seconds, without stalling the main thread, that’s what threads are for.

Now for fun let’s explore join, if after spawning the thread we do this:

thread.join();

The thread is spawned, doing it’s job, but then we join it with the main GUI thread, now the main GUI thread has to ‘wait’ for that thread to finish, and we block our GUI thread again, oh well.

Remember the generics? we could pass some data though

private void on_preferences_action () {
    message ("app.preferences action activated");
    
    var thread = new GLib.Thread<int>("thread-example", () => {
        Thread.usleep (5000000);
        message ("Hello, World!");
        return 5;
    });
    
    message("Value %d", thread.join ());
}

Now after 5 seconds of blocking, we get back the message from the thread. A use case could be spawning the thread for it to do it’s heavy work but then join it at a later point when it might’ve been done or we need the results and it probably is almost done anyway.

Communicating back from the thread

So we can do work on another thread, but if the user is to be aware of the work going on we need to be able to communicate back to report progress and know when it’s done.

Guess what, GTK is not thread-safe, what this means is you must only use gtk in one thread, that’s the thread you initialized gtk, presented your window, your main thread, your GUI thread, using gtk outside of this thread is considered to be undefined behavior, so it may work for some cases or it may completely crash your program, it’s just not the right way.

In order to communicate back from the thread to perform UI updates and such, we commonly use GLib’s idle or timeout functions. Idle functions are callbacks registered to glib for it to be called as soon as no other higher priority work is going on, so after ensuring we won’t be blocking any crucial UI stuff we get to do something, we get to update the UI here since these callbacks are run in the main thread.

To do this we use g_idle_add which in vala is GLib.Idle.add like so:

GLib.Idle.add(() => {
  // This is run in the main thread, update the UI as you will.
  return false;
});

The callback returns a boolean on whether the callback should be rescheduled again, say false to remove it after that, returning true could be useful for repetitive work, say keeping an unknown progress bar pulsing, but for that timers are a better choice so you can pulse it in given intervals.

Timers are done in a similar manner g_timeout_add or in Vala GLib.Timeout.add, it works the same way except we can now add a time in the first argument to delay the callback by that much milliseconds.

Vala’s async

Now we have learned a lot about how threads can be used in general and you can also apply the same knowledge to C or Python as that was all general glib concepts, but now we will take a look at what Vala offers to us to simplify our work, you can leave here if you are not interested in Vala.

Vala allows marking methods with async this brings interesting functionality to deal with asynchronous code.

At a glance just marking it as async does not magically make it non-blocking, the code inside the function is still ran in the same thread and will block the thread if you do blocking code inside it. What async offers is ways for that method to do asynchronous work like creating a thread but allow us to pause the execution sequence in a non-blocking manner so the caller can wait for the results, again, without blocking.

If you are familiar with async/await in other languages like JavaScript it’s basically very similar.

Do you ever remember having to wrap a callback-based JavaScript function to use Promises because well the library is still in ancient code? Something like this:

function doWorkPromise() {
  return new Promise((resolve, reject) => {
    doWork((err, res) => {
      if (err) return reject(err);
      return resolve(res);
    });
  });
}

It’s a bit ugly but but once you are done the new function is promisified and we can use it very neatly with async/await. That’s basically what Vala also allows us to do, so let’s wrap a thread with it.

public async void do_work () {
  new Thread<void>(() => {
    // Simulate heavy work.
    Thread.usleep (5000000);
    // Schedule the callback to be called in the main thread using idle.
    GLib.Idle.add(do_work.callback);
  });

  yield;
  // Anything below will only run after the yield has been acknowleged.
  // In our case that happens after the thread is done.
}

And we are done, it’s just like the previous JavaScript snippet but now for a Vala thread-based code, we wrapped it to be able to use it in a manner similar to JavaScript’s Promises.

But, what is going on, you ask. Essentially when you call yield the function is told to wait (non-blockingly) before returning any results until the same function’s .callback property is called. Similar to JavaScript having to wait for the Promise’s resolve method to be called. But in our case a bit of thread switching is involved so we chose to call that callback in the main thread with the same idle sources I explained before, this allows the rest of the method to continue executing in the main thread.

But how do we call this? Now if you are familiar with other async/await languages you already know that if the method is async it can only be awaited in other async functions. If the caller is also inside an async function they can call this method with yield (equivalent of await in Vala) so like:

public async void do_stuff () {
  yield do_work();
}  

If do_work had any return we could use the value here as well.

But how does it all begin? if do_stuff needs to be async to call do_work who’s gonna call do_stuff ? Again it’s all too similar to JavaScript, we can also use a callback-based method to invoke them, similar to using those promise-based functions with their .then/.cache which avoids the need for an async context.

In Vala this comes with each async method coming with a .begin() property that takes a callback, similar to .then in JavaScript

do_stuff.begin ((obj) => {
  // Callback here after do_stuff is done.
});

obj is the Object on which the async method was called, it is optional and we can omit it if we don’t care about it.

if do_stuff returned anything we’d have it as the second argument here, but not so easily, the value is not directly given, instead we get a GLib.AsyncResult which acts like some token that we need to exchange for the results, the async method also includes a end method which is to be used to exchange the results.

To demonstrate, update do_stuff to include return 5; at the end and change return type to int now we’d call it like so:

do_stuff.begin ((obj, res) => {
  var value = do_stuff.end(res);
  message ("Value: %d", value);
});

Now we got our results out of it. This should be familiar if you are used to programming with Gio in which functions typically have an _async variant and then a _finish variant to do the exchange for the results.

Conclusion

So if you ever have some slow operation, consider wrapping it in a thread.

Also don’t overestimate your users’ resources. For example if a network request is happily working fast because you have a blazing fast internet connection so you don’t really feel that GUI hang, don’t assume your users have that, people on slow connections will suffer, move that network request onto a thread! Same goes with other CPU intensive calculations, don’t assume it’s fine just because you have a powerful CPU to crunch through it, not noticing the hang, others will suffer on their tiny Raspberry Pi or something.

I don’t claim to be an expert on this topic and this post was supposed to be me exploring threads as someone who’ve rarely had to touch threads, so take it with a grain of salt and leave a comment if there’s anything I misunderstood.

That said, it was a fun journey and I started to love Vala as a language for GTK/GLib programming, it really makes things neat.