]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Add platform wait functions for Darwin [PR120527]
authorJonathan Wakely <jwakely@redhat.com>
Fri, 5 Dec 2025 15:47:10 +0000 (15:47 +0000)
committerJonathan Wakely <redi@gcc.gnu.org>
Tue, 21 Apr 2026 14:22:28 +0000 (15:22 +0100)
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>
libstdc++-v3/include/bits/atomic_wait.h
libstdc++-v3/src/c++20/atomic.cc

index 47bd1073f881aca28610e02e82708e3816d11d59..704c10d5f3825bf1ef6c0a6322e4f13744d9d5be 100644 (file)
@@ -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
   {
index 177c503330d5675ed076c570bbc7b225b7e9fefc..3e02f437db3c5a02b87829d88f4fa905f17e3fb9 100644 (file)
@@ -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.
 # 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