gobject运行时重要的数据结构。

os posted @ 2013年8月10日 18:26 in GObject , 3317 阅读

 

 数据定义

 说明

gtype

static TypeNode                               *static_fundamental_type_nodes[256]

一个全局指针数组, 用来保存基本类型;

通过基本类型顺藤摸瓜,可以找到其子类型

static GHashTable       *static_type_nodes_ht = NULL;

一个全局的Hash Table

这个Hash Table一般是通过“名字”来查询TypeNode时使用

 

 

 

Signal

static SignalNode   **g_signal_nodes = NULL;

双指针 == 指针数组?

  static GBSearchArray *g_signal_key_bsa = NULL;  方便根据signal name/type来查询signal_id

 

static GHashTable    *g_handler_list_bsa_ht = NULL;

信号处理函数用hash table

 

static Emission      *g_recursive_emissions = NULL;

 

 

static Emission      *g_restart_emissions = NULL;

 

 

object

static GQuark                 quark_closure_array = 0;

 这个和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

static GParamSpecPool *pspec_pool = NULL;

 用Hash Table来保存Object的ParamSpec

 

/* --- type initialization --- */

GType *g_param_spec_types = NULL;

一个全局数组

Gtype.c中

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;
}

 

GParamSpec.c:

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的属性如何保持的?

在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

 

Gobject的属性如何install上去的:

安装属性,其实就是把属性放到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的属性被被改变时,如何响应的:

 

 

GObject的回调数组

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);

 

GSignal的SignalNode用指针数组保存:

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;
}

 

Signal的二分查找数组:

上面定义的数组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);
    }

 

handler的处理:

这里的处理有点意思,也不知道到底是否高效:

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);

 

gobjec相关学习文章的list.

 

 

 

 

 

 

 

 

 

 

 

 

 

 


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter