]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Fix race condition in new atomic notify code
authorJonathan Wakely <jwakely@redhat.com>
Thu, 9 Jan 2025 22:32:02 +0000 (22:32 +0000)
committerJonathan Wakely <redi@gcc.gnu.org>
Fri, 30 May 2025 09:02:27 +0000 (10:02 +0100)
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

index 172910a4e34f8254ca2fc87d421282127e3e7f9f..725ea030245ddb6e1e84ba8be761003d7397aadf 100644 (file)
@@ -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