https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127757

--- Comment #1 from Jonathan Wakely <redi at gcc dot gnu.org> ---
I think it's better to put the fence in _M_waiting() like so:

--- a/libstdc++-v3/src/c++20/atomic.cc
+++ b/libstdc++-v3/src/c++20/atomic.cc
@@ -325,15 +325,28 @@ namespace

     void
     _M_enter_wait() noexcept
-    { __atomic_fetch_add(&_M_waiters, 1, __ATOMIC_SEQ_CST); }
+    {
+      // This seq_cst RMW and the seq_cst fence in _M_waiting() are both in
+      // the single total order on seq_cst operations.
+      __atomic_fetch_add(&_M_waiters, 1, __ATOMIC_SEQ_CST);
+    }

     void
     _M_leave_wait() noexcept
     { __atomic_fetch_sub(&_M_waiters, 1, __ATOMIC_RELEASE); }

+    // Called by notifying thread to decide if it needs to do the notification
+    // (which requires an expensive wake syscall or condvar signal).
     bool
     _M_waiting() const noexcept
     {
+      // This thread modifies the atomic (or the proxy _M_ver) at memory
+      // location A, then loads _M_waiters here. A waiter thread calls
+      // _M_enter_wait() to increment _M_waiters, then loads A. Without this
+      // fence the two loads can both read stale values due to store buffering
+      // and if the notifier sees `_M_waiters == 0` the notification would be
+      // skipped, meaning the waiter would not get unblocked.
+      __atomic_thread_fence(__ATOMIC_SEQ_CST);
       __platform_wait_t __res;
       __atomic_load(&_M_waiters, &__res, __ATOMIC_SEQ_CST);
       return __res != 0;


I'm testing this and plan to submit it with a variation of your testcase
modified by claude.

I'm inclined to rename _M_waiting() to _M_waiters_present() or something a bit
more self-explanatory.

Reply via email to