Repository navigation
thread semaphore
ceragon edited this page Mar 29, 2022
·
4 revisions
class Semaphore : public CHeapObj<mtSynchronizer> {
SemaphoreImpl _impl;
};class PosixSemaphore : public CHeapObj<mtInternal> {
sem_t _semaphore;
};
typedef PosixSemaphore SemaphoreImpl;define __SIZEOF_SEM_T 32
typedef union
{
char __size[__SIZEOF_SEM_T];
long int __align;
} sem_t;struct new_sem
{
uint64_t data;
int private;
int pad;
};PosixSemaphore::PosixSemaphore(uint value) {
int ret = sem_init(&_semaphore, 0, value);
}int __new_sem_init (sem_t *sem, int pshared, unsigned int value) {
struct new_sem *isem = (struct new_sem *) sem;
isem->data = value;
isem->private = (pshared == PTHREAD_PROCESS_PRIVATE
? FUTEX_PRIVATE : FUTEX_SHARED);
}
versioned_symbol (libc, __new_sem_init, sem_init, GLIBC_2_34);void PosixSemaphore::signal(uint count) {
for (uint i = 0; i < count; i++) {
int ret = sem_post(&_semaphore);
}
}int __new_sem_post (sem_t *sem) {
struct new_sem *isem = (struct new_sem *) sem;
int private = isem->private;
// uint64_t d = atomic_load_relaxed (&isem->data);
// __atomic_load_n + __ATOMIC_RELAXED,告诉编译器强制从内存读取 &isem->data
uint64_t d = __atomic_load_n((&isem->data), __ATOMIC_RELAXED);
do
{
// 当前信号量超过了最大上限
if ((d & SEM_VALUE_MASK) == SEM_VALUE_MAX)
{
__set_errno (EOVERFLOW);
return -1;
}
} // 期望是 d这个值,然后与 d + 1 交换,d 会被修改为旧值
// while (!atomic_compare_exchange_weak_release (&isem->data, &d, d + 1));
while (!__atomic_compare_exchange_n((&isem->data), (&d), (d + 1), 1, __ATOMIC_RELEASE, __ATOMIC_RELAXED));
// SEM_NWAITERS_SHIFT = 32,右移 32位。 如果 +1 成功,则判断高32 有没有值
// 如果条件成立,就会进入内核态去唤醒等待中的线程
if ((d >> SEM_NWAITERS_SHIFT) > 0) {
// 高32位有值,说明有人在等待,唤醒队列
// 小端机器,SEM_VALUE_OFFSET = 0
futex_wake (((unsigned int *) &isem->data) + SEM_VALUE_OFFSET, 1, private);
}
return 0;
}static __always_inline void futex_wake (unsigned int* futex_word, int processes_to_wake, int private)
{
// int res = lll_futex_wake (futex_word, processes_to_wake, private);
// int res = lll_futex_syscall (4, futex_word, __lll_private_flag (FUTEX_WAKE, private), processes_to_wake, 0);
int res = INTERNAL_SYSCALL (futex, 4, futex_word, __lll_private_flag (FUTEX_WAKE, private), processes_to_wake, 0);
if (res >= 0)
return;
// 代码省略
}void PosixSemaphore::wait() {
int ret;
do {
ret = sem_wait(&_semaphore);
// EINTR = 4
// ret == 0 表示成功。 ret == EAGAIN == 11,表示需要重试,但也是成功。
// 所以这里的判断条件要这么写
} while (ret != 0 && errno == EINTR);
}- return: 0 表示获取到信号量, EAGAIN==11 表示需要重试(当前值与目标值不一致)。
int __new_sem_wait (sem_t *sem) {
// 我们需要在这里检查我们是否需要对取消请求采取行动,
// 因为 POSIX 指定取消点在 sem_wait 和 sem_timedwait 中“应发生”,
// 这也意味着无论是否阻塞它们都需要检查这一点(与“可能发生”功能不同)。
__pthread_testcancel ();
// 第二个参数传0,表示只尝试一次 cas
if (__new_sem_wait_fast ((struct new_sem *) sem, 0) == 0)
// 如果获取到信号量,直接返回
return 0;
else
return __new_sem_wait_slow64 ((struct new_sem *) sem, CLOCK_REALTIME, NULL);
}static int __new_sem_wait_fast (struct new_sem *sem, int definitive_result) {
// 原子从内存加载一个值
uint64_t d = atomic_load_relaxed (&sem->data);
do
{
// SEM_VALUE_MASK 对应的是 0xFFFFFFFF
if ((d & SEM_VALUE_MASK) == 0) // == 0 说明低32位都是0。也就是信号量不足,必须等待
break;
// 原子地将 data - 1,且此时低32位不为0,
if (atomic_compare_exchange_weak_acquire(&sem->data, &d, d - 1))
// 由于操作之前 data > 0,所以走到这里就说明获取到了信号量
return 0;
}
while (definitive_result);
return -1;
}- return: 0 表示获取到信号量, EAGAIN==11 表示需要重试(当前值与目标值不一致)。
static int
__attribute__ ((noinline))
__new_sem_wait_slow64 (struct new_sem *sem, clockid_t clockid,
const struct __timespec64 *abstime)
{
int err = 0;
// SEM_NWAITERS_SHIFT = 32。相当于拿 data 的高32位来记录当前有几个等待者。
uint64_t d = atomic_fetch_add_relaxed (&sem->data, (uint64_t) 1 << SEM_NWAITERS_SHIFT);
pthread_cleanup_push (__sem_wait_cleanup, sem);
for (;;) {
// SEM_VALUE_MASK = 0xFFFFFFFF,如果信号量为0
if ((d & SEM_VALUE_MASK) == 0) {
// 进入等待状态。
// err = 0 表示获取到信号量, err = EAGAIN = 11 表示需要重试(当前值与目标值不一致)。
err = do_futex_wait (sem, clockid, abstime);
// 遇到错误。超时 | | 栈溢出
if (err == ETIMEDOUT || err == EINTR || err == EOVERFLOW) {
__set_errno (err);
err = -1;
/* Stop being registered as a waiter. */
// 取消等待中的状态
atomic_fetch_add_relaxed (&sem->data,
-((uint64_t) 1 << SEM_NWAITERS_SHIFT));
break;
}
// 由于被唤醒了,大概率是信号量不为0了,所以再次尝试从内存获取一下 data 值
d = atomic_load_relaxed (&sem->data);
} else {
// 尝试获取一下锁,由于高32 + 1 和 低 32 + 1。所以通过参数3就可以还原回去。
if (atomic_compare_exchange_weak_acquire (&sem->data,
&d, d - 1 - ((uint64_t) 1 << SEM_NWAITERS_SHIFT)))
{
err = 0;
break;
}
}
}
// 此操作会将高32位的等待获取信号量的个数 -1
pthread_cleanup_pop (0);
return err;
}static void __sem_wait_cleanup (void *arg) {
struct new_sem *sem = (struct new_sem *) arg;
// SEM_NWAITERS_SHIFT = 32,就是将高32的值 -1
atomic_fetch_add_relaxed (&sem->data, -((uint64_t) 1 << SEM_NWAITERS_SHIFT));
}- return: 0 表示获取到信号量, EAGAIN==11 表示需要重试(当前值与目标值不一致)。
static int
__attribute__ ((noinline))
do_futex_wait (struct new_sem *sem, clockid_t clockid,
const struct __timespec64 *abstime)
{
int err;
// clockid = CLOCK_REALTIME, abstime = NULL
err = __futex_abstimed_wait_cancelable64 (
(unsigned int *) &sem->data + SEM_VALUE_OFFSET, 0,
clockid, abstime,
sem->private);
return err;
}- return: 0 表示获取到信号量, EAGAIN==11 表示需要重试(当前值与目标值不一致)。
int
__futex_abstimed_wait_cancelable64 (unsigned int* futex_word,
unsigned int expected, clockid_t clockid,
const struct __timespec64* abstime,
int private)
{
return __futex_abstimed_wait_common (futex_word, expected, clockid,
abstime, private, true);
}- return: 0 表示获取到信号量, EAGAIN==11 表示需要重试(当前值与目标值不一致)。
static int
__futex_abstimed_wait_common (unsigned int* futex_word,
unsigned int expected, clockid_t clockid,
const struct __timespec64* abstime,
int private, bool cancel)
{
int err;
unsigned int clockbit;
// 由于 clockid == CLOCK_REALTIME,所以 clockbit = FUTEX_CLOCK_REALTIME
clockbit = (clockid == CLOCK_REALTIME) ? FUTEX_CLOCK_REALTIME : 0;
// int op = __lll_private_flag (FUTEX_WAIT_BITSET | clockbit, private);
int op = ((FUTEX_WAIT_BITSET | clockbit) | FUTEX_PRIVATE_FLAG) ^ (private);
err = __futex_abstimed_wait_common64 (futex_word, expected, op, abstime,
private, cancel);
switch (err)
{
case 0:
case -EAGAIN:
case -EINTR:
case -ETIMEDOUT:
case -EINVAL:
case -EOVERFLOW: /* Passed absolute timeout uses 64 bit time_t type, but
underlying kernel does not support 64 bit time_t futex
syscalls. */
return -err;
case -EFAULT: /* Must have been caused by a glibc or application bug. */
case -ENOSYS: /* Must have been caused by a glibc bug. */
/* No other errors are documented at this time. */
default:
futex_fatal_error ();
}
}- return: 0 表示获取到信号量, EAGAIN==11 表示需要重试(当前值与目标值不一致)。
static int
__futex_abstimed_wait_common64 (unsigned int* futex_word,
unsigned int expected, int op,
const struct __timespec64* abstime,
int private, bool cancel)
{
if (cancel)
// cancel 为 true
// 发起一次linux 系统调用,系统函数是 futex_time64。且等待 futex_word 指向的值是 op
return INTERNAL_SYSCALL_CANCEL (futex_time64, futex_word, op, expected,
abstime, NULL /* Unused. */,
FUTEX_BITSET_MATCH_ANY);
else
return INTERNAL_SYSCALL_CALL (futex_time64, futex_word, op, expected,
abstime, NULL /* Ununsed. */,
FUTEX_BITSET_MATCH_ANY);
}- MEMMODEL_RELAXED = 0: 最宽松的模式,memory_order_relaxed没有happens-before的约束,编译器和处理器可以对memory access做任何的re-order,因此另外的线程不能对其做任何的假设,这种模式下能做的唯一保证,就是一旦线程读到了变量var的最新值,那么这个线程将再也见不到var修改之前的值了。
- MEMMODEL_CONSUME = 1: 是一个相比Acquire/Release更加宽松的内存模型,对非依赖的变量也去除了happens-before的限制,减少了所需同步的数据量,可以加快执行的速度。相当于 happen-before
- MEMMODEL_ACQUIRE = 2: 保证在这个操作之后的memory accesses不会重排到这个操作之前去,但是这个操作之前的memory accesses可能会重排到这个操作之后去。
- MEMMODEL_RELEASE = 3: 保证在这个操作之前的memory accesses不会重排到这个操作之后去,但是这个操作之后的memory accesses可能会重排到这个操作之前去。
- MEMMODEL_ACQ_REL = 4:
- MEMMODEL_SEQ_CST = 5: 所有memory accesses操作的重排不允许跨域这个操作,同时这个限制是双向的。
- MEMMODEL_LAST = 6,