gotk3: GTK3 the Go way!

Today marks the first release of gotk3, Conformal’s own GTK3 bindings for Go (available on Github here). These bindings were developed out of a frustration with other GTK bindings for Go either using ancient versions of GTK, not handling memory in a way a Go developer would expect, or simply not working at all on our developers’ OpenBSD and Bitrig machines. gotk3 is Conformal’s response to these issues and attempts to be the best solution for developing new GTK applications with Go.

One of the goals for developing gotk3 was to perform memory management in a very Go-like manner. Like many libraries which must handle memory management manually due to the language they are implemented with, GLib (and GTK which uses it) uses reference counting to determine when an object will never again be used and is ready to be freed, releasing the memory resources it required back to the operating system. However, GLib chooses not to use traditional reference counting, but instead uses a quirky variant called “floating references” to achieve this goal. The rest of this post will cover what floating references do, why they exist, and how gotk3 works around this design to handle memory the way a Go developer expects.

Let’s say that while you are developing a GTK application using the native C API, you create a GtkLabel with a call to gtk_label_new(). This label is created with a reference count of one, but that reference is not a normal reference. Instead, GTK returned an object with a “floating” reference. This means that at the time of initialization, no other objects (read: container widgets) hold a reference to the initialized object. If this label is then added to a container, such as a GtkGrid, the grid will “sink” (remove) the floating reference, changing the object into a traditional reference counted object, but keeping the reference count still at one.

Why is this useful? Consider the following C function in a world where floating references do not exist:

1
2
3
4
5
6
7
8
void
insert_new_label_in_grid(GtkGrid *grid)
{
    GtkWidget *label;

    label = gtk_label_new(“a new label”);
    gtk_container_add(GTK_CONTAINER(grid), label);
}

Calling this function would leak memory because the code to create the label also never unreferences the label before it leaves scope. When this label is added to the grid container, the reference count would be bumped to two. Later, when gtk_widget_destroy() is called on the label, all references to other widgets will be broken (in this case, only the grid). However, because this call causes only one unreference (for removal from the grid), the reference count would never decrease to zero, and the label would never be freed.

There are two possible ways to fix this issue. The first method is to add an explicit unreference at the end of the above function to drop the reference count back to one since the calling function no longer requires a reference to the label. This is a simple solution and is one that most any C programmer expecting normal reference counting would expect to work properly. The other possibility, and the one chosen by GLib, is to use floating references to cause the first container the object is added to to only sink the floating reference and never modify the reference count. According to the GObject class documentation, this particular implementation was chosen only because it was believed to be a convenience to C programmers to save them an extra function call, rather than making programmers remember to perform an explicit unreference at the end of every function where a GTK widget is created.

However nice this code may be for someone who understands how floating references work, this kind of clever code is often dangerous for those who do not understand the intricacies of GLib’s memory management. Even for a developer who does understand the concept, it still takes a lot of time to go through a code base and fix all the issues related to improper handling of floating references (as was done to Conformal’s browser xombrero recently). As another example, the GTK mailing list archives are filled with posts from users wondering why allocating a widget and then calling gtk_widget_destroy() on it in a loop still ramps up their memory usage as they watch the output of top. These users may expect GTK to handle their memory automatically for them even without ever adding a widget to a container as GTK seems to perform this task fine in some situations where explicit unreferences are missing.

This brings us back to Go, and one of the reasons why Conformal began implementing our own GTK bindings. Go programmers expect objects to be automatically freed by Go’s garbage collector after going out of scope, proving that they will never be used by the program again. This assumption changes when using cgo, however. Special care is needed for this situation as any memory allocated by C function calls must be managed manually. Many other GTK bindings for Go make no attempt to perform any memory management for GTK objects and instead opt to rely solely on GObject’s floating references to perform the final unreference when a widget is removed from a container. As this is in no way different from what native GTK in C does, leaks are inevitable unless a developer is acutely aware of the implementation and its pitfalls.

gotk3 takes a different approach by working as closely as possible with Go’s garbage collector to free any unused objects, even if these objects were never added to a container. When gotk3 creates a new struct representing any object inheriting from GInitiallyUnowned (a GLib base class where all children are initialized with a floating reference), the GObject’s floating reference is immediately sunk, and a runtime finalizer is set on the Go struct to perform an unreference of the GObject when the Go struct goes out of scope.

Due to this design, a Go developer never needs to take any special considerations for whether a GTK widget will be freed or not. If a widget is never added to a container, an unreference will occur during the next garbage collection cycle after the widget is out of scope. If the widget was added to a container and the container was removed, the object could either be freed here or at a future garbage collection cycle depending on whether the finalizer has run yet. In either situation, however, the memory is still eventually freed. If Go holds a persistent reference to a widget (for example, a global variable pointing to the object), the finalizer will never run until this pointer has been set to another value. This last situation can be seen as a limitation of mark-and-sweep garbage collectors (even if a value is logically never going to be used again, but it is in scope, it might be used later and should not be freed). However, this pitfall describes how Go’s garbage collector already works, and because it is familiar to any Go developer, it is the behavior we wish to mimic in our bindings as well.

gotk3 is still a relatively new project and more work must be done to cover all of GTK’s calls and features. However, Conformal believes that we have developed one of the better options for writing modern GUI code in Go, and the only good solution for using GTK. With this initial release Conformal hopes to gather the attention of the open source golang community and gain additional help from users and supporters who hold a clear understanding of the goals and design of this project. If you would like to help out, grab a copy from Github and start hacking away.

14 thoughts on “gotk3: GTK3 the Go way!”

    1. Johan,

      I’m failing to see the point of using GObjectIntrospection. It appears to be some kind of middle layer used by other language bindings. However, all we needed for our bindings was a method of calling native GTK functions, and that’s achieved using cgo.

  1. Interesting. Python gets around this by using container functions that “borrow” or “steal” the reference. Usually it makes for convenience, but it is also a pain when chasing down reference count bugs.

    For example PyList_Append() will steal a reference, but PySequence_Append() will add a reference.

  2. Steel,

    I already replied on Hacker News, but wanted to add a comment here as well. In short, we found a lot of issues when trying to install that particular package on OpenBSD/Bitrig, and the package has not been updated in a year. We also wished to target a newer version of GTK and to not develop any bindings for any deprecated functions and features from previous GTK versions.

    1. The github repo includes some small example programs in the examples directory. If you’re looking for more useful programs using gotk3, we are currently in the process of developing a GUI for btcd to handle wallet capabilities.

      1. That sounds really cool. I’d be really interested in hearing more about your experiences with gotk3 in a production environment. Could you guys roll another blog post on the subject? Or if it doesn’t merit a blog post, perhaps a detailed reply here?

        1. In short, it works great. Doing GUI updates using channels for notifications is especially nice. Expect some more code using it on Github soon. 🙂

          1. That’s awesome. Do you mean I should expect it from you guys or generally expect the public to start using it?

          2. Craig,

            We just pushed btcgui to github. Not ready for use yet, but feel free to poke through the source.

  3. Hi, I spent some of my evening checking this out. Thank you for providing this to the community. It’s exactly what I was looking for to do desktop application development with go.

    I was excited to see you folks had recently added GtkBuilder support since that’s really the way to create and manage UI with large applications. I decided to play with that a bit.

    Everything works more or less how I expected it to but I did have one question. This might stem from my inexperience with golang: What is the idiomatic way to handle types returned from gtk.Builder.GetObject?

    It of course returns objects of type IGobject which makes perfect sense. But, of course, IGobject’s don’t have methods that sub classes of Gobject do so we need to inform Go of the type of the returned value.

    I ended up doing something like this:

    obj, err := glade.GetObject(“myWindow”)
    if err != nil {
    // complain loudly and die
    }

    var win *gtk.Window
    win = obj.(*gtk.Window)

    So I have this ‘generic’ var obj that I used to do type assertions with to declared variables of the correct type.

    This all works fine and I had my .glade file up and running quickly but I was curious what you folks at Conformal do because I suspect this method isn’t the most optimal.

    Thanks for gotk3!

    1. Hi! We’re glad everything’s working out for you. There’s an example of how to deal with the returned glib.IObject in the comments (run godoc -http=localhost:6060, and then point your browser to http://localhost:6060/pkg/github.com/conformal/gotk3/gtk/#Builder.GetObject). You will have to use a type assertion (like you do) to get a concrete type, because GetObject() returns an interface (as it must support returning any object that embeds a glib.Object). Note that the type assertion will not always work though (for example, if you tried to type assert a gtk.Button as a gtk.Label). Setting and checking the ‘ok’ variable, like in that example, will let you know whether the type assertion succeeded or failed.

    2. What’s your app? I’d be curious to look at the source code to see what’s happening under the hood (and possibly contribute if the project is sufficiently interesting for my purposes).

Leave a Reply

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