Reference counting

About

This document contains guidelines about when and how to use reference counting. If you wonder about the annotations in the gtk-doc comments of the examples (caller-owns, null-ok, callee-owns), take a look at these future ideas for gtk-doc and the documentation about GObject introspection.

The official GObject documentation about this can be found here.

Overview

  • For deferred calls, delegates (callbacks), threads, workers
  • Anything that returns an instance, needs to add a reference to it
    • That includes singletons
    • Constructors do this too, they just return a first reference
    • Don't forget out parameters (by reference passing an instance)
  • For example when using a threadpool

  • When passing to another part of the program of which you don't know how it'll be implemented in future
    • The problem here is that you don't know what your colleague developers will do to implement it.
  • In general assume that adding (and removing) a reference is relatively cheap. Unless you are referencing thousands of times, you shouldn't worry about performance impact at all.
  • It's FALSE to think that NOT adding references if necessary is a memory leak fix. You don't fix memory leaks by introducing race conditions. You produce correct code instead. That's really the only way.

Caveats

GObject will not auto-detect cyclic references.

  • You can use the dispose method instead to clean up instance references, although I don't know myself how exactly this works (somebody please describe this). Read the section "Reference counts and cycles" of this document for more information about g_object_dispose.

  • You can use weak references in case your code requires a cyclic reference. In the GWeakNotify you can set the reference to NULL and make the implementation of A aware of its B property being NULL everywhere (for example).

These are examples of cyclic references even if your finalize (instance destructors) are correct at cleaning up resources:

A.B = ref (B)
B.A = ref (A)

A.B = ref (B)
A.C = ref (C)
C.B = ref (B)
C.A = ref (A)

list = new list()

# if list.Add adds a reference to A (which it probably
# should do. A weak reference where the GWeakNotify 
# removes the pointer from list might be a nice solution
# too)

list.Add (A)
A.list = ref (list)

or

B.list = ref (list)
A.B = ref (B)

Deferred calls

Threads

This is a typical worker thread example

typedef struct {
   GObject *instance;
} ThreadInfo;

static gpointer
thread_func (gpointer data)
{
  ThreadInfo *info = data;

  /* Perform work on info->instance */  

  gdk_threads_enter ();
  /* Update your UI, and use info->instance */
  gdk_threads_leave ();

  g_object_unref (info->instance);
  g_slice_free (ThreadInfo, info);

  g_thread_exit (NULL);
  return NULL;
}

The caller

static void
MyMethod (GObject *instance) {
  ThreadInfo *info = g_slice_new (ThreadInfo);
  info->instance = g_object_ref (instance);

  g_thread_create (thread_func,
                   info,
                   TRUE/FALSE,
                   NULL);
}

Timeouts and idle-adds

Also read about Gdk and threading for the gdk_threads_enter.

static gboolean
callback (gpointer user_data) {
  GObject *instance = user_data;
  gdk_threads_enter ();
  /* work with instance and don’t worry */
  gdk_threads_leave ();
  return FALSE;
}

The caller as timeout

static void
MyMethod (GObject *instance) {
  g_timeout_add_full (G_PRIORITY_DEFAULT, 
                      5 * 1000,
                      callback, 
                      g_object_ref (instance),
                      (GDestroyNotify) g_object_unref);
}

The caller as idle

static void
MyMethod (GObject *instance) {
  g_idle_add_full (G_PRIORITY_DEFAULT,
                   callback, 
                   g_object_ref (instance),
                   (GDestroyNotify) g_object_unref);

}

In your library's API

Whenever you return an instance

You do want to opt for so-called caller-owns APIs. The reason for that is consistency and to improve thread safety (not solve). If all of your instance returning methods add a reference, then you are more sure that the user of your API wont finalize the instance that he got in another context than the one where he asked for the instance.

/**
 * my_type_get_something:
 * @self: a #MyType
 *
 * Returns something, you must unreference the returned value
 *
 * returns: (caller-owns): something
 **/
GObject*
my_type_get_something (MyType *self)
{
  return g_object_ref (self->something);
}

For example

In this example we have two contexts. If my_type_get_something wouldn't have returned a reference, we would have created a race condition in case either context_a or context_b or the calling context where MyMethod runs would cause a finalization of self->something before the work of either context_a or context_b is done.

Now that we do add a reference, and since we also added a reference to my_something_owner at the creation of the worker threads, there's no race possible. If the calling context where MyMethod runs does a g_object_unref, then while either context_a or context_b are working on instance, instance will always be kept alive until the last one is finished with it.

Context A

static gpointer
context_a (gpointer data)
{
  ThreadInfo *info_a = data;

  instance = my_type_get_something (info_a->my_something_owner);

  /* The if is unnecessary because the API annotation doesn't claim 
   * that null is ok. But this would be for a (null-ok) annotated
   * API then. 
   *
   * A null-ok would have had a returns line like this:
   * returns: (caller-owns) (null-ok): something
   **/

  if (instance) {
      /* Work with instance */
      g_object_unref (instance);
  }

  g_object_unref (info_a->my_something_owner);
  g_slice_free (ThreadInfo, info_a);
  g_thread_exit (NULL);
  return NULL;
}

Context B

static gpointer
context_b (gpointer data)
{
  ThreadInfo *info_b = data;

  instance = my_type_get_something (info_b->my_something_owner);

  if (instance) {
      /* Work with instance */
      g_object_unref (instance);
  }

  g_object_unref (info_b->my_something_owner);
  g_slice_free (ThreadInfo, info_b);
  g_thread_exit (NULL);
  return NULL;
}

The caller

static void
MyMethod (MyType *a)
{
  ThreadInfo *info_a = g_slice_new (ThreadInfo);
  ThreadInfo *info_b = g_slice_new (ThreadInfo);

  my_type_set_something (a, inject_something);

  /* Per wanted context we add a reference to a */
  info_a->my_something_owner = g_object_ref (a);
  info_b->my_something_owner = g_object_ref (a);

  g_thread_create (context_a,
                   info_a,
                   TRUE/FALSE,
                   NULL);

  g_thread_create (context_b,
                   info_b,
                   TRUE/FALSE,
                   NULL);

  /* Each context has put its own 'reason to life' on a above.
   * This means that we can take our own reasons for a to life
   * away (a will be alive until both context_a and context_b
   * are done with it, as they'll take their own references).
   *
   * Because we implemented a correct, that also means that it
   * will itself keep its 'something' property working until it
   * itself gets finalized. Meaning that none of the instances
   * that either context_a nor context_b need will ever be in
   * finalized state. */
  ...
}

Out parameters

This follows the same rules as returning things. If you by-reference pass instances, you better make APIs that guarantee that references are added.

/**
 * my_type_fetch_me_into:
 * @self: a #MyType
 * @to_fetch: (out) (null-ok) (caller-owns): NULL or a location where to store what you want
 *
 * You must unreference @to_fetch after use.
 **/
void
my_type_fetch_me_into (MyType *self, GObject **to_fetch)
{
  if (to_fetch) {
        *to_fetch = g_object_ref (what_you_want);
  }
}

Example:

static void MyMethod (...) 
{
  MyType *inst = ...
  GObject *what_i_want = NULL;

  my_type_fetch_me_into (inst, &what_i_want);

  /* Our out parameter is annotated as null-ok, 
   * so we must check for it */

  if (what_i_want) {

      /* Work with what_i_want */

      g_object_unref (what_i_want);
  }
}

The typical ones

Iterator

This example shows that all methods that return an instance, add a reference. The example shows how you use libgee in C. Gee is a library written in Vala that provides a group of typical collection types.

GeeCollection *list = ...
GeeIterator *iter = gee_iterable_iterator (list);
while (gee_iterator_next (iter)) {
  GObject *current = gee_iterator_get (iter);
  /* Work with current */
  g_object_unref (current);
}
g_object_unref (iter);

Debugging reference count

You can get some hints on reference count debugging at ReferenceCountDebugging.

Attic/ReferenceCounting (last edited 2013-11-23 00:50:27 by WilliamJonMcCann)