Hi Jonathan, As discussed off-list, here are my findings about implementing this on Darwin (presented here for the search engine record).
> On 26 Nov 2025, at 09:48, Jonathan Wakely <[email protected]> wrote: > > On Tue, 25 Nov 2025 at 21:08, Jonathan Wakely <[email protected]> wrote: >> >> This defines __platform_wait, __platform_notify, and >> __platform_wait_until for darwin, making use of the __ulock_wait and >> __ulock_wake functions. >> >> As with the FreeBSD _mutx_op syscall, these functions support both >> 32-bit and 64-bit types. >> >> libstdc++-v3/ChangeLog: >> >> PR libstdc++/120527 >> * include/bits/atomic_wait.h [__APPLE__]: Reuse FreeBSD >> definitions of __platform_wait_t and __platform_wait_uses_type. >> * src/c++20/atomic.cc [__APPLE__] (_GLIBCXX_HAVE_PLATFORM_WAIT) >> (__platform_wait, __platform_notify, __platform_wait_until): >> Define. >> --- >> >> Untested, so I don't plan to commit this, but maybe Iain will want to >> test it some time. > > N.B. libc++ uses __ulock_wait based on this check: > > # if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101500 > > so we might want a similar check, 1/ The __ulock_{wait,wake} functions are undocumented, but are very thin wrappers over the corresponding syscall (the source code for the syscall impl. is available but the source code for the wrappers is not). The issue is less about whether the function is available (it is on much of the supported os verison range), but more that the functionality of the syscall has expanded over time - and therefore the presence of the function cannot be taken to represent this (in this case whether UL_COMPARE_AND_WAIT64 is supported). NOTE: 32b locks are much more widely supported over OS versions if that is useful. 2/ The functions appear to return the number of remaining waiters on the object for OK and -1 + errno for failure. Optionally, it seems that one can pass a flag to arrange for the result to be -ERRNO-VALUE and avoid using errno. Since the use is in the cold path, there does not seem to be too much motivation to avoid errno in this case. However, we must detect fail as res < 0 rather than != 0. > and might want to not define the > __platform_wait_uses_type variable template to true in > bits/atomic_wait.h and instead only do a runtime check in > use_proxy_wait, based on whether a weak symbol is resolved at runtime. > So it would depend on the system where the code executes, instead of > being a compile-time constant. Because the presence of the function does not indicate it’s capability and because we also build the runtimes to support a minimum OS version it seems reasonable to use __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ as is done elsewhere. I’ve done that in the changes I made. Using a weak ref is not enough here. If we were to make a runtime check, it would have to be a dummy call that used UL_COMPARE_AND_WAIT64 and then set some flag (I did not try to implement this) I’m attaching two incremental patchlets to make sure it’s clear what I tested (they are really quite trivial - consider them review feedback). thanks Iain
0001-libstdc-Darwin-Incremental-patch-for-platform_wait.patch
Description: Binary data
0002-libstdc-Dragonfly-Fix-computation-of-time-offset-in-.patch
Description: Binary data
>> libstdc++-v3/include/bits/atomic_wait.h | 2 +-
>> libstdc++-v3/src/c++20/atomic.cc | 49 +++++++++++++++++++++++++
>> 2 files changed, 50 insertions(+), 1 deletion(-)
>>
>> diff --git a/libstdc++-v3/include/bits/atomic_wait.h
>> b/libstdc++-v3/include/bits/atomic_wait.h
>> index 0205df40d68c..752f0e1e1baa 100644
>> --- a/libstdc++-v3/include/bits/atomic_wait.h
>> +++ b/libstdc++-v3/include/bits/atomic_wait.h
>> @@ -68,7 +68,7 @@ _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
>> +#elif defined __APPLE__ || (defined __FreeBSD__ && __SIZEOF_LONG__ == 8)
>> namespace __detail
>> {
>> using __platform_wait_t = __UINT64_TYPE__;
>> diff --git a/libstdc++-v3/src/c++20/atomic.cc
>> b/libstdc++-v3/src/c++20/atomic.cc
>> index af3b074a52c9..57509caca87a 100644
>> --- a/libstdc++-v3/src/c++20/atomic.cc
>> +++ b/libstdc++-v3/src/c++20/atomic.cc
>> @@ -39,6 +39,15 @@
>> # include <syscall.h>
>> # include <sys/time.h> // timespec
>> # define _GLIBCXX_HAVE_PLATFORM_WAIT 1
>> +#elif defined __APPLE__
>> +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__ && __SIZEOF_LONG__ == 8
>> # include <sys/types.h>
>> # include <sys/umtx.h>
>> @@ -113,6 +122,46 @@ namespace
>> }
>> return true;
>> }
>> +#elif defined __APPLE__
>> + void
>> + __platform_wait(const void* addr, uint64_t val, int obj_sz) noexcept
>> + {
>> + const uint32_t op = obj_sz == 4 ? UL_COMPARE_AND_WAIT :
>> UL_COMPARE_AND_WAIT64;
>> + if (__ulock_wait(op, 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 = obj_sz == 4 ? UL_COMPARE_AND_WAIT : UL_COMPARE_AND_WAIT64;
>> + 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>(__wait_clock_t::now() - atime);
>> + if (reltime <= reltime.zero())
>> + return false;
>> + uint32_t op = obj_sz == 4 ? UL_COMPARE_AND_WAIT : UL_COMPARE_AND_WAIT64;
>> +
>> + if (__ulock_wait(op, const_cast<void*>(addr), val, reltime.count()))
>> + {
>> + if (errno == ETIMEDOUT)
>> + return false;
>> + if (errno != EINTR && errno != EFAULT)
>> + __throw_system_error(errno);
>> + }
>> + return true;
>> + }
>> #elif defined __FreeBSD__ && __SIZEOF_LONG__ == 8
>> void
>> __platform_wait(const void* addr, uint64_t val, int obj_sz) noexcept
>> --
>> 2.51.1
