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 reasonsas 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是以他们连接到信号的先后顺序执行的。