The GObject messaging system
Closures
closure是一个抽象、通用的概念,它包含一个函数和一些变量。作用就是实现函数回调功能。
我们看看GLib对closure是怎么定义的。
/**
* GClosure:
* @in_marshal: Indicates whether the closure is currently being invoked with
* g_closure_invoke()
* @is_invalid: Indicates whether the closure has been invalidated by
* g_closure_invalidate()
*
* A #GClosure represents a callback supplied by the programmer.
*/
struct _GClosure
{
/*< private >*/
guint ref_count : 15; /* (atomic) */
/* meta_marshal is not used anymore but must be zero for historical reasons
as it was exposed in the G_CLOSURE_N_NOTIFIERS macro */
guint meta_marshal_nouse : 1; /* (atomic) */
guint n_guards : 1; /* (atomic) */
guint n_fnotifiers : 2; /* finalization notifiers (atomic) */
guint n_inotifiers : 8; /* invalidation notifiers (atomic) */
guint in_inotify : 1; /* (atomic) */
guint floating : 1; /* (atomic) */
/*< protected >*/
guint derivative_flag : 1; /* (atomic) */
/*< public >*/
guint in_marshal : 1; /* (atomic) */
guint is_invalid : 1; /* (atomic) */
/*< private >*/ void (*marshal) (GClosure *closure,
GValue /*out*/ *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
/*< protected >*/ gpointer data;
/*< private >*/ GClosureNotifyData *notifiers;
/* invariants/constraints:
* - ->marshal and ->data are _invalid_ as soon as ->is_invalid==TRUE
* - invocation of all inotifiers occurs prior to fnotifiers
* - order of inotifiers is random
* inotifiers may _not_ free/invalidate parameter values (e.g. ->data)
* - order of fnotifiers is random
* - each notifier may only be removed before or during its invocation
* - reference counting may only happen prior to fnotify invocation
* (in that sense, fnotifiers are really finalization handlers)
*/
};
对于不同的runtime,closure有不同具体实现,对于C/C++而言,这个具体的实现就是GCClosure.
其定义如下:
struct _GCClosure
{
GClosure closure;
gpointer callback;
};
g_cclosure_new、g_cclosure_new_swap都是用于创建closure的函数,创建完成的closure会以用户提供的数据为参数,调用用户提供的callback_func。二者的不同在于,用户提的的数据作为参数传递给函数时,数据在参数中的位置不同,对于g_cclosure_new,用户的参数是作为最后一个参数,而对于g_cclosure_new_swap,用户的数据作为第一个参数的。
我们注意到,在GClosure的定义中,有一个函数指针,marshaller,对于C语言来说,这个函数的作用是将一组GValue转换为c格式的参数列表,这组GValue代表的是即将传递给回调函数的参数。当参数转换完成后,marhaaller还会调用用户提供的回调函数,并以刚刚转换完成的参数列表作为参数。当回调函数执行完成后,有将函数的返回结果转换成GValue类型,并将GValue返回到marshaller的调用者。
Glib中有一个通用的函数,g_cclosure_marshal_generic,我们在平常在调用g_signal_new时,当将其参数c_marshaller设置为NULL时,g_cclosure_marshal_generic就是此时的默认marshaller.当然,还存在其他的marshaller,比如说g_cclosure_marshal_VOID__INT。
我们以g_cclosure_marshal_generic为例,看看marshaller的执行流程
/**
* g_cclosure_marshal_generic:
* @closure: A #GClosure.
* @return_gvalue: A #GValue to store the return value. May be %NULL
* if the callback of closure doesn't return a value.
* @n_param_values: The length of the @param_values array.
* @param_values: An array of #GValues holding the arguments
* on which to invoke the callback of closure.
* @invocation_hint: The invocation hint given as the last argument to
* g_closure_invoke().
* @marshal_data: Additional data specified when registering the
* marshaller, see g_closure_set_marshal() and
* g_closure_set_meta_marshal()
*
* A generic marshaller function implemented via
* [libffi](http://sourceware.org/libffi/).
*
* Normally this function is not passed explicitly to g_signal_new(),
* but used automatically by GLib when specifying a %NULL marshaller.
*
* Since: 2.30
*/
void
g_cclosure_marshal_generic (GClosure *closure,
GValue *return_gvalue,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data)
{
ffi_type *rtype;
void *rvalue;
int n_args;
ffi_type **atypes;
void **args;
int i;
ffi_cif cif;
GCClosure *cc = (GCClosure*) closure;
gint *enum_tmpval;
gboolean tmpval_used = FALSE;
enum_tmpval = g_alloca (sizeof (gint));
if (return_gvalue && G_VALUE_TYPE (return_gvalue))
{
rtype = value_to_ffi_type (return_gvalue, &rvalue, enum_tmpval, &tmpval_used);
}
else
{
rtype = &ffi_type_void;
}
rvalue = g_alloca (MAX (rtype->size, sizeof (ffi_arg)));
n_args = n_param_values + 1;
atypes = g_alloca (sizeof (ffi_type *) * n_args);
args = g_alloca (sizeof (gpointer) * n_args);
if (tmpval_used)
enum_tmpval = g_alloca (sizeof (gint));
if (G_CCLOSURE_SWAP_DATA (closure))
{
atypes[n_args-1] = value_to_ffi_type (param_values + 0,
&args[n_args-1],
enum_tmpval,
&tmpval_used);
atypes[0] = &ffi_type_pointer;
args[0] = &closure->data;
}
else
{
atypes[0] = value_to_ffi_type (param_values + 0,
&args[0],
enum_tmpval,
&tmpval_used);
atypes[n_args-1] = &ffi_type_pointer;
args[n_args-1] = &closure->data;
}
for (i = 1; i < n_args - 1; i++)
{
if (tmpval_used)
enum_tmpval = g_alloca (sizeof (gint));
atypes[i] = value_to_ffi_type (param_values + i,
&args[i],
enum_tmpval,
&tmpval_used);
}
if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, n_args, rtype, atypes) != FFI_OK)
return;
ffi_call (&cif, marshal_data ? marshal_data : cc->callback, rvalue, args);
if (return_gvalue && G_VALUE_TYPE (return_gvalue))
value_from_ffi_type (return_gvalue, rvalue);
}
Signals
在之前的例子中,我们在应用程序中使用过signal, 接下来,我们深入聊聊signal背后的逻辑。
信号的作用是连接特定用户事件与事件监听者。例如,在GTK中, 当窗口系统收到用户事件(比如键盘敲击事件、鼠标移动事件),窗口系统会生成GTK事件,并通过信号的形式发送到widget对象实例。
信号会在注册过程中会与具体的对象类型进行绑定,这意味着此对象类型会发出这个信号。在父对象类型注册的信号在其子对象类型也能使用。信号发生过程中会使用到closure.用户能通过连接到信号的closure控制发出或者停止发出信号。当一个类型实例上有信号发出,这个类型实例上所有连接到此信号的closure都会被调用。
为一个存在的对象类型注册新信号时,会使用函数g_signal_newv()、g_signal_new_valist()以及g_signal_new()。g_signal_new_valist()和g_signal_new()都是通过g_signal_newv()来实现的,我们看看GLib中g_signal_newv()中的声明。
/**
* g_signal_newv:
* @signal_name: the name for the signal
* @itype: the type this signal pertains to. It will also pertain to
* types which are derived from this type
* @signal_flags: a combination of #GSignalFlags specifying detail of when
* the default handler is to be invoked. You should at least specify
* %G_SIGNAL_RUN_FIRST or %G_SIGNAL_RUN_LAST
* @class_closure: (nullable): The closure to invoke on signal emission;
* may be %NULL
* @accumulator: (nullable): the accumulator for this signal; may be %NULL
* @accu_data: (nullable) (closure accumulator): user data for the @accumulator
* @c_marshaller: (nullable): the function to translate arrays of
* parameter values to signal emissions into C language callback
* invocations or %NULL
* @return_type: the type of return value, or %G_TYPE_NONE for a signal
* without a return value
* @n_params: the length of @param_types
* @param_types: (array length=n_params) (nullable): an array of types, one for
* each parameter (may be %NULL if @n_params is zero)
*
* Creates a new signal. (This is usually done in the class initializer.)
*
* See g_signal_new() for details on allowed signal names.
*
* If c_marshaller is %NULL, g_cclosure_marshal_generic() will be used as
* the marshaller for this signal.
*
* Returns: the signal id
*/
guint
g_signal_newv (const gchar *signal_name,
GType itype,
GSignalFlags signal_flags,
GClosure *class_closure,
GSignalAccumulator accumulator,
gpointer accu_data,
GSignalCMarshaller c_marshaller,
GType return_type,
guint n_params,
GType *param_types)
可以看出,信号基本就是对连接到信号的closure的描述。
信号在发送过程中会调用一系列的回调函数。这些回调函数主要分为两类:per-object类和用户提供的。per-object类回调函数通常称为"object method handler" 或者"default(signal)handler",用户提供的回调函数通常称为“signal handler"。
所有的handler都将对象类型实例指针作为第一个参数,将一个gpointer user_data作为最后一个参数,signal-define 参数在两者之间,user_data中通常会有用户在连接handler到信号时所提供的数据。回调函数的类型是GCallback。
一个正常的信号发送过程包含五个阶段,除非中间的过程被打断。
- 为设置了G_SIGNAL_RUN_FIRST flag的信号调用 default handler
- 调用用户提供的signal handler(signal handler 不是通过g_signal_connect_after与信号进行连接的)
- 为设置了G_SIGNAL_RUN_LAST flag的信号调用default handler
- 调用用户提供的signal handler(signal handler 不是通过g_signal_connect_after与信号进行连接的)
- 为设置了G_SIGNAL_RUN_CLEANUP flag的信号调用default handler
用户提供的signal handler是以他们连接到信号的先后顺序执行的。