https://gcc.gnu.org/g:45477b791daf72d62dd0d7dbc72e8a3cb66391e5

commit r16-7962-g45477b791daf72d62dd0d7dbc72e8a3cb66391e5
Author: Jonathan Wakely <[email protected]>
Date:   Tue Nov 25 14:29:50 2025 +0000

    libstdc++: Add platform wait functions for FreeBSD [PR120527]
    
    This defines __platform_wait, __platform_notify, and
    __platform_wait_until for FreeBSD, making use of the _umtx_op syscall.
    
    The Linux versions of those functions only support 32-bit integers, but
    the FreeBSD versions use the syscall for both 32-bit and 64-bit types,
    as the _umtx_op supports both.
    
    We also need to change __spin_impl because it currently assumes the
    waitable at args._M_obj is always a __platform_wait_t. Because FreeBSD
    supports waiting on both 32-bit and 64-bit integers, we need a
    platform-specific function for loading a value from _M_obj.  This adds a
    new __platform_load function, which does an atomic load of the right
    size. The Linux definition just loads an int, but for FreeBSD it depends
    on _M_obj_size. We also need a generic version of the function for
    platforms without __platform_wait, because __spin_impl is always used,
    even when the __waitable_state contains a condition_variable.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/120527
            * include/bits/atomic_wait.h [__FreeBSD__] (__platform_wait_t):
            Define typedef.
            [__FreeBSD__] (__platform_wait_uses_type): Define variable
            template.
            * src/c++20/atomic.cc (__platform_load): New function.
            [__FreeBSD__] (_GLIBCXX_HAVE_PLATFORM_WAIT, __platform_wait)
            (__platform_notify, __platform_wait_until): Define.
            (__detail::__spin_impl): Use __platform_load.
    
    Reviewed-by: Tomasz KamiƄski <[email protected]>

Diff:
---
 libstdc++-v3/include/bits/atomic_wait.h | 11 +++++
 libstdc++-v3/src/c++20/atomic.cc        | 78 ++++++++++++++++++++++++++++++++-
 2 files changed, 87 insertions(+), 2 deletions(-)

diff --git a/libstdc++-v3/include/bits/atomic_wait.h 
b/libstdc++-v3/include/bits/atomic_wait.h
index fb4457566395..47bd1073f881 100644
--- a/libstdc++-v3/include/bits/atomic_wait.h
+++ b/libstdc++-v3/include/bits/atomic_wait.h
@@ -69,6 +69,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     inline constexpr bool __platform_wait_uses_type
       = __detail::__waitable<_Tp>
          && sizeof(_Tp) == sizeof(int) && alignof(_Tp) >= 4;
+#elif defined __FreeBSD__ && __SIZEOF_LONG__ == 8
+  namespace __detail
+  {
+    using __platform_wait_t = __UINT64_TYPE__;
+    inline constexpr size_t __platform_wait_alignment = 8;
+  }
+  template<typename _Tp>
+    inline constexpr bool __platform_wait_uses_type
+      = __detail::__waitable<_Tp>
+         && ((sizeof(_Tp) == 4 && alignof(_Tp) >= 4)
+               || (sizeof(_Tp) == 8 && alignof(_Tp) >= 8));
 #else
 // define _GLIBCX_HAVE_PLATFORM_WAIT and implement __platform_wait()
 // and __platform_notify() if there is a more efficient primitive supported
diff --git a/libstdc++-v3/src/c++20/atomic.cc b/libstdc++-v3/src/c++20/atomic.cc
index 8b4d5233bbbf..177c503330d5 100644
--- a/libstdc++-v3/src/c++20/atomic.cc
+++ b/libstdc++-v3/src/c++20/atomic.cc
@@ -27,7 +27,7 @@
 #if __glibcxx_atomic_wait
 #include <atomic>
 #include <bits/atomic_timed_wait.h>
-#include <cstdint> // uint32_t, uint64_t
+#include <cstdint> // uint32_t, uint64_t, uintptr_t
 #include <climits> // INT_MAX
 #include <cerrno>  // errno, ETIMEDOUT, etc.
 #include <bits/std_mutex.h>  // std::mutex, std::__condvar
@@ -39,6 +39,11 @@
 # include <unistd.h>
 # include <sys/time.h> // timespec
 # define _GLIBCXX_HAVE_PLATFORM_WAIT 1
+#elif defined __FreeBSD__ && __FreeBSD__ >= 11 && __SIZEOF_LONG__ == 8
+# include <sys/types.h>
+# include <sys/umtx.h>
+# include <sys/time.h>
+# define _GLIBCXX_HAVE_PLATFORM_WAIT 1
 #endif
 
 #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
@@ -87,6 +92,13 @@ namespace
                        __wait_clock_t::time_point timeout,
                        int obj_size) = delete;
 
+  // This is needed even when we don't have __platform_wait
+  [[gnu::always_inline]]
+  inline __wait_value_type
+  __platform_load(const __platform_wait_t* addr, int memory_order,
+                 int /* obj_sz */) noexcept
+  { return __atomic_load_n(addr, memory_order); }
+
 #elif defined _GLIBCXX_HAVE_LINUX_FUTEX
 
   const int futex_private_flag = 128;
@@ -136,6 +148,68 @@ namespace
       }
     return true;
   }
+
+  [[gnu::always_inline]]
+  inline __wait_value_type
+  __platform_load(const int* addr, int order, int /* obj_sz */) noexcept
+  { return __atomic_load_n(addr, order); }
+
+#elif defined __FreeBSD__ && __SIZEOF_LONG__ == 8
+  [[gnu::always_inline]]
+  inline int
+  wait_op(int obj_sz) noexcept
+  { return obj_sz == sizeof(unsigned) ? UMTX_OP_WAIT_UINT : UMTX_OP_WAIT; }
+
+  void
+  __platform_wait(const void* addr, uint64_t val, int obj_sz) noexcept
+  {
+    if (_umtx_op(const_cast<void*>(addr), wait_op(obj_sz), val,
+                nullptr, nullptr))
+      if (errno != EINTR)
+       __throw_system_error(errno);
+  }
+
+  void
+  __platform_notify(const void* addr, bool all, int /* obj_sz */) noexcept
+  {
+    const int count = all ? INT_MAX : 1;
+    _umtx_op(const_cast<void*>(addr), UMTX_OP_WAKE, count, nullptr, nullptr);
+  }
+
+  // returns true if wait ended before timeout
+  bool
+  __platform_wait_until(const void* addr, uint64_t val,
+                       const __wait_clock_t::time_point& atime,
+                       int obj_sz) noexcept
+  {
+    struct _umtx_time timeout = {
+      ._timeout = chrono::__to_timeout_timespec(atime),
+      ._flags = UMTX_ABSTIME,
+      ._clockid = CLOCK_MONOTONIC
+    };
+    // _umtx_op hangs if timeout._timeout is {0, 0}
+    if (atime.time_since_epoch() < chrono::nanoseconds(1))
+      return false;
+    constexpr uintptr_t timeout_sz = sizeof(timeout);
+    if (_umtx_op(const_cast<void*>(addr), wait_op(obj_sz), val,
+                (void*)timeout_sz, &timeout))
+      {
+       if (errno == ETIMEDOUT)
+         return false;
+       if (errno != EINTR)
+         __throw_system_error(errno);
+      }
+    return true;
+  }
+
+  [[gnu::always_inline]]
+  inline __wait_value_type
+  __platform_load(const void* addr, int order, int obj_sz) noexcept
+  {
+    if (obj_sz == sizeof(long))
+      return __atomic_load_n(static_cast<const long*>(addr), order);
+    return __atomic_load_n(static_cast<const unsigned*>(addr), order);
+  }
 #endif // HAVE_PLATFORM_WAIT
 
   // The state used by atomic waiting and notifying functions.
@@ -259,7 +333,7 @@ namespace
     __wait_value_type wval;
     for (auto i = 0; i < atomic_spin_count; ++i)
       {
-       wval = __atomic_load_n(addr, args._M_order);
+       wval = __platform_load(addr, args._M_order, args._M_obj_size);
        if (wval != args._M_old)
          return { ._M_val = wval, ._M_has_val = true, ._M_timeout = false };
        if (i < atomic_spin_count_relax)

Reply via email to