[libcxx-commits] [libcxx] [libc++] fix `counting_semaphore` lost wakeups (PR #79265)
Jan Kokemüller via libcxx-commits
libcxx-commits at lists.llvm.org
Sat Feb 3 02:58:01 PST 2024
================
@@ -94,20 +95,25 @@ public:
auto __old = __a_.fetch_add(__update, memory_order_release);
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
__update <= _LIBCPP_SEMAPHORE_MAX - __old, "update is greater than the expected value");
-
- if (__old > 0) {
- // Nothing to do
- } else if (__update > 1)
+ (void)__old;
+ if (__update > 1) {
__a_.notify_all();
- else
+ } else {
__a_.notify_one();
+ }
}
_LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void acquire() {
auto const __test_fn = [this]() -> 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);
+ auto const __test_with_old = [this](__cxx_contention_t& __monitor) -> bool {
----------------
jiixyj wrote:
> edit: Scratch the next part -- I'm not 100% sure, but it could well be that the condition for the `cxx_atomic_wait` has to be "strong". So if one has a "weak" `try_acquire()`, one has to implement the `acquire()` seperately, just as you wrote. I have to think about this some more :)
I think I got it! You basically can implement `try_acquire()` in three different ways, and the way you would implement `acquire()` in terms of that differs significantly:
1. "strong" `try_acquire()`: this only ever returns `false` if it has actually observed the semaphore value to be "0". You can implement `acquire()` in terms of that if you notify the eventcount if `old == 0` in `release()`.
2. "weak" `try_acquire()` with strong CAS (`return value != 0 && cas_strong(...);`): You could implement `acquire()` in terms of that, but then you would have to notify in any case, not only if `old == 0`! Example execution:
```
producer consumer sem evc (eventcount)
-------------------------------------------
42 0
-------------------------------------------
monitor=0 42 0
-------------------------------------------
old=42 42 0
-------------------------------------------
release(1) 43 0
-------------------------------------------
strong
CAS(42->41) 43 0
fails
-------------------------------------------
evc.notify() 43 1
^
this is
important!
-------------------------------------------
evc.wait() 43 1
ends
as monitor
doesn't
match (0!=1)
-------------------------------------------
loop restarts
and strong CAS 42 1
will succeed
this time
```
3. "weak" `try_acquire()`, with weak CAS (`return value != 0 && cas_weak(...);`): You cannot implement `acquire()` in terms of that, because the weak CAS could just randomly decide to spuriously fail, and any notifications would get lost.
Sorry for the confused rambling, but I hope this kinda makes sense...
https://github.com/llvm/llvm-project/pull/79265
More information about the libcxx-commits
mailing list