When using a proxy object for atomic waiting and notifying operations, we need to ensure that the _M_ver value is always incremented by a notifying operation, even if we return early without doing the futex wake syscall. Otherwise we get missed wake-ups because the notifying thread doesn't modify the value that other threads are doing a futex wait on.
libstdc++-v3/ChangeLog: * include/bits/atomic_wait.h (__notify_impl): Increment the proxy value before returning early for the uncontended case. --- libstdc++-v3/include/bits/atomic_wait.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h index 172910a4e34f..725ea030245d 100644 --- a/libstdc++-v3/include/bits/atomic_wait.h +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -406,18 +406,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { __waiter_pool_impl* __pool = nullptr; - if (__args & __wait_flags::__track_contention) - { - __pool = &__waiter_pool_impl::_S_impl_for(__addr); - if (!__pool->_M_waiting()) - return; - } - const __platform_wait_t* __wait_addr; if (__args & __wait_flags::__proxy_wait) { - if (!__pool) - __pool = &__waiter_pool_impl::_S_impl_for(__addr); + __pool = &__waiter_pool_impl::_S_impl_for(__addr); // Waiting for *__addr is actually done on the proxy's _M_ver. __wait_addr = &__pool->_M_ver; __atomic_fetch_add(&__pool->_M_ver, 1, __ATOMIC_RELAXED); @@ -430,6 +422,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION else // Use the atomic variable's own address. __wait_addr = __addr; + if (__args & __wait_flags::__track_contention) + { + if (!__pool) + __pool = &__waiter_pool_impl::_S_impl_for(__addr); + if (!__pool->_M_waiting()) + return; + } + #ifdef _GLIBCXX_HAVE_PLATFORM_WAIT __platform_notify(__wait_addr, __all); #else -- 2.49.0