From: Jonathan Wakely Date: Fri, 5 Dec 2025 15:47:10 +0000 (+0000) Subject: libstdc++: Add platform wait functions for Darwin [PR120527] X-Git-Tag: basepoints/gcc-17~24 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=a261719a1fc474d7ec00a59b6e0218f5c851c109;p=thirdparty%2Fgcc.git 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 Signed-off-by: Iain Sandoe --- diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h index 47bd1073f88..704c10d5f38 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 + 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 177c503330d..3e02f437db3 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 #include +#include // cmp_less #include // uint32_t, uint64_t, uintptr_t #include // INT_MAX #include // errno, ETIMEDOUT, etc. @@ -39,6 +40,19 @@ # include # include // 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 # include @@ -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(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(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(atime - __wait_clock_t::now()); + if (reltime <= reltime.zero()) + return false; + uint32_t timeout = numeric_limits::max(); + if (std::cmp_less(reltime.count(), timeout)) + timeout = reltime.count(); + + if (0 > __ulock_wait(wait_op(obj_sz), const_cast(addr), val, + timeout)) + { + if (errno == ETIMEDOUT) + return timeout == numeric_limits::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