[gcc r16-8780] libstdc++: Add platform wait functions for Darwin [PR120527]

Jonathan Wakely redi@gcc.gnu.org
Tue Apr 21 14:42:07 GMT 2026


https://gcc.gnu.org/g:a261719a1fc474d7ec00a59b6e0218f5c851c109

commit r16-8780-ga261719a1fc474d7ec00a59b6e0218f5c851c109
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Fri Dec 5 15:47:10 2025 +0000

    libstdc++: Add platform wait functions for Darwin [PR120527]
    
    Darwin has kernel support for this facility from 10.12 (macOS Sierra).
    
    From 10.15 (macOS Catalina) 64bit qualitities are supported.
    
    When the library is built for 10.12+ both 32b and 64b quantities will be
    supported by the DSO which means it can be installed on 10.12+ with support
    for 64bit available when the instalation is >= 10.15.
    
    The header will only recognise 64b quantities when the deployment version
    is >= 10.15.
    
    If the library is built for <= 10.11, the support will be missing and attempts
    to use it wlll result in link errors.
    
    The platform wait type is unconditionally set to 32bits, since this is compatible
    across supported OS editions.
    
            PR libstdc++/120527
    
    libstdc++-v3/ChangeLog:
    
            * include/bits/atomic_wait.h:
            * src/c++20/atomic.cc (__ulock_wait): Enable supported Darwin versions.
            (__ulock_wake): Likewise.
            (UL_COMPARE_AND_WAIT): New.
            (UL_COMPARE_AND_WAIT64): New.
            (ULF_WAKE_ALL): New.
            (_GLIBCXX_HAVE_PLATFORM_WAIT): Enable for suppported Darwin versions.
    
    Co-authored-by: Iain Sandoe <iain@sandoe.co.uk>
    Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>

Diff:
---
 libstdc++-v3/include/bits/atomic_wait.h | 21 ++++++++++
 libstdc++-v3/src/c++20/atomic.cc        | 74 +++++++++++++++++++++++++++++++++
 2 files changed, 95 insertions(+)

diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h
index 47bd1073f881..704c10d5f382 100644
--- a/libstdc++-v3/include/bits/atomic_wait.h
+++ b/libstdc++-v3/include/bits/atomic_wait.h
@@ -69,6 +69,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     inline constexpr bool __platform_wait_uses_type
       = __detail::__waitable<_Tp>
 	  && sizeof(_Tp) == sizeof(int) && alignof(_Tp) >= 4;
+#elif defined __APPLE__ \
+      && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101200
+  namespace __detail
+  {
+    using __platform_wait_t = __INT32_TYPE__;
+    inline constexpr size_t __platform_wait_alignment = 4;
+  }
+  // Defined to true for a subset of __waitable types which are statically
+  // known to definitely be able to use __ulock_wait, not a proxy wait.
+  // We know that OS Versions later than 10.15 support 64b wait types even
+  // though we must make the __platform_wait_t 32b for compatibility with
+  // earlier versions of __ulock_xxxx.
+  template<typename _Tp>
+    inline constexpr bool __platform_wait_uses_type
+      = __detail::__waitable<_Tp>
+#  if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101500
+	  && sizeof(_Tp) == 4 && alignof(_Tp) >= 4;
+#  else
+	  && ((sizeof(_Tp) == 4 && alignof(_Tp) >= 4)
+		|| (sizeof(_Tp) == 8 && alignof(_Tp) >= 8));
+#  endif
 #elif defined __FreeBSD__ && __SIZEOF_LONG__ == 8
   namespace __detail
   {
diff --git a/libstdc++-v3/src/c++20/atomic.cc b/libstdc++-v3/src/c++20/atomic.cc
index 177c503330d5..3e02f437db3c 100644
--- a/libstdc++-v3/src/c++20/atomic.cc
+++ b/libstdc++-v3/src/c++20/atomic.cc
@@ -27,6 +27,7 @@
 #if __glibcxx_atomic_wait
 #include <atomic>
 #include <bits/atomic_timed_wait.h>
+#include <utility> // cmp_less
 #include <cstdint> // uint32_t, uint64_t, uintptr_t
 #include <climits> // INT_MAX
 #include <cerrno>  // errno, ETIMEDOUT, etc.
@@ -39,6 +40,19 @@
 # include <unistd.h>
 # include <sys/time.h> // timespec
 # define _GLIBCXX_HAVE_PLATFORM_WAIT 1
+#elif defined __APPLE__ \
+      && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101200
+// These are thin wrappers over the underlying syscall, they exist on
+// earlier versions of the OS, however those versions do not support the
+// UL_COMPARE_AND_WAIT64 operation.
+extern "C" int
+__ulock_wait(uint32_t operation, void* addr, uint64_t value, uint32_t timeout);
+extern "C" int
+__ulock_wake(uint32_t operation, void* addr, uint64_t wake_value);
+# define UL_COMPARE_AND_WAIT             1
+# define UL_COMPARE_AND_WAIT64          5
+# define ULF_WAKE_ALL                    0x00000100
+# define _GLIBCXX_HAVE_PLATFORM_WAIT 1
 #elif defined __FreeBSD__ && __FreeBSD__ >= 11 && __SIZEOF_LONG__ == 8
 # include <sys/types.h>
 # include <sys/umtx.h>
@@ -154,6 +168,66 @@ namespace
   __platform_load(const int* addr, int order, int /* obj_sz */) noexcept
   { return __atomic_load_n(addr, order); }
 
+#elif defined __APPLE__ \
+      && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101200
+
+  [[gnu::always_inline]]
+  inline uint32_t
+  wait_op(int obj_sz) noexcept
+  {
+    __glibcxx_assert(obj_sz == 4 || obj_sz == 8);
+    return obj_sz == 4 ? UL_COMPARE_AND_WAIT : UL_COMPARE_AND_WAIT64;
+  }
+
+  void
+  __platform_wait(const void* addr, uint64_t val, int obj_sz) noexcept
+  {
+    if (0 > __ulock_wait(wait_op(obj_sz), const_cast<void*>(addr), val, 0))
+      if (errno != EINTR && errno != EFAULT)
+	__throw_system_error(errno);
+  }
+
+  void
+  __platform_notify(const void* addr, bool all, int obj_sz) noexcept
+  {
+    uint32_t op = wait_op (obj_sz);
+    if (all)
+      op |= ULF_WAKE_ALL;
+    __ulock_wake(op, const_cast<void*>(addr), 0);
+  }
+
+  // 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
+  {
+    auto reltime
+      = chrono::ceil<chrono::microseconds>(atime - __wait_clock_t::now());
+    if (reltime <= reltime.zero())
+      return false;
+    uint32_t timeout = numeric_limits<uint32_t>::max();
+    if (std::cmp_less(reltime.count(), timeout))
+       timeout = reltime.count();
+
+    if (0 > __ulock_wait(wait_op(obj_sz), const_cast<void*>(addr), val,
+			 timeout))
+      {
+	if (errno == ETIMEDOUT)
+	  return timeout == numeric_limits<uint32_t>::max();
+	if (errno != EINTR && errno != EFAULT)
+	  __throw_system_error(errno);
+      }
+    return true;
+  }
+
+  // ??? CHECKME: this could likely be more efficient.
+  [[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 __FreeBSD__ && __SIZEOF_LONG__ == 8
   [[gnu::always_inline]]
   inline int


More information about the Libstdc++-cvs mailing list