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