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 调用中逃脱时发生。标准不需要这样的东西。