On Fri, 05 Dec 2025 at 20:08 +0000, Iain Sandoe wrote:
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.

We could define __detail::__platform_wait_t as uint32_t in the header,
as the statically-known-to-work type, and have the library decide at
runtime whether 64b types are also supported. That would mean code
compiled against an older SDK would still use __ulock_wait if it's
deployed on a system where the libstdc++.dylib was built against a
newer SDK and so knows it can use 64b __ulock_wait.

(FAOD the "runtime check" I'm referring to here means the user code
compiled against libstdc++ headers doesn't know the answer so calls a
function in the dylib, which gives an answer - but that answer is
statically determined in the dylib based on which SDK the dylib was
compiled against. I'm not talking about the dylib trying a 64b
__ulock_wait call to see if it works, or checking weak refs or
anything like that.)

Either way, the #ifdef __APPLE__ check in bits/atomic_wait.h needs to
ensure that __platform_wait_uses_type<T> is only true for types that
really are supported by the library code. So if we restrict the use of
__ulock_wait to __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 101500
in the library, then we need to also guard the code in the header with
an equivalent check. Or we could not have the __APPLE__ check in the
header at all, so that no types are statically-known-to-work and all
types need to do a runtime check by calling into the library (which
would then say that 32b and 64b types are supported, and we could make
that support 32b types for older OS versions, and restrict 64b support
to >= 101500.

Or the header could use the older check and be conservative and only
define __platform_wait_uses_type<T> to true for 32b types, and the
library runtime check could say yes for 64b based on a check for the
later version.

I can hack that up to show what it would look like if you want to try
and have more support for older systems.

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)

Yeah, let's stick to checking the macro, so that for a given .dylib
the decision of whether 64b types work is set when the dylib is built.

That still allows user code compiled on an older OS version to
magically gain support for 64b ulock_wait if deployed+run against a
newer dylib on a newer OS version.


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!


Reply via email to