<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>