信号选择器

XWOS的信号选择器

概述

信号选择器类似于事件标志,使用位图来管理一组 同步对象 。使得单一线程可以同时等待多个 同步对象

每个 同步对象 在信号选择器位图中都绑定一个特定的

当这些 同步对象 发送 选择信号 时,信号选择器位图中特定的 被置 1 ,同时唤醒正在等待信号选择器的线程。 线程唤醒后可以通过检测哪些 被置 1 来判断哪些 同步对象 发送了 选择信号

信号选择器对象与对象描述符描述符

信号选择器对象是 XWOS对象 struct xwos_object 的派生类 。 类似的,信号选择器对象也用 信号选择器对象描述符 xwos_sel_d 来解决有效性和身份合法性的问题。

信号选择器对象描述符由 信号选择器对象的指针标签 组成:

typedef struct {
        struct xwos_sel * sel; /**< 信号选择器对象的指针 */
        xwsq_t tik; /**< 标签 */
} xwos_sel_d;

通过对象描述符引用对象时,首先检测 obj->magic 的值,是否为 0x58574F53U ,由此可确定指针 obj 指向一个有效的 XWOS的对象 。 然后对比标签 obj->tiktik 是否相等,由此可以确定对象的 身份 。 因为对象的 tik 是全局唯一的,当对象被释放后,它的 tik 会被析构函数析构为 0 。 当内存地址被重新构建为新的对象,那么它的 tik 一定与对象描述符的 tik 不一致。

同步对象的绑定与解绑

XWOS中所有 同步对象 都带有两个相似的CAPI:

同步对象 绑定操作又分为 独占绑定非独占绑定

  • 独占绑定:是指 同步对象 一旦绑定了信号选择器位图中的某一位后,其他 同步对象 不可再绑定此位,发送的 选择信号 也被称为 独占 方式的 选择信号
  • 非独占绑定:是指 同步对象 一旦绑定了信号选择器位图中的某一位后,其他 同步对象 还可继续再绑定此位,发送的 选择信号 也被称为 非独占 方式的 选择信号

同步对象 采用的绑定方式:

  • 信号量:独占绑定
  • 条件量:非独占绑定
  • 事件标志:非独占绑定
  • 线程栅栏:非独占绑定
  • 信号选择器:非独占绑定

选择信号

选择信号的设置

选择信号 的设置,对于不同的 同步对象 有不同解释:

  • 信号量
    • 计数器的值大于 0 时就会被设置。
  • 条件量
  • 事件标志
  • 线程栅栏
    • 所有线程抵达栅栏处,并同时被唤醒;
  • 信号选择器
    • 信号选择器本身也是 同步对象 ,也可绑定在另一个信号选择器上。当源信号选择器收到了 选择信号 ,会将其传递到绑定的另一个目的信号选择器上。

XWOS信号选择器示意图
Photo: xwos.tech / CC-BY

选择信号的清除

独占 方式的 选择信号

  • 信号量:当信号量中的计数器的值小于等于 0 时, 选择信号 才会被清除。

非独占 方式的 选择信号

非独占 方式绑定的 同步对象 向信号选择器发送 选择信号 后,其位图中的位置会被置 1 。 同时会唤醒所有等待的线程,此时线程们会竞争进入信号选择器的临界区。

最先进入的线程会读取信号选择器的 选择信号 位图,并与调用函数时传递的 掩码 进行比较,判断是否有 掩码 中的 选择信号

  • 如果有,会清除信号选择器位图中 所有非独占 方式的 选择信号 ,包括 掩码 中没有设置的 选择信号 。 因此后续线程将无法再检测到任何 非独占 方式的 选择信号 ,会重新阻塞等待。 XWOS不推荐在信号选择器上,多于一个线程等待。
  • 如果没有,线程会重新阻塞等待,然后下一个线程进入临界区检测。

信号选择器的初始化、销毁与动态创建、删除

静态初始化、销毁

  • 静态初始化: xwos_sel_init()
    • 静态 是指用户预先定义线程结构体对象,这些对象在编译期由编译器分配内存。
  • 销毁静态初始化的信号选择器: xwos_sel_fini()

动态创建、删除

  • 动态创建: xwos_sel_create()
    • 动态 是指程序在运行时,通过内存分配函数申请内存,并在申请的内存上构造对象。
  • 删除动态创建的信号选择器: xwos_sel_delete()

等待选择信号

  • xwos_sel_select() :等待信号选择器中的 选择信号 ,只能在 线程 上下文使用
  • xwos_sel_select_to() :限时等待信号选择器中的 选择信号 ,只能在 线程 上下文使用
  • xwos_sel_tryselect() :检测信号选择器中是否有 选择信号 ,可在 任意 上下文使用

绑定与解绑其他信号选择器

可以通过 xwos_sel_bind() 将信号选择器绑定到另一个 信号选择器 上,形成传递链。 但信号选择器不可相互绑定形成循环链,否则会造成无限传递。

绑定后的信号选择器可以通过 xwos_sel_unbind() 解绑。

信号选择器对象的生命周期管理

信号选择器对象的基类是 XWOS对象 struct xwos_object 。 信号选择器对象也有两组生命周期管理的CAPI:

  • 使用 对象指针 访问生命周期管理的CAPI:需要确保调用CAPI时,对象一定是有效的,且不存在 释放-又被申请 为另一个对象的情况。

    • xwos_sel_grab() :增加引用计数。
    • xwos_sel_put() :减少引用计数,当引用计数减少为 0 时,调用垃圾回收函数释放对象。
  • 使用 对象描述符 访问生命周期管理的CAPI:用户无法确保对象一定有效或无法确保对象不会变成另一个对象时使用。

    • xwos_sel_acquire() :通过对象描述符确定对象有效且合法,再增加引用计数。
    • xwos_sel_release() :通过对象描述符确定对象有效且合法,再减少引用计数。 当引用计数减少为 0 时,调用垃圾回收函数释放对象。

CAPI参考