Reference counting
Contents
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.