libc++ counting_semaphore 是否有死锁问题?
Does libc++ counting_semaphore have deadlock issue?
libc++ counting_semaphore::release
:
void release(ptrdiff_t __update = 1)
{
if(0 < __a.fetch_add(__update, memory_order_release))
;
else if(__update > 1)
__a.notify_all();
else
__a.notify_one();
}
仅当增量前内部计数为零时才通知,仅当增量大于一时才通知多于一名服务员。
libc++ counting_semaphore::acquire
:
void acquire()
{
auto const __test_fn = [=]() -> bool {
auto __old = __a.load(memory_order_relaxed);
return (__old != 0) && __a.compare_exchange_strong(__old, __old - 1, memory_order_acquire, memory_order_relaxed);
};
__cxx_atomic_wait(&__a.__a_, __test_fn);
}
等待计数为非零,并尝试用递减的值对其进行 CAS。
现在请看下面的3线程案例:
counting_semaphore<10> s;
T1: { s.acquire(); /*signal to T3*/ }
T2: { s.acquire(); /*signal to T3*/ }
T3: { /*wait until both signals*/ s.release(1); s.release(1); }
最初:
__a == 0
(desired
参数传递为 0,任何对 acquire
的尝试都会被阻止)
时间线
T1: enters wait
T2: enters wait
T3: fetch add 1 & returns 0, now __a == 1
T3: (0 < 0) is false, so notify_one happens
T1: unblocks from wait
T3: fetch add 1 & returns 1, now __a == 2
T3: (0 < 1) is true, so no notification
T1: loads 2
T1: cas 2 to 1 successfully
T1: returns from acquire
T2: still waits despite __a == 1
这看起来像一个有效的死锁吗?
为什么我在这里提问而不是报告问题?
我很久以前就报道了issue,到现在还没有回复。
我想了解是否确实存在死锁或者我遗漏了什么。
条件if (0 < ...)
是个问题,但不是唯一的问题。
effects of release
表示为:
Atomically execute counter += update. Then, unblocks any threads that are waiting for counter to be greater than zero.
注意“任何线程”这个词。复数。这意味着,即使特定的 update
值恰好为 1,所有在此条件下阻塞的线程 也必须 得到通知。所以调用 notify_one
是错误的,除非 notify_one
的实现总是解除所有等待线程的阻塞。这将是该功能的...有趣的实现。
即使将 notify_one
更改为 notify_all
,也无法解决问题。该条件的逻辑基本上假定线程通知只应在先前 release
通知的所有线程(逻辑上)已从其 acquire
调用中逃脱时发生。标准不需要这样的东西。
libc++ counting_semaphore::release
:
void release(ptrdiff_t __update = 1)
{
if(0 < __a.fetch_add(__update, memory_order_release))
;
else if(__update > 1)
__a.notify_all();
else
__a.notify_one();
}
仅当增量前内部计数为零时才通知,仅当增量大于一时才通知多于一名服务员。
libc++ counting_semaphore::acquire
:
void acquire()
{
auto const __test_fn = [=]() -> bool {
auto __old = __a.load(memory_order_relaxed);
return (__old != 0) && __a.compare_exchange_strong(__old, __old - 1, memory_order_acquire, memory_order_relaxed);
};
__cxx_atomic_wait(&__a.__a_, __test_fn);
}
等待计数为非零,并尝试用递减的值对其进行 CAS。
现在请看下面的3线程案例:
counting_semaphore<10> s;
T1: { s.acquire(); /*signal to T3*/ }
T2: { s.acquire(); /*signal to T3*/ }
T3: { /*wait until both signals*/ s.release(1); s.release(1); }
最初:
__a == 0
(desired
参数传递为 0,任何对 acquire
的尝试都会被阻止)
时间线
T1: enters wait
T2: enters wait
T3: fetch add 1 & returns 0, now __a == 1
T3: (0 < 0) is false, so notify_one happens
T1: unblocks from wait
T3: fetch add 1 & returns 1, now __a == 2
T3: (0 < 1) is true, so no notification
T1: loads 2
T1: cas 2 to 1 successfully
T1: returns from acquire
T2: still waits despite __a == 1
这看起来像一个有效的死锁吗?
为什么我在这里提问而不是报告问题?
我很久以前就报道了issue,到现在还没有回复。 我想了解是否确实存在死锁或者我遗漏了什么。
条件if (0 < ...)
是个问题,但不是唯一的问题。
effects of release
表示为:
Atomically execute counter += update. Then, unblocks any threads that are waiting for counter to be greater than zero.
注意“任何线程”这个词。复数。这意味着,即使特定的 update
值恰好为 1,所有在此条件下阻塞的线程 也必须 得到通知。所以调用 notify_one
是错误的,除非 notify_one
的实现总是解除所有等待线程的阻塞。这将是该功能的...有趣的实现。
即使将 notify_one
更改为 notify_all
,也无法解决问题。该条件的逻辑基本上假定线程通知只应在先前 release
通知的所有线程(逻辑上)已从其 acquire
调用中逃脱时发生。标准不需要这样的东西。