The GObject messaging system

Closures
C Closures
Non-C closures (for the fearless)
Signals
Signal registration
Signal connection
Signal emission
The detail argument

Closures

Closures are central to the concept of asynchronous signal delivery which is widely used throughout GTK+ and GNOME applications. A closure is an abstraction, a generic representation of a callback. It is a small structure which contains three objects:

  • a function pointer (the callback itself) whose prototype looks like:

    1
    return_type function_callback ( , gpointer user_data);

  • the user_data pointer which is passed to the callback upon invocation of the closure

  • a function pointer which represents the destructor of the closure: whenever the closure's refcount reaches zero, this function will be called before the closure structure is freed.

The GClosure structure represents the common functionality of all closure implementations: there exists a different closure implementation for each separate runtime which wants to use the GObject type system. [4] The GObject library provides a simple GCClosure type which is a specific implementation of closures to be used with C/C++ callbacks.

A GClosure provides simple services:

C Closures

If you are using C or C++ to connect a callback to a given event, you will either use simple GCClosures which have a pretty minimal API or the even simpler g_signal_connect functions (which will be presented a bit later).

g_cclosure_new will create a new closure which can invoke the user-provided callback_func with the user-provided user_data as its last parameter. When the closure is finalized (second stage of the destruction process), it will invoke the destroy_data function if the user has supplied one.

g_cclosure_new_swap will create a new closure which can invoke the user-provided callback_func with the user-provided user_data as its first parameter (instead of being the last parameter as with g_cclosure_new). When the closure is finalized (second stage of the destruction process), it will invoke the destroy_data function if the user has supplied one.

Non-C closures (for the fearless)

As was explained above, closures hide the details of callback invocation. In C, callback invocation is just like function invocation: it is a matter of creating the correct stack frame for the called function and executing a call assembly instruction.

C closure marshallers transform the array of GValues which represent the parameters to the target function into a C-style function parameter list, invoke the user-supplied C function with this new parameter list, get the return value of the function, transform it into a GValue and return this GValue to the marshaller caller.

A generic C closure marshaller is available as g_cclosure_marshal_generic which implements marshalling for all function types using libffi. Custom marshallers for different types are not needed apart from performance critical code where the libffi-based marshaller may be too slow.

An example of a custom marshaller is given below, illustrating how GValues can be converted to a C function call. The marshaller is for a C function which takes an integer as its first parameter and returns void.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
g_cclosure_marshal_VOID__INT (GClosure     *closure,
                              GValue       *return_value,
                              guint         n_param_values,
                              const GValue *param_values,
                              gpointer      invocation_hint,
                              gpointer      marshal_data)
{
  typedef void (*GMarshalFunc_VOID__INT) (gpointer     data1,
                                          gint         arg_1,
                                          gpointer     data2);
  register GMarshalFunc_VOID__INT callback;
  register GCClosure *cc = (GCClosure*) closure;
  register gpointer data1, data2;

  g_return_if_fail (n_param_values == 2);

  data1 = g_value_peek_pointer (param_values + 0);
  data2 = closure->data;

  callback = (GMarshalFunc_VOID__INT) (marshal_data ? marshal_data : cc->callback);

  callback (data1,
            g_marshal_value_peek_int (param_values + 1),
            data2);
}

There exist other kinds of marshallers, for example there is a generic Python marshaller which is used by all Python closures (a Python closure is used to invoke a callback written in Python). This Python marshaller transforms the input GValue list representing the function parameters into a Python tuple which is the equivalent structure in Python.



[4] In practice, closures sit at the boundary of language runtimes: if you are writing Python code and one of your Python callbacks receives a signal from a GTK+ widget, the C code in GTK+ needs to execute your Python code. The closure invoked by the GTK+ object invokes the Python callback: it behaves as a normal C object for GTK+ and as a normal Python object for Python code.

[5] Closures are reference counted and notify listeners of their destruction in a two-stage process: the invalidation notifiers are invoked before the finalization notifiers.