Way back in 2011 people were discussing usage of modern GCC features like __attribute__((cleanup()))
. A few years later it found it’s way into our API’s in GLib with one small caveat, only GCC/Clang support (so no MSVC/Xlc/SunProC). Since I couldn’t care less about MSVC I’ve been using it for years (and really Microsoft, you could contribute more to the mental health of open source programmers by modernizing MSVC).
I want to give a few examples of patterns I use to make tracking down issues easier.
Using GTask
static void my_async_cb (GObject *object, GAsyncResult *result, gpointer user_data) { // take ownership of task from caller g_autoptr(GTask) task = user_data; g_autoptr(GError) error = NULL; g_assert (G_IS_TASK (task)); g_assert (G_IS_ASYNC_RESULT (result)); if (!do_something_finish (result, &error)) // explicitly pass ownership of error to GTask g_task_return_error (task, g_steal_pointer (&error)); else g_task_return_boolean (task, TRUE); } void my_obj_frob_async (MyObj *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(GTask) task = NULL; g_return_if_fail (MY_IS_OBJ (self)); g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); task = g_task_new (self, cancellable, callback, user_data); g_task_set_source_tag (task, my_obj_frob_async); // pass task ownership to callback do_something_async (cancellable, my_async_cb, g_steal_pointer (&task)); }
The nice thing about this style is that all ownership transfers are explicit. I hope that in the future we can get some automatic checking of this via coverity or gcc/clang plugins. But we’re not quite there yet. Either way, it simplifies the auditing case.
Using Idle Callbacks
State tracking during idle callbacks can very easily turn into security issues. So make sure your function always has access to a reference, and simplify your releasing of the data by allowing the GSource
to own the closure. For example, with a GObject
it is pretty simple.
static gboolean frob_from_idle_cb (gpointer data) { MyObj *self = data; my_obj_frob (self); return G_SOURCE_REMOVE; } gdk_threads_add_idle_full (G_PRIORITY_LOW, frob_from_idle_cb, g_object_ref (obj), g_object_unref);
The GSource which is registered and calls frob_from_idle_cb()
will automatically call g_object_unref()
after the function returns G_SOURCE_REMOVE
. This also ensures your object isn’t finalized before the callback has occurred.
This also works with g_timeout_add_full()
, gdk_threads_add_timeout_full()
.
Creating Custom Closures
Sometimes you might have state that is more complex than passing around a single GObject
. In that case, create a closure structure and define a cleanup function so you can use g_autoptr()
.
typedef struct { MyObj *self; guint count; } FrobState; static void frob_state_free (FrobState *state) { g_clear_object (&state->self); g_free (state); } G_DEFINE_AUTOPTR_CLEANUP_FUNC (FrobState, frob_state_free)
With the above definition, you can use g_autoptr(FrobState) state = user_data;
like you would for objects. This also works with the idle functions, just use (GDestroyNotify)frob_state_free
as your cleanup function.