<html>
    <head>
      <base href="https://bugs.llvm.org/">
    </head>
    <body><table border="1" cellspacing="0" cellpadding="8">
        <tr>
          <th>Bug ID</th>
          <td><a class="bz_bug_link 
          bz_status_NEW "
   title="NEW - counting_semaphore deadlocks?"
   href="https://bugs.llvm.org/show_bug.cgi?id=47013">47013</a>
          </td>
        </tr>

        <tr>
          <th>Summary</th>
          <td>counting_semaphore deadlocks?
          </td>
        </tr>

        <tr>
          <th>Product</th>
          <td>libc++
          </td>
        </tr>

        <tr>
          <th>Version</th>
          <td>11.0
          </td>
        </tr>

        <tr>
          <th>Hardware</th>
          <td>PC
          </td>
        </tr>

        <tr>
          <th>OS</th>
          <td>Windows NT
          </td>
        </tr>

        <tr>
          <th>Status</th>
          <td>NEW
          </td>
        </tr>

        <tr>
          <th>Severity</th>
          <td>normal
          </td>
        </tr>

        <tr>
          <th>Priority</th>
          <td>P
          </td>
        </tr>

        <tr>
          <th>Component</th>
          <td>All Bugs
          </td>
        </tr>

        <tr>
          <th>Assignee</th>
          <td>unassignedclangbugs@nondot.org
          </td>
        </tr>

        <tr>
          <th>Reporter</th>
          <td>gutenev@gmail.com
          </td>
        </tr>

        <tr>
          <th>CC</th>
          <td>llvm-bugs@lists.llvm.org, mclow.lists@gmail.com
          </td>
        </tr></table>
      <p>
        <div>
        <pre>counting_semaphore::release only notifies waiting thread when count reaches
zero. it only notifies one waiting thread if __update == 1:

    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();
    }

<a href="https://github.com/llvm/llvm-project/blob/4357986b411dd164932c66ebfe4a9cf96a7d74cd/libcxx/include/semaphore#L86-L94">https://github.com/llvm/llvm-project/blob/4357986b411dd164932c66ebfe4a9cf96a7d74cd/libcxx/include/semaphore#L86-L94</a>


counting_semaphore::acquire enters waiting when __a is observed to be zero:

    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);
    }


<a href="https://github.com/llvm/llvm-project/blob/4357986b411dd164932c66ebfe4a9cf96a7d74cd/libcxx/include/semaphore#L96-L103">https://github.com/llvm/llvm-project/blob/4357986b411dd164932c66ebfe4a9cf96a7d74cd/libcxx/include/semaphore#L96-L103</a>

Now assume two threads are waiting on acquire call:
T1:  { s.acquire(); }
T2:  { s.acquire(); }

And third thread calls release, to release two times by one:
T3:  { s.release(1); s.release(1); }

first release call unblocks one waiting thread. Assume it is T1, and assume
that before T1 did compare_exchange_strong, T3 executes the second release
call. Since second release observes __a to be 1 (from the previous call), it
never releases T2. So T2 stays blocked while __a == 1.

---

If this analysis is correct, I would have called notify_all() for all cases if
__a was observed to be 0. Except for counting_semaphore<1> specialization,
where it is always safe to call notify_one().

Another alternative could be avoiding 0 < __a.fetch_add check, this will
unblock T2 in subsequent release in my example, but it looks like to be less
efficient and more complex.</pre>
        </div>
      </p>


      <hr>
      <span>You are receiving this mail because:</span>

      <ul>
          <li>You are on the CC list for the bug.</li>
      </ul>
    </body>
</html>