Using g_object_unref from non-main threads

According to the GObject Reference Manual g_object_ref and g_object_unref are thead-safe. I am aware that many libraries, e.g. GTK aren’t attempting to be thread-safe, so presumably g_object_unref cannot be used from any thread in case it removes the last reference, causing some clean-up. (Or should libraries ensure that their clean-up is thread-safe?) On the other hand, some libraries appear to claim some level of thread-safety, for example, GIO documentation says that the GLib documentation is generally applicable to it and the GLib documentation says all functions are invisibly threadsafe with the exception of data structure manipulation functions, so I would expect g_object_unref to be generally thread-safe for GIO objects.

Is there a way to know from introspection metadata whether g_object_unref can be called from a thread that is not the main context’s thread? Or should we assume that no objects are thread-safe except those in GIO?

Good questions. Ideally, our documentation would be clear enough on these topics that you wouldn’t need to ask here, but I’m not surprised that’s not currently true.

Unless documented otherwise, you need to assume the GObject that you are working with is not threadsafe. In that case, you can’t share it on multiple threads at all. You should never wind up thinking about “can I call g_object_unref() on this thread or not?” because the object should never be used on any thread other than the thread that it was created on. i.e. no, it’s not safe, and you already lost before you got to this point.

Correct. Well, in theory, at least. I wouldn’t be surprised if there were threadsafety bugs in various functions.

Nah, GObjects count as data structures, I’m afraid, so they are generally not threadsafe. Only a few GObjects are documented to be threadsafe. You should start by assuming your object is not threadsafe, and modify that assumption only if the documentation explicitly tells you otherwise.

Sometimes the safety guarantees are complicated. For example, GIOStream is documented to be safe to use from one reader thread and one writer thread, but only if certain rules are followed. Other objects may be threadsafe, but only if you don’t call certain functions. For example, using g_object_add_weak_pointer() or g_signal_connect_object() on a threadsafe object is never safe if that object is used on multiple threads, because these functions are documented to not be threadsafe.

All that said, non-threadsafe objects should still generally be safe to create and use on secondary threads, as long as they stay on the thread that they are created on. Some libraries or applications might not follow this rule – e.g. GTK objects may only be safely used on the GTK thread, which is almost always the main thread – but anything provided by GLib itself will.

No, there’s no metadata for this. You just have to read the docs. And again, don’t assume GIO objects are threadsafe, because they’re usually not.

Also, keep in mind that your application may have multiple main contexts. In general, you’ll probably want to have one per thread, but sometimes you’ll need more. Here’s the best documentation on how to use GMainContext.

Unfortunately there is no annotation for it, just an old issue: Thread-safety annotation needed (#119) · Issues · GNOME / gobject-introspection · GitLab

Everything in the GObject library is (supposed to be) thread-safe (with the notable exception of GBinding until the latest release), but in general like Michael said you’ll have to check the documentation. GStreamer would be an example of a library on top of GObject that is fully thread-safe.

The Rust bindings (as Rust has a way to mark and enforce thread-safety to some degree via its type system) are currently manually marking APIs as thread-safe or not and assume that everything not explicitly configured as thread-safe on the bindings-side is not.

Thanks for your quick reply and the link. That gave me quite a lot to think about.

The reason for asking relates to the SML bindings that I am developing. For both supported compilers, a hook into the garbage collector to run code for unreachable objects (currently just g_object_unref) occurs in its own thread. To some extent the bindings can dictate a policy. Ideally, this would just be simple guidance for the user to follow, e.g. if you have objects that must be freed from the thread in which their ownership was taken, not the thread of the default main context, you must create a local main context (but I haven’t done enough with multiple threads to know whether that is practical yet).

My initial thought of just using g_idle_add to schedule g_object_unref is potentially problematic for two reasons:

  1. On termination, the main loop may have stopped iterating the default main context, so g_object_unref scheduled with g_idle_add wouldn’t get run. However, in that case it should be fine to run g_object_unref from the GC thread as there won’t be any race conditions (I think). The code for g_main_context_invoke_full looks like a useful guide for doing this. (I haven’t yet worked out whether that code guarantees that a source added is actually run - can the main loop exit after g_main_context_invoke_full tried to acquire the context but before the source was added?)

  2. The default main context may not be suitable because the object was created in a thread that is not the one for the default main context and the object is not thread-safe. In this case, I think the user would need to create their own thread-local main context and the bindings would add g_object_unref as an idle source to that context. With that solution, the question of termination applies as for issue 1 , above, and I need to think about whether the bindings keep a strong or weak reference to a main context.

I’ll report back once I’ve done some experimentation.

The C# bindings are doing all unreffing from the default main context for that reason. That of course a) requires a main loop to actually run there and b) introduces a lot of overhead if lots of objects are from other threads. In case of GStreamer usage from C# this is a problem.

Not sure how other bindings are handling this but probably similar as they’ll likely be designed for GTK usage first and multi-threaded libraries were an afterthought.

For doing it from a local main context you would require the user to create one when creating objects, remember that one and on GC pass the unref to that?

GMainContext is unfortunately no GObject and doesn’t have any weak reference mechanisms :frowning:

Good to know there are such libraries and it’s worth retaining support thread-safe objects.

Hopefully one day that can feed back upstream to GI but I’ll likely do something similar for now.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.