1 GInitiallyUnowned基本概念

一个用于具有初始浮动引用的对象的类型。该对象继承于GObject,除了实例初始化的时候把该对象初始位浮点引用外,其余都和GObject对象一样。

  • 我们在GTK库或者其他库的时候,使用继承 GInitiallyUnowned 的GTK对象,处于浮点状态的时候,不能直接解引用。

  • 浮点引用状态调用 g_object_ref_sink ,浮点引用状态变成正常引用状态,引用数不变。

  • 正常引用状态调用 g_object_ref_sink ,引用数 +1

2 GInitiallyUnowned结构体与定义

通过查看 gobject 源文件和头文件,不难发现 GInitiallyUnownedGObject 类结构体和实例结构体完全相同,唯一的区别就是 GInitiallyUnowned 对象初始化的时候调用了强制浮点引用函数。

/* filename: gobject.h */

typedef struct _GObject                  GInitiallyUnowned;
typedef struct _GObjectClass             GInitiallyUnownedClass;

/* filename: gobject.c */

G_DEFINE_TYPE (GInitiallyUnowned, g_initially_unowned, G_TYPE_OBJECT)

static void
g_initially_unowned_init (GInitiallyUnowned *object)
{
  g_object_force_floating (object);
}

static void
g_initially_unowned_class_init (GInitiallyUnownedClass *klass)
{
}

3 GInitiallyUnowned浮点引用

强制浮点引用函数操作的是实例结构体中的 GData *qdata 成员变量,该变量应该是被用来存储GData数据,也就是用户需要挂载自定义关联数据到该对象。虽然进行其他操作的时候应该都会先把浮点引用变成正常引用,但是我觉得这样还是有安全风险。

其实并不会出现这样的问题,是我肤浅了。

object_floating_flag_handler 实际处理浮点引用标记位的函数。

/* filename: gobject.c */

static guint (*floating_flag_handler) (GObject*, gint) = object_floating_flag_handler;

void
g_object_force_floating (GObject *object)
{
  g_return_if_fail (G_IS_OBJECT (object));
  g_return_if_fail (g_atomic_int_get (&object->ref_count) >= 1);

  floating_flag_handler (object, +1);
}

/**
 * @brief: 标记浮点引用的关键函数
 *         其实就是操作 object->qdata 的内容,标记 object->qdata | 0x02 就是标记了浮点引用
*/
static guint
object_floating_flag_handler (GObject        *object,
                              gint            job)
{
  switch (job)
    {
      gpointer oldvalue;
    case +1:    /* force floating if possible */
      do
        oldvalue = g_atomic_pointer_get (&object->qdata);
      while (!g_atomic_pointer_compare_and_exchange ((void**) &object->qdata, oldvalue,
                                                     (gpointer) ((gsize) oldvalue | OBJECT_FLOATING_FLAG)));
      return (gsize) oldvalue & OBJECT_FLOATING_FLAG;
    case -1:    /* sink if possible */
      do
        oldvalue = g_atomic_pointer_get (&object->qdata);
      while (!g_atomic_pointer_compare_and_exchange ((void**) &object->qdata, oldvalue,
                                                     (gpointer) ((gsize) oldvalue & ~(gsize) OBJECT_FLOATING_FLAG)));
      return (gsize) oldvalue & OBJECT_FLOATING_FLAG;
    default:    /* check floating */
      return 0 != ((gsize) g_atomic_pointer_get (&object->qdata) & OBJECT_FLOATING_FLAG);
    }
}

3.1 如何让指针变量既能存储地址又能做Flag

通过测试程序 /assets/GObjectStudy/202311/14_GInitiallyUnowned/GInitiallyUnowned.c 不难发现,我们创建浮点引用对象后,直接设定qdata,然后浮点引用变成正常,从对象取出qdata。完全没有任何问题。

我们设定qdata和获取qdata指针的时候,经过 G_DATALIST_GET_POINTERG_DATALIST_SET_POINTER 宏。可以获取包含Flag的指针地址(此时地址不是真正的地址,不能直接使用)。

/* filename: gdataset.c */

#define G_DATALIST_FLAGS_MASK_INTERNAL 0x7

/* datalist pointer accesses have to be carried out atomically */
#define G_DATALIST_GET_POINTER(datalist)						\
  ((GData*) ((gsize) g_atomic_pointer_get (datalist) & ~(gsize) G_DATALIST_FLAGS_MASK_INTERNAL))

#define G_DATALIST_SET_POINTER(datalist, pointer)       G_STMT_START {                  \
  gpointer _oldv, _newv;                                                                \
  do {                                                                                  \
    _oldv = g_atomic_pointer_get (datalist);                                            \
    _newv = (gpointer) (((gsize) _oldv & G_DATALIST_FLAGS_MASK_INTERNAL) | (gsize) pointer);     \
  } while (!g_atomic_pointer_compare_and_exchange ((void**) datalist, _oldv, _newv));   \
} G_STMT_END