条件量

XWOS的条件量

概述

条件量是操作系统比较底层的同步机制,可以同时阻塞多个线程。当条件成立,条件量可以唤醒一个或所有正在等待的线程。

操作系统或语言库都提供了条件量的功能,例如:

  • POSIX的 pthread_cond_t
  • C++的 std::condition_variable
  • Java的 java.util.concurrent.locks.Condition
  • Python的 threading.Condition
  • Rust的 std::sync::condvar

线程需要在持有 互斥锁 的情况下去等待条件量, 条件量阻塞线程时会同步释放 互斥锁 。当条件成立,线程被唤醒时,条件量会自动上锁 互斥锁 。 等待条件量发生错误时,条件量也会自动上锁 互斥锁 后再返回。

XWOS的条件量的功能类似,主要包括以下操作:

  • 线程 A 等待条件量的 条件成立 而阻塞;
  • 另一个线程 B 或中断上下文或其他上下文通过 单播广播 使 条件成立 ,并唤醒条件量上阻塞的线程 A
  • 线程 A 持有 的情况下去等待条件量,会自动解锁;
  • 线程 A 持有 的情况下去等待条件量,当等到 条件成立 时,会自动上锁;
  • 线程 A 持有 的情况下去等待条件量,当发生错误返回时, 不会 自动上锁;
  • 支持多种锁类型:
    • 互斥锁
    • 自旋锁
    • 顺序锁
    • 自定义的加锁与解锁函数
  • 支持没有伴生锁的情况下操作。

条件量对象与对象描述符描述符

条件量对象是 XWOS对象 struct xwos_object 的派生类 。 类似的,条件量对象也用 条件量对象描述符 xwos_cond_d 来解决有效性和身份合法性的问题。

条件量对象描述符由 条件量对象的指针标签 组成:

typedef struct {
        struct xwos_cond * cond; /**< 条件量对象的指针 */
        xwsq_t tik; /**< 标签 */
} xwos_cond_d;

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

条件量的初始化、销毁与动态创建、删除

静态初始化、销毁

  • 静态初始化: xwos_cond_init()
    • 静态 是指用户预先定义线程结构体对象,这些对象在编译期由编译器分配内存。
  • 销毁静态初始化的条件量: xwos_cond_fini()

动态创建、删除

  • 动态创建: xwos_cond_create()
    • 动态 是指程序在运行时,通过内存分配函数申请内存,并在申请的内存上构造对象。
  • 删除动态创建的条件量: xwos_cond_delete()

单播

xwos_cond_unicast() 可用来在 任意 上下文使得条件量的条件成立,但只唤醒一个线程。 单播不会产生 选者信号

广播

xwos_cond_broadcast() 可用来在 任意 上下文使得条件量的条件成立,唤醒全部线程。 广播 还会使得条件量向绑定的 信号选择器 发送 选择信号

等待条件量

当使用了带附作用的上锁函数时,例如:

  • xwos_splk_lock_cpuirq()
  • xwos_splk_lock_cpuirqsv()
  • xwos_splk_lock_irqs()
  • xwos_splk_lock_bh()
  • xwos_sqlk_wr_lock_cpuirq()
  • xwos_sqlk_wr_lock_cpuirqsv()
  • xwos_sqlk_wr_lock_irqs()
  • xwos_sqlk_wr_lock_bh()
  • xwos_sqlk_rdex_lock_cpuirq()
  • xwos_sqlk_rdex_lock_cpuirqsv()
  • xwos_sqlk_rdex_lock_irqs()
  • xwos_sqlk_rdex_lock_bh()

等待条件量 不会管理调度器开关、中断开关以及中断底半部开关。 等待之前是什么状态,无论返回值是 XWOK 还是错误码,等待之后还是什么状态。

XWOS的条件量与 pthread_cond_t 不同:

  • XWOS的条件量, 等待条件量 返回值为 XWOK 时才会对锁进行上锁,如果返回错误码,是否上锁不确定。
  • pthread_cond_wait() 无论如何都会等待互斥锁被上锁时才返回。

冻结与解冻

冻结

条件量可以使用 xwos_cond_freeze() 进行 冻结, 被冻结的条件量不能被 单播广播 ,但不影响 等待 操作。

解冻

通过 xwos_cond_thaw() 可将已经冻结的条件量 解冻 。 条件量 解冻 后,可重新 单播广播

绑定与解绑信号选择器

可以通过 xwos_cond_bind() 将条件量绑定到 信号选择器 上。 当 广播 条件量时,条件量会向 信号选择器 发送一个 选择信号 。此时 信号选择器 会唤醒正在等待的线程。此外, 单播 不会产生 选者信号

绑定后的条件量可以通过 xwos_cond_unbind() 解绑。

条件量对象的生命周期管理

条件量对象的基类是 XWOS对象 struct xwos_object 。 条件量对象也有两组生命周期管理的CAPI:

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

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

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

API参考