|
数据定义 |
说明 |
gtype |
一个全局指针数组, 用来保存基本类型; 通过基本类型顺藤摸瓜,可以找到其子类型 |
|
static GHashTable *static_type_nodes_ht = NULL; |
一个全局的Hash Table 这个Hash Table一般是通过“名字”来查询TypeNode时使用 |
|
|
|
|
Signal |
双指针 == 指针数组? |
|
static GBSearchArray *g_signal_key_bsa = NULL; | 方便根据signal name/type来查询signal_id | |
|
信号处理函数用hash table |
|
|
|
|
|
static Emission *g_restart_emissions = NULL; |
|
|
||
object |
这个和CArray关系紧密;是Object的回调函数数组的标志。 |
|
|
static GQuark quark_weak_refs = 0; |
|
|
static GQuark quark_toggle_refs = 0; |
|
|
static GObjectNotifyContext property_notify_context = { 0, }; |
|
|
static gulong gobject_signals[LAST_SIGNAL] = { 0, }; |
全局数组 |
param_spec |
用Hash Table来保存Object的ParamSpec |
|
|
/* --- type initialization --- */ |
一个全局数组 |
1. 定义:
//这个用处比较少,只是用“名字”来查询时用 static GHashTable *static_type_nodes_ht = NULL; //这个用的比较多,访问数组查询效率高 static TypeNode *static_fundamental_type_nodes[(G_TYPE_FUNDAMENTAL_MAX >> G_TYPE_FUNDAMENTAL_SHIFT) + 1] = { NULL, };
2. 初始化
void g_type_init_with_debug_flags (GTypeDebugFlags debug_flags) { G_LOCK_DEFINE_STATIC (type_init_lock); const gchar *env_string; GTypeInfo info; TypeNode *node; volatile GType votype; /* type qname hash table */ static_type_nodes_ht = g_hash_table_new (g_direct_hash, g_direct_equal); /* invalid type G_TYPE_INVALID (0) */ static_fundamental_type_nodes[0] = NULL;
3. New 一个Type:
type_node_any_new_W()
如果是基本类型的,直接放入数组中:static_fundamental_type_nodes;
如果有父类,则查到父类,然后放入父类的子类数组pnode->children[i]中;
同时把新建的Type, 放入Hash Table:static_type_nodes_ht中。
static TypeNode* type_node_any_new_W (TypeNode *pnode, GType ftype, const gchar *name, GTypePlugin *plugin, GTypeFundamentalFlags type_flags) { guint n_supers; GType type; TypeNode *node; guint i, node_size = 0; n_supers = pnode ? pnode->n_supers + 1 : 0; if (!pnode) node_size += SIZEOF_FUNDAMENTAL_INFO; /* fundamental type info */ node_size += SIZEOF_BASE_TYPE_NODE (); /* TypeNode structure */ node_size += (sizeof (GType) * (1 + n_supers + 1)); /* self + ancestors + (0) for ->supers[] */ node = g_malloc0 (node_size); if (!pnode) /* offset fundamental types */ { node = G_STRUCT_MEMBER_P (node, SIZEOF_FUNDAMENTAL_INFO); static_fundamental_type_nodes[ftype >> G_TYPE_FUNDAMENTAL_SHIFT] = node; type = ftype; } else type = (GType) node; g_assert ((type & TYPE_ID_MASK) == 0); node->n_supers = n_supers; if (!pnode) { node->supers[0] = type; node->supers[1] = 0; node->is_classed = (type_flags & G_TYPE_FLAG_CLASSED) != 0; node->is_instantiatable = (type_flags & G_TYPE_FLAG_INSTANTIATABLE) != 0; if (NODE_IS_IFACE (node)) { IFACE_NODE_N_PREREQUISITES (node) = 0; IFACE_NODE_PREREQUISITES (node) = NULL; } else _g_atomic_array_init (CLASSED_NODE_IFACES_ENTRIES (node)); } else { node->supers[0] = type; memcpy (node->supers + 1, pnode->supers, sizeof (GType) * (1 + pnode->n_supers + 1)); node->is_classed = pnode->is_classed; node->is_instantiatable = pnode->is_instantiatable; if (NODE_IS_IFACE (node)) { IFACE_NODE_N_PREREQUISITES (node) = 0; IFACE_NODE_PREREQUISITES (node) = NULL; } else { guint j; IFaceEntries *entries; entries = _g_atomic_array_copy (CLASSED_NODE_IFACES_ENTRIES (pnode), IFACE_ENTRIES_HEADER_SIZE, 0); if (entries) { for (j = 0; j < IFACE_ENTRIES_N_ENTRIES (entries); j++) { entries->entry[j].vtable = NULL; entries->entry[j].init_state = UNINITIALIZED; } _g_atomic_array_update (CLASSED_NODE_IFACES_ENTRIES (node), entries); } } i = pnode->n_children++; pnode->children = g_renew (GType, pnode->children, pnode->n_children); pnode->children[i] = type; } TRACE(GOBJECT_TYPE_NEW(name, node->supers[1], type)); g_hash_table_insert (static_type_nodes_ht, GUINT_TO_POINTER (node->qname), (gpointer) type); return node; }
4. 查询TypeNode,
用的比较多的是lookup_type_node_I,直接访问数组
static inline TypeNode* lookup_type_node_I (register GType utype) { if (utype > G_TYPE_FUNDAMENTAL_MAX) return (TypeNode*) (utype & ~TYPE_ID_MASK); else return static_fundamental_type_nodes[utype >> G_TYPE_FUNDAMENTAL_SHIFT]; }
当使用Type Name来查询时,访问Hash Table比较快:
这个函数目前看,使用还不是很多。
GType g_type_from_name (const gchar *name) { GType type = 0; GQuark quark; g_return_val_if_fail (name != NULL, 0); quark = g_quark_try_string (name); if (quark) { G_READ_LOCK (&type_rw_lock); type = (GType) g_hash_table_lookup (static_type_nodes_ht, GUINT_TO_POINTER (quark)); G_READ_UNLOCK (&type_rw_lock); } return type; }
1. 定义一个全局指针g_param_spec_types,引用时也可以用数组的方式进行:
/* --- type initialization --- */ GType *g_param_spec_types = NULL;
2. 初始化
首先分配一块内存给g_param_spec_types:
然后注册23个基本的参数类型,同时对每种参数类型,都提供相应的处理函数:
void g_param_spec_types_init (void) { const guint n_types = 23; GType type, *spec_types, *spec_types_bound; g_param_spec_types = g_new0 (GType, n_types); spec_types = g_param_spec_types; spec_types_bound = g_param_spec_types + n_types; /* G_TYPE_PARAM_CHAR */ { static const GParamSpecTypeInfo pspec_info = { sizeof (GParamSpecChar), /* instance_size */ 16, /* n_preallocs */ param_char_init, /* instance_init */ G_TYPE_CHAR, /* value_type */ NULL, /* finalize */ param_char_set_default, /* value_set_default */ param_char_validate, /* value_validate */ param_int_values_cmp, /* values_cmp */ }; type = g_param_type_register_static (g_intern_static_string ("GParamChar"), &pspec_info); *spec_types++ = type; g_assert (type == G_TYPE_PARAM_CHAR); }
基本23个参数类型:
+GParamChar | +GParamUChar | +GParamBoolean | +GParamInt | +GParamUInt | +GParamLong | +GParamULong | +GParamInt64 | +GParamUInt64 | +GParamUnichar | +GParamEnum | +GParamFlags | +GParamFloat | +GParamDouble | +GParamString | +GParamParam | +GParamBoxed | +GParamPointer | +GParamValueArray | +GParamObject | +GParamOverride | +GParamGType | `GParamVariant
3. 引用这个数组:
初始化后,就可以直接使用这个数组了:
今后就用宏直接访问该参数类型,以及其关联的处理函数了。
#define G_TYPE_PARAM_CHAR (g_param_spec_types[0]) #define G_TYPE_PARAM_UCHAR (g_param_spec_types[1]) #define G_TYPE_PARAM_BOOLEAN (g_param_spec_types[2]) #define G_TYPE_PARAM_INT (g_param_spec_types[3]) #define G_TYPE_PARAM_UINT (g_param_spec_types[4]) #define G_TYPE_PARAM_LONG (g_param_spec_types[5]) #define G_TYPE_PARAM_ULONG (g_param_spec_types[6]) #define G_TYPE_PARAM_INT64 (g_param_spec_types[7]) #define G_TYPE_PARAM_UINT64 (g_param_spec_types[8]) #define G_TYPE_PARAM_UNICHAR (g_param_spec_types[9]) #define G_TYPE_PARAM_ENUM (g_param_spec_types[10]) #define G_TYPE_PARAM_FLAGS (g_param_spec_types[11]) #define G_TYPE_PARAM_FLOAT (g_param_spec_types[12]) #define G_TYPE_PARAM_DOUBLE (g_param_spec_types[13]) #define G_TYPE_PARAM_STRING (g_param_spec_types[14]) #define G_TYPE_PARAM_PARAM (g_param_spec_types[15]) #define G_TYPE_PARAM_BOXED (g_param_spec_types[16]) #define G_TYPE_PARAM_POINTER (g_param_spec_types[17]) #define G_TYPE_PARAM_VALUE_ARRAY (g_param_spec_types[18]) #define G_TYPE_PARAM_OBJECT (g_param_spec_types[19]) #define G_TYPE_PARAM_OVERRIDE (g_param_spec_types[20]) #define G_TYPE_PARAM_GTYPE (g_param_spec_types[21]) #define G_TYPE_PARAM_VARIANT (g_param_spec_types[22])
例如,创建参数类型实例:
GParamSpec* g_param_spec_char (const gchar *name, const gchar *nick, const gchar *blurb, gint8 minimum, gint8 maximum, gint8 default_value, GParamFlags flags) { GParamSpecChar *cspec; g_return_val_if_fail (default_value >= minimum && default_value <= maximum, NULL); cspec = g_param_spec_internal (G_TYPE_PARAM_CHAR, name, nick, blurb, flags); cspec->minimum = minimum; cspec->maximum = maximum; cspec->default_value = default_value; return G_PARAM_SPEC (cspec); }
Gobject install property时,会大量使用上面这类函数:
static void gst_play_base_bin_class_init (GstPlayBaseBinClass * klass) { GObjectClass *gobject_klass; GstElementClass *gstelement_klass; GstBinClass *gstbin_klass; gobject_klass = (GObjectClass *) klass; gstelement_klass = (GstElementClass *) klass; gstbin_klass = (GstBinClass *) klass; parent_class = g_type_class_peek_parent (klass); gobject_klass->set_property = gst_play_base_bin_set_property; gobject_klass->get_property = gst_play_base_bin_get_property; g_object_class_install_property (gobject_klass, ARG_URI, g_param_spec_string ("uri", "URI", "URI of the media to play", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_klass, ARG_SUBURI, g_param_spec_string ("suburi", ".sub-URI", "Optional URI of a subtitle", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_klass, ARG_QUEUE_SIZE, g_param_spec_uint64 ("queue-size", "Queue size", "Size of internal queues in nanoseconds", 0, G_MAXINT64, DEFAULT_QUEUE_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_klass, ARG_QUEUE_THRESHOLD, g_param_spec_uint64 ("queue-threshold", "Queue threshold", "Buffering threshold of internal queues in nanoseconds", 0, G_MAXINT64, DEFAULT_QUEUE_THRESHOLD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_klass, ARG_QUEUE_MIN_THRESHOLD, g_param_spec_uint64 ("queue-min-threshold", "Queue min threshold", "Buffering low threshold of internal queues in nanoseconds", 0, G_MAXINT64, DEFAULT_QUEUE_MIN_THRESHOLD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_klass, ARG_NSTREAMS, g_param_spec_int ("nstreams", "NStreams", "number of streams", 0, G_MAXINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_klass, ARG_STREAMINFO, g_param_spec_pointer ("stream-info", "Stream info", "List of streaminfo", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_klass, ARG_STREAMINFO_VALUES, g_param_spec_value_array ("stream-info-value-array", "StreamInfo GValueArray", "value array of streaminfo", g_param_spec_object ("streaminfo", "StreamInfo", "Streaminfo object", GST_TYPE_STREAM_INFO, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS), G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_klass, ARG_SOURCE, g_param_spec_object ("source", "Source", "Source element", GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_klass, ARG_VIDEO, g_param_spec_int ("current-video", "Current video", "Currently playing video stream (-1 = none)", -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_klass, ARG_AUDIO, g_param_spec_int ("current-audio", "Current audio", "Currently playing audio stream (-1 = none)", -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_klass, ARG_TEXT, g_param_spec_int ("current-text", "Current text", "Currently playing text stream (-1 = none)", -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_klass, ARG_SUBTITLE_ENCODING, g_param_spec_string ("subtitle-encoding", "subtitle encoding", "Encoding to assume if input subtitles are not in UTF-8 encoding. " "If not set, the GST_SUBTITLE_ENCODING environment variable will " "be checked for an encoding to use. If that is not set either, " "ISO-8859-15 will be assumed.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * GstPlayBaseBin:connection-speed * * Network connection speed in kbps (0 = unknown) * <note><simpara> * Since version 0.10.10 in #GstPlayBin, at 0.10.15 moved to #GstPlayBaseBin * </simpara></note> * * Since: 0.10.10 */ g_object_class_install_property (gobject_klass, ARG_CONNECTION_SPEED, g_param_spec_uint ("connection-speed", "Connection Speed", "Network connection speed in kbps (0 = unknown)", 0, G_MAXUINT, DEFAULT_CONNECTION_SPEED, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); GST_DEBUG_CATEGORY_INIT (gst_play_base_bin_debug, "playbasebin", 0, "playbasebin"); gobject_klass->dispose = gst_play_base_bin_dispose; gobject_klass->finalize = gst_play_base_bin_finalize; gstbin_klass->handle_message = GST_DEBUG_FUNCPTR (gst_play_base_bin_handle_message_func); gstelement_klass->change_state = GST_DEBUG_FUNCPTR (gst_play_base_bin_change_state); }
注意:g_param_spec_types数组仅仅保存上面预定义的23个基本类型的参数,以及其对应的处理函数。
通过g_param_spec_xxx() 实例化出来的参数实例,是保持在另外一个参数池GParamSpecPool中的。参加下面内容:
在Gobject.c中,
1. 定义一个静态全局变量:
static GParamSpecPool *pspec_pool = NULL;
属性池其实就是:互斥保护+散列表
2. 申请一块内存:
static void g_object_do_class_init (GObjectClass *class) { /* read the comment about typedef struct CArray; on why not to change this quark */ quark_closure_array = g_quark_from_static_string ("GObject-closure-array"); quark_weak_refs = g_quark_from_static_string ("GObject-weak-references"); quark_toggle_refs = g_quark_from_static_string ("GObject-toggle-references"); pspec_pool = g_param_spec_pool_new (TRUE); property_notify_context.quark_notify_queue = g_quark_from_static_string ("GObject-notify-queue"); property_notify_context.dispatcher = g_object_notify_dispatcher;
3. 把参数属性,插入到属性池中,也就是放到Hash Table中:
static inline void install_property_internal (GType g_type, guint property_id, GParamSpec *pspec) { if (g_param_spec_pool_lookup (pspec_pool, pspec->name, g_type, FALSE)) { g_warning ("When installing property: type `%s' already has a property named `%s'", g_type_name (g_type), pspec->name); return; } g_param_spec_ref (pspec); g_param_spec_sink (pspec); PARAM_SPEC_SET_PARAM_ID (pspec, property_id); g_param_spec_pool_insert (pspec_pool, pspec, g_type); }
void g_param_spec_pool_insert (GParamSpecPool *pool, GParamSpec *pspec, GType owner_type) { gchar *p; if (pool && pspec && owner_type > 0 && pspec->owner_type == 0) { G_SLOCK (&pool->smutex); for (p = pspec->name; *p; p++) { if (!strchr (G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-_", *p)) { g_warning (G_STRLOC ": pspec name \"%s\" contains invalid characters", pspec->name); G_SUNLOCK (&pool->smutex); return; } } pspec->owner_type = owner_type; g_param_spec_ref (pspec); g_hash_table_insert (pool->hash_table, pspec, pspec);
4. 从属性池中查询属性:
static GParamSpecPool *pspec_pool = NULL; list = g_param_spec_pool_list_owned (pspec_pool, G_OBJECT_CLASS_TYPE (class)); g_param_spec_pool_remove (pspec_pool, pspec); pspec_pool = g_param_spec_pool_new (TRUE); if (g_param_spec_pool_lookup (pspec_pool, pspec->name, g_type, FALSE)) g_param_spec_pool_insert (pspec_pool, pspec, g_type); pspec = g_param_spec_pool_lookup (pspec_pool, pspec->name, g_type_parent (G_OBJECT_CLASS_TYPE (class)), TRUE); pspec = g_param_spec_pool_lookup (pspec_pool, pspec->name, parent_type, TRUE); pspec = g_param_spec_pool_lookup (pspec_pool, return g_param_spec_pool_lookup (pspec_pool, overridden = g_param_spec_pool_lookup (pspec_pool, overridden = g_param_spec_pool_lookup (pspec_pool, pspecs = g_param_spec_pool_list (pspec_pool, pspecs = g_param_spec_pool_list (pspec_pool, pspec = g_param_spec_pool_lookup (pspec_pool, pspecs = g_param_spec_pool_list (pspec_pool, iface_type, &n); GParamSpec *class_pspec = g_param_spec_pool_lookup (pspec_pool, GParamSpec *pspec = g_param_spec_pool_lookup (pspec_pool, GParamSpec *pspec = g_param_spec_pool_lookup (pspec_pool, pspec = g_param_spec_pool_lookup (pspec_pool, pspec = g_param_spec_pool_lookup (pspec_pool, pspec = g_param_spec_pool_lookup (pspec_pool, pspec = g_param_spec_pool_lookup (pspec_pool,
5. 从Hash Table中提取所有的属性,到链表中,
用g_param_spec_pool_list
6. 从属性池中,根据属性名字,提取一个属性:
g_param_spec_pool_lookup
安装属性,其实就是把属性放到hash table中:
void g_object_class_install_property (GObjectClass *class, guint property_id, GParamSpec *pspec)
void g_object_class_install_properties (GObjectClass *oclass, guint n_pspecs, GParamSpec **pspecs)
void g_object_interface_install_property (gpointer g_iface, GParamSpec *pspec) { GTypeInterface *iface_class = g_iface; g_return_if_fail (G_TYPE_IS_INTERFACE (iface_class->g_type)); g_return_if_fail (G_IS_PARAM_SPEC (pspec)); g_return_if_fail (!G_IS_PARAM_SPEC_OVERRIDE (pspec)); /* paranoid */ g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0); /* paranoid */ install_property_internal (iface_class->g_type, 0, pspec); }
最后都是放到那个全局变量pspec_pool中:
static inline void install_property_internal (GType g_type, guint property_id, GParamSpec *pspec) { if (g_param_spec_pool_lookup (pspec_pool, pspec->name, g_type, FALSE)) { g_warning ("When installing property: type `%s' already has a property named `%s'", g_type_name (g_type), pspec->name); return; } g_param_spec_ref (pspec); g_param_spec_sink (pspec); PARAM_SPEC_SET_PARAM_ID (pspec, property_id); g_param_spec_pool_insert (pspec_pool, pspec, g_type); }
GObject的回调,实际上是为了处理用户定义的signal/callback.
1. 定义个数据结构:
typedef struct { GObject *object; guint n_closures; GClosure *closures[1]; /* flexible array */ } CArray; //closure Array
同时定义一个标识:
static GQuark quark_closure_array = 0;
static void g_object_do_class_init (GObjectClass *class) { /* read the comment about typedef struct CArray; on why not to change this quark */ quark_closure_array = g_quark_from_static_string ("GObject-closure-array");
2. 从object的回调数组 中拿掉一个回调:
static void object_remove_closure (gpointer data, GClosure *closure) { GObject *object = data; CArray *carray; guint i; G_LOCK (closure_array_mutex); carray = g_object_get_qdata (object, quark_closure_array); for (i = 0; i < carray->n_closures; i++) if (carray->closures[i] == closure) { carray->n_closures--; if (i < carray->n_closures) carray->closures[i] = carray->closures[carray->n_closures]; G_UNLOCK (closure_array_mutex); return; } G_UNLOCK (closure_array_mutex); g_assert_not_reached (); }
3. 给Object增加一个需要监视的回调(GClosure), 放到回调数组中(Closure Array)
void g_object_watch_closure (GObject *object, GClosure *closure) { CArray *carray; guint i; g_return_if_fail (G_IS_OBJECT (object)); g_return_if_fail (closure != NULL); g_return_if_fail (closure->is_invalid == FALSE); g_return_if_fail (closure->in_marshal == FALSE); g_return_if_fail (object->ref_count > 0); /* this doesn't work on finalizing objects */ g_closure_add_invalidate_notifier (closure, object, object_remove_closure); g_closure_add_marshal_guards (closure, object, (GClosureNotify) g_object_ref, object, (GClosureNotify) g_object_unref); G_LOCK (closure_array_mutex); carray = g_datalist_id_remove_no_notify (&object->qdata, quark_closure_array); if (!carray) { carray = g_renew (CArray, NULL, 1); carray->object = object; carray->n_closures = 1; i = 0; } else { i = carray->n_closures++; carray = g_realloc (carray, sizeof (*carray) + sizeof (carray->closures[0]) * i); } carray->closures[i] = closure; g_datalist_id_set_data_full (&object->qdata, quark_closure_array, carray, destroy_closure_array); G_UNLOCK (closure_array_mutex); }
4. 创建一个新的回调:注册user callback, 并且绑定Object, 同时加入Object的 Callback Array中:
GClosure* g_cclosure_new_object (GCallback callback_func, GObject *object) { GClosure *closure; g_return_val_if_fail (G_IS_OBJECT (object), NULL); g_return_val_if_fail (object->ref_count > 0, NULL); /* this doesn't work on finalizing objects */ g_return_val_if_fail (callback_func != NULL, NULL); closure = g_cclosure_new (callback_func, object, NULL); g_object_watch_closure (object, closure); return closure; }
5. 把signal和信号回调联系在一起
这时候,把signal和object的callback array联系在一起了:
gulong g_signal_connect_object (gpointer instance, const gchar *detailed_signal, GCallback c_handler, gpointer gobject, GConnectFlags connect_flags) { g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), 0); g_return_val_if_fail (detailed_signal != NULL, 0); g_return_val_if_fail (c_handler != NULL, 0); if (gobject) { GClosure *closure; g_return_val_if_fail (G_IS_OBJECT (gobject), 0); closure = ((connect_flags & G_CONNECT_SWAPPED) ? g_cclosure_new_object_swap : g_cclosure_new_object) (c_handler, gobject); return g_signal_connect_closure (instance, detailed_signal, closure, connect_flags & G_CONNECT_AFTER); } else return g_signal_connect_data (instance, detailed_signal, c_handler, NULL, NULL, connect_flags); }
6. 进一步的封装:可以链接一串信号
gpointer g_object_connect (gpointer _object, const gchar *signal_spec, ...) { GObject *object = _object; va_list var_args; g_return_val_if_fail (G_IS_OBJECT (object), NULL); g_return_val_if_fail (object->ref_count > 0, object); va_start (var_args, signal_spec); while (signal_spec) { GCallback callback = va_arg (var_args, GCallback); gpointer data = va_arg (var_args, gpointer); gulong sid; if (strncmp (signal_spec, "signal::", 8) == 0) sid = g_signal_connect_data (object, signal_spec + 8, callback, data, NULL, 0); else if (strncmp (signal_spec, "object_signal::", 15) == 0 || strncmp (signal_spec, "object-signal::", 15) == 0) sid = g_signal_connect_object (object, signal_spec + 15, callback, data, 0); else if (strncmp (signal_spec, "swapped_signal::", 16) == 0 || strncmp (signal_spec, "swapped-signal::", 16) == 0) sid = g_signal_connect_data (object, signal_spec + 16, callback, data, NULL, G_CONNECT_SWAPPED); else if (strncmp (signal_spec, "swapped_object_signal::", 23) == 0 || strncmp (signal_spec, "swapped-object-signal::", 23) == 0) sid = g_signal_connect_object (object, signal_spec + 23, callback, data, G_CONNECT_SWAPPED); else if (strncmp (signal_spec, "signal_after::", 14) == 0 || strncmp (signal_spec, "signal-after::", 14) == 0) sid = g_signal_connect_data (object, signal_spec + 14, callback, data, NULL, G_CONNECT_AFTER); else if (strncmp (signal_spec, "object_signal_after::", 21) == 0 || strncmp (signal_spec, "object-signal-after::", 21) == 0) sid = g_signal_connect_object (object, signal_spec + 21, callback, data, G_CONNECT_AFTER); else if (strncmp (signal_spec, "swapped_signal_after::", 22) == 0 || strncmp (signal_spec, "swapped-signal-after::", 22) == 0) sid = g_signal_connect_data (object, signal_spec + 22, callback, data, NULL, G_CONNECT_SWAPPED | G_CONNECT_AFTER); else if (strncmp (signal_spec, "swapped_object_signal_after::", 29) == 0 || strncmp (signal_spec, "swapped-object-signal-after::", 29) == 0) sid = g_signal_connect_object (object, signal_spec + 29, callback, data, G_CONNECT_SWAPPED | G_CONNECT_AFTER); else { g_warning ("%s: invalid signal spec \"%s\"", G_STRFUNC, signal_spec); break; } signal_spec = va_arg (var_args, gchar*); } va_end (var_args); return object; }
7. 例子:比如gstbin:
/* use the sync signal handler to link elements while the pipeline is still * doing the state change */ gst_bus_set_sync_handler (bus, gst_bus_sync_signal_handler, pipeline); g_object_connect (bus, "signal::sync-message::state-changed", G_CALLBACK (test_link_structure_change_state_changed_sync_cb), pipeline, NULL);
8. 再如:gtk test中:
button = g_object_new (gtk_button_get_type (), "label", "push something", "visible", TRUE, "parent", box2, NULL); g_object_connect (button, "signal::clicked", statusbar_push, statusbar, NULL); button = g_object_connect (g_object_new (gtk_button_get_type (), "label", "pop", "visible", TRUE, "parent", box2, NULL), "signal_after::clicked", statusbar_pop, statusbar, NULL); button = g_object_connect (g_object_new (gtk_button_get_type (), "label", "steal #4", "visible", TRUE, "parent", box2, NULL), "signal_after::clicked", statusbar_steal, statusbar, NULL); button = g_object_connect (g_object_new (gtk_button_get_type (), "label", "test contexts", "visible", TRUE, "parent", box2, NULL), "swapped_signal_after::clicked", statusbar_contexts, statusbar, NULL); button = g_object_connect (g_object_new (gtk_button_get_type (), "label", "push something long", "visible", TRUE, "parent", box2, NULL), "signal_after::clicked", statusbar_push_long, statusbar, NULL);
1. 定义一个双指针
双指针,类似于指针数组;由于数组大小未知,定义为双指针。
/* --- signal nodes --- */ static guint g_n_signal_nodes = 0; static SignalNode **g_signal_nodes = NULL;
如果知道了g_signal_nodes的大小,
可以像TypeNode那样定义:
static TypeNode *static_fundamental_type_nodes[(G_TYPE_FUNDAMENTAL_MAX >> G_TYPE_FUNDAMENTAL_SHIFT) + 1] = { NULL, };
即:*static_fundamental_type_nodes[256]
假设信号最多有128个,我们可以这样定义:
static SignalNode *g_signal_nodes[128 + 1] = { NULL, }
2. 初始化
void g_signal_init (void) { SIGNAL_LOCK (); if (!g_n_signal_nodes) { /* setup handler list binary searchable array hash table (in german, that'd be one word ;) */ g_handler_list_bsa_ht = g_hash_table_new (g_direct_hash, NULL); g_signal_key_bsa = g_bsearch_array_create (&g_signal_key_bconfig); /* invalid (0) signal_id */ g_n_signal_nodes = 1; g_signal_nodes = g_renew (SignalNode*, g_signal_nodes, g_n_signal_nodes); g_signal_nodes[0] = NULL; } SIGNAL_UNLOCK (); }
3. 创建信号时:
吧新创建的SignalNode放入指针数组g_signal_nodes[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) { gchar *name; guint signal_id, i; SignalNode *node; /* setup permanent portion of signal node */ //////////////////////////// //.................... //////////////////////////// if (!node) { SignalKey key; signal_id = g_n_signal_nodes++; node = g_new (SignalNode, 1); node->signal_id = signal_id; g_signal_nodes = g_renew (SignalNode*, g_signal_nodes, g_n_signal_nodes); g_signal_nodes[signal_id] = node; node->itype = itype; node->name = name; key.itype = itype; key.quark = g_quark_from_string (node->name); key.signal_id = signal_id; g_signal_key_bsa = g_bsearch_array_insert (g_signal_key_bsa, &g_signal_key_bconfig, &key); g_strdelimit (name, "_", '-'); node->name = g_intern_string (name); key.quark = g_quark_from_string (name); g_signal_key_bsa = g_bsearch_array_insert (g_signal_key_bsa, &g_signal_key_bconfig, &key); TRACE(GOBJECT_SIGNAL_NEW(signal_id, name, itype)); } node->destroyed = FALSE; node->test_class_offset = 0;
4. 查询SignalNode时,直接访问指针数组:
static inline SignalNode* LOOKUP_SIGNAL_NODE (register guint signal_id) { if (signal_id < g_n_signal_nodes) return g_signal_nodes[signal_id]; else return NULL; }
上面定义的数组g_signal_nodes[signal_id]; 在知道signal_id已知的情况下,可以直接访问数组,提取SignalNode*,
但是,很多情况下,我们不知道signal_id, 而知道signal_name.
如果通过signal_name如何查询与之对应的signal id等信息呢?
1 在前面New Signal时,我们就看到:为了方便查询,留了后手:
key.itype = itype; key.quark = g_quark_from_string (node->name); key.signal_id = signal_id; g_signal_key_bsa = g_bsearch_array_insert (g_signal_key_bsa, &g_signal_key_bconfig, &key); g_strdelimit (name, "_", '-'); node->name = g_intern_string (name); key.quark = g_quark_from_string (name); g_signal_key_bsa = g_bsearch_array_insert (g_signal_key_bsa, &g_signal_key_bconfig, &key);
把SignalNode的3个信息:
Node type, node name(-->quark), node signal id, 三位一体的几个简单信息,保存在SignalKey结构变量中,
然后放到一个数组中g_signal_key_bsa;
GBSearchArray是glib提供的一个二分查找数组,可以更快的使用“name"等查到与之对应的signal id.
常用的函数:
2 根据quark/type来查找:
/* --- functions --- */ static inline guint signal_id_lookup (GQuark quark, GType itype) { GType *ifaces, type = itype; SignalKey key; guint n_ifaces; key.quark = quark; /* try looking up signals for this type and its ancestors */ do { SignalKey *signal_key; key.itype = type; signal_key = g_bsearch_array_lookup (g_signal_key_bsa, &g_signal_key_bconfig, &key); if (signal_key) return signal_key->signal_id; type = g_type_parent (type); } while (type); /* no luck, try interfaces it exports */ ifaces = g_type_interfaces (itype, &n_ifaces); while (n_ifaces--) { SignalKey *signal_key; key.itype = ifaces[n_ifaces]; signal_key = g_bsearch_array_lookup (g_signal_key_bsa, &g_signal_key_bconfig, &key); if (signal_key) { g_free (ifaces); return signal_key->signal_id; } } g_free (ifaces); return 0; }
3 再封装一次,提供给外界使用:
guint g_signal_lookup (const gchar *name, GType itype) { guint signal_id; g_return_val_if_fail (name != NULL, 0); g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (itype) || G_TYPE_IS_INTERFACE (itype), 0); SIGNAL_LOCK (); signal_id = signal_id_lookup (g_quark_try_string (name), itype); SIGNAL_UNLOCK (); if (!signal_id) { /* give elaborate warnings */ if (!g_type_name (itype)) g_warning (G_STRLOC ": unable to lookup signal \"%s\" for invalid type id `%"G_GSIZE_FORMAT"'", name, itype); else if (!G_TYPE_IS_INSTANTIATABLE (itype)) g_warning (G_STRLOC ": unable to lookup signal \"%s\" for non instantiatable type `%s'", name, g_type_name (itype)); else if (!g_type_class_peek (itype)) g_warning (G_STRLOC ": unable to lookup signal \"%s\" of unloaded type `%s'", name, g_type_name (itype)); } return signal_id; }
4 第三方库使用:
如gst-inspect:
static void print_signal_info (GstElement * element) { /* Signals/Actions Block */ guint *signals; guint nsignals; gint i = 0, j, k; GSignalQuery *query = NULL; GType type; GSList *found_signals, *l; for (k = 0; k < 2; k++) { found_signals = NULL; /* For elements that have sometimes pads, also list a few useful GstElement * signals. Put these first, so element-specific ones come later. */ if (k == 0 && has_sometimes_template (element)) { query = g_new0 (GSignalQuery, 1); g_signal_query (g_signal_lookup ("pad-added", GST_TYPE_ELEMENT), query); found_signals = g_slist_append (found_signals, query); query = g_new0 (GSignalQuery, 1); g_signal_query (g_signal_lookup ("pad-removed", GST_TYPE_ELEMENT), query); found_signals = g_slist_append (found_signals, query); query = g_new0 (GSignalQuery, 1); g_signal_query (g_signal_lookup ("no-more-pads", GST_TYPE_ELEMENT), query); found_signals = g_slist_append (found_signals, query); }
这里的处理有点意思,也不知道到底是否高效:
1). handler -->放入handler list, 这没有问题
2). hander list作为key 放入handler_list_bsa (二分查找数组),可以很快查询?
3). handler_list_bsa 有装入了Hash Table中:handler_list_bsa_ht
1. 看看New hander_list_bsa_ht
在g_signal_init()时,先创建一个hash tabe:
void g_signal_init (void) { SIGNAL_LOCK (); if (!g_n_signal_nodes) { /* setup handler list binary searchable array hash table (in german, that'd be one word ;) */ g_handler_list_bsa_ht = g_hash_table_new (g_direct_hash, NULL);
这个hash table何时用?如何用?
2. New 一个Handler
static inline Handler* handler_new (gboolean after) { Handler *handler = g_slice_new (Handler); #ifndef G_DISABLE_CHECKS if (g_handler_sequential_number < 1) g_error (G_STRLOC ": handler id overflow, %s", REPORT_BUG); #endif handler->sequential_number = g_handler_sequential_number++; handler->prev = NULL; handler->next = NULL; handler->detail = 0; handler->ref_count = 1; handler->block_count = 0; handler->after = after != FALSE; handler->closure = NULL; return handler; }
3. New 的Handler一般会放到Handler list中 (各种插入)
static void handler_insert (guint signal_id, gpointer instance, Handler *handler) { HandlerList *hlist; g_assert (handler->prev == NULL && handler->next == NULL); /* paranoid */ hlist = handler_list_ensure (signal_id, instance); if (!hlist->handlers) { hlist->handlers = handler; if (!handler->after) hlist->tail_before = handler; } else if (handler->after) { handler->prev = hlist->tail_after; hlist->tail_after->next = handler; } else { if (hlist->tail_before) { handler->next = hlist->tail_before->next; if (handler->next) handler->next->prev = handler; handler->prev = hlist->tail_before; hlist->tail_before->next = handler; } else /* insert !after handler into a list of only after handlers */ { handler->next = hlist->handlers; if (handler->next) handler->next->prev = handler; hlist->handlers = handler; } hlist->tail_before = handler; } if (!handler->next) hlist->tail_after = handler; }
这里就有一个重要的函数:handler_list_ensure
它做如下动作:
1)首先查询一下hash table: handler_list_bsa_ht, 里面有没有instance匹配的handler_list_bsa,
if 没有
(1)则创建一个handler_list_bsa二分查找数组,并且把handler list的signal_id作为关键Key,
(2) 把handler list放入到这个二分数组中;
(3)然后把这个二分数组handler_list_bsa, 插入到hash table中,此时instance作为key
else 已经存在和instance对应的handler_list_bsa二分数组
(1) 把handler list直接插入到二分数组中
(2)二分数组,可能在插入到hash table中
具体参考代码:
static inline HandlerList* handler_list_ensure (guint signal_id, gpointer instance) { GBSearchArray *hlbsa = g_hash_table_lookup (g_handler_list_bsa_ht, instance); HandlerList key; key.signal_id = signal_id; key.handlers = NULL; key.tail_before = NULL; key.tail_after = NULL; if (!hlbsa) { hlbsa = g_bsearch_array_create (&g_signal_hlbsa_bconfig); hlbsa = g_bsearch_array_insert (hlbsa, &g_signal_hlbsa_bconfig, &key); g_hash_table_insert (g_handler_list_bsa_ht, instance, hlbsa); } else { GBSearchArray *o = hlbsa; hlbsa = g_bsearch_array_insert (o, &g_signal_hlbsa_bconfig, &key); if (hlbsa != o) g_hash_table_insert (g_handler_list_bsa_ht, instance, hlbsa); } return g_bsearch_array_lookup (hlbsa, &g_signal_hlbsa_bconfig, &key); }
上面函数执行的是各种插入操作。
4 下面看看查询过程:
1)用instanc作为key, 在hash table中,找到handler_list_bsa数组
2)从binary searchable array中提取handler list
3) 遍历list, 找到与handler_id对应的handler
static inline HandlerList* handler_list_lookup (guint signal_id, gpointer instance) { GBSearchArray *hlbsa = g_hash_table_lookup (g_handler_list_bsa_ht, instance); HandlerList key; key.signal_id = signal_id; return hlbsa ? g_bsearch_array_lookup (hlbsa, &g_signal_hlbsa_bconfig, &key) : NULL; } static Handler* handler_lookup (gpointer instance, gulong handler_id, guint *signal_id_p) { GBSearchArray *hlbsa = g_hash_table_lookup (g_handler_list_bsa_ht, instance); if (hlbsa) { guint i; for (i = 0; i < hlbsa->n_nodes; i++) { HandlerList *hlist = g_bsearch_array_get_nth (hlbsa, &g_signal_hlbsa_bconfig, i); Handler *handler; for (handler = hlist->handlers; handler; handler = handler->next) if (handler->sequential_number == handler_id) { if (signal_id_p) *signal_id_p = hlist->signal_id; return handler; } } } return NULL; }
5 handler_new和外界的接口:
一般在外面调用g_signal_connect_xxx()是调用:
比如:
gulong g_signal_connect_data (gpointer instance, const gchar *detailed_signal, GCallback c_handler, gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags)
gulong g_signal_connect_closure (gpointer instance, const gchar *detailed_signal, GClosure *closure, gboolean after)
gulong g_signal_connect_closure_by_id (gpointer instance, guint signal_id, GQuark detail, GClosure *closure, gboolean after)
就是说,当Caller用上面3个函数时,就会给当前的instance/signal_id,
1. 注册好callback
2. 绑定closure
3. 生成handler (handler 绑定callback/closure)
4. 放入handler list
5. 放入handler list的二分查找array中
6. 插入到全局的hash table中。
之后的查询函数:
handler_lookup/handler_list_lookup/handlers_find, 从会根据instance/signal_id,来找到与之对应的handlers
1. 先定义:这其实是个链表,为啥定义2个链表呢?
static Emission *g_recursive_emissions = NULL;
static Emission *g_restart_emissions = NULL;
2. 再定义3个内联函数:
把临时emission, 放入全局的list中:push
查找:find
弹出临时emission: pop
static inline void emission_push (Emission **emission_list_p, Emission *emission); static inline void emission_pop (Emission **emission_list_p, Emission *emission); static inline Emission* emission_find (Emission *emission_list, guint signal_id, GQuark detail, gpointer instance);