Use g_set_object() to simplify (and safetyify) GObject property setters

tl;dr: Use g_set_object() to conveniently update owned object pointers in property setters (and elsewhere); see the bottom of the post for an example.

A little while ago, GLib gained a useful function called g_clear_object(), which clears an owned object pointer (a pointer which owns a reference to a GObject). GLib also just gained a function called g_set_object(), which works similarly but can either clear an object pointer or update it to point to a new object.

Why is this valuable? It saves a few lines of code each time an object pointer is updated, which isn’t much in itself. However, one thing it gets right is the order of reference counting operations, which is a common mistake in property setters. Instead of:

/* This is bad code. */
if (object_ptr != NULL)
    g_object_unref (object_ptr);
if (new_object != NULL)
    g_object_ref (new_object);
object_ptr = new_object;

you should always do:

/* This is better code. */
if (new_object != NULL)
    g_object_ref (new_object);
if (object_ptr != NULL)
    g_object_unref (object_ptr);
object_ptr = new_object;

because otherwise, if (new_object == object_ptr) (or if the objects have some other ownership relationship) and the object only has one reference left, object_ptr will end up pointing to a finalised GObject (and g_object_ref() will be called on a finalised GObject too, which it really won’t like).

So how does g_set_object() help? We can now do:

g_set_object (&object_ptr, new_object);

which takes care of all the reference counting, and allows new_object to be NULL. &object_ptr must not be NULL. If you’re worried about performance, never fear. g_set_object() is a static inline function, so shouldn’t adversely affect your code size.

Even better, the return value of g_set_pointer() indicates whether the value changed, so we can conveniently base GObject::notify emissions off it:

/* This is how all GObject property setters should look in future. */
if (g_set_object (&priv->object_ptr, new_object))
    g_object_notify (self, "object-ptr");

Hopefully this will make property setters (and other object updates) simpler in new code.

5 thoughts on “Use g_set_object() to simplify (and safetyify) GObject property setters

  1. Emmanuele

    to be fair, if you have a setter function that takes an object, you should always check if the existing instance and the new instance are the same, and bail out early, e.g.:

    if (priv->obj == new_obj)

    g_clear_object (&priv->obj);
    priv->obj = new_obj ? g_object_ref (new_obj) : NULL;
    g_object_notify (self, "obj");

    this will avoid notifying a property change when there wasn't any as well.

  2. desrt

    Your comment about the reason for doing the ref before the unref is slightly misleading considering we explicitly check for the equals case and early-return.

    It's still useful to order the operations this way, however, in case the two objects have some relationship to each other (ie: one holds a ref on the other). If that's the only ref, and you used a (transfer none) getter to get the other object from inside of it, the unref() on the old could end up killing both objects before you are able to add a ref to the new.

    Thanks again for writing this patch. I look forward to using it soon.

  3. Will Manley

    I quite like swapping pointers in these cases. It makes it easier to do safe locking because you are only doing a pointer swap in the lock so you are safe if `finalise` or `destroy` were to be called and were to call back into your object expecting to be able to take a lock.

    You example with swap and locking:

    GObject *obj = NULL;
    if (new_object != NULL)
    obj = g_object_ref (new_object);
    SWAP(object_ptr, obj);
    g_clear_object (&obj);

    This would be a lot tidier if g_object_ref would take NULL.

    An example with GValue as used in properties:

    GObject *new_object = g_value_dup_object (value)
    SWAP(this->object, new_object);
    g_clear_object (&new_object);

    Not as tidy as using g_set_object, but better from a locking perspective. I find this useful when writing GStreamer code which tends to be heavily threaded. Probably most GObject code doesn't have these concerns

Comments are closed.