C# / GTK# – Re-Entrancy Issues

As I’ve mentioned a couple of times before, I’ve been working on a C# application, using GTK# to enable me to create a GUI application which can run on Linux, Mac or Windows. It works well but I’ve been working through some “gotchas” here and there. Here’s a useful little tip… Gtk# is not a thread-safe toolkit.

Grizzled GTK veterans will all know that one can only make GTK calls from the main thread, but I didn’t :)

I had my application working quite nicely, and I thought it would be useful to have a label and the window’s title change according to the status of the application (the status changes according to time elapsed). To ensure the application remained responsive, I figured UI changes could happen on a separate thread. Oops.

All seemed to work OK, except I started noticing the application would suddenly die for apparently no reason, usually after several minutes of working fine. Running the application in the debugger showed some console output at the time of the crash:

The program 'xyz' received an X Window System error.
This probably reflects a bug in the program. 
The error was 'BadLength (poly request too large 
or internal Xlib length erro'.

Well, that’s not much help! A bit of “subtractive debugging” (commenting out chunks of code) later, I’d narrowed it down to the UI updates on the separate thread. A bit of Googling around and suddenly the penny dropped about doing GTK calls on the separate thread.

Fortunately there’s a really simple way of avoiding this problem… simply run your code on the GTK main thread using an anonymous method with Gtk.Application.Invoke():

string s="World";
Gtk.Application.Invoke (delegate {
    this.Title="Hello " + s;
});

Using an anonymous method like this means you have access to any variables in the “outer” scope of the block in which it is defined (the string “s” in the example above), which makes it easy to pass over exactly what you want to be executed on the “safe” thread.

All rock solid now.

Leave a Reply

Your email address will not be published. Required fields are marked *