On Tue, Nov 25, 2025 at 10:04 PM Jonathan Wakely <[email protected]> wrote:
> This defines __platform_wait, __platform_notify, and
> __platform_wait_until for FreeBSD, making use of the _umtx_op syscall.
>
> The Linux versions of those functions only support 32-bit integers, but
> the FreeBSD versions use the syscall for both 32-bit and 64-bit types,
> as the _umtx_op supports both.
>
> libstdc++-v3/ChangeLog:
>
> PR libstdc++/120527
> * include/bits/atomic_wait.h [__FreeBSD__] (__platform_wait_t):
> Define typedef.
> [__FreeBSD__] (__platform_wait_uses_type): Define variable
> template.
> * src/c++20/atomic.cc [__FreeBSD__] (_GLIBCXX_HAVE_PLATFORM_WAIT)
> (__platform_wait, __platform_notify, __platform_wait_until):
> Define.
> ---
>
> Tested x86_64-linux and x86_64-freebsd14.
>
> v2:
> - Only use _umtx_op for 64-bit long.
> - Fixed error handling to only check for documented errno values.
>
This second patch LGTM.
>
> libstdc++-v3/include/bits/atomic_wait.h | 11 ++++++
> libstdc++-v3/src/c++20/atomic.cc | 48 +++++++++++++++++++++++++
> 2 files changed, 59 insertions(+)
>
> diff --git a/libstdc++-v3/include/bits/atomic_wait.h
> b/libstdc++-v3/include/bits/atomic_wait.h
> index a280d3534f46..0205df40d68c 100644
> --- a/libstdc++-v3/include/bits/atomic_wait.h
> +++ b/libstdc++-v3/include/bits/atomic_wait.h
> @@ -68,6 +68,17 @@ _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
> + namespace __detail
> + {
> + using __platform_wait_t = __UINT64_TYPE__;
> + inline constexpr size_t __platform_wait_alignment = 8;
> + }
> + template<typename _Tp>
> + inline constexpr bool __platform_wait_uses_type
> + = __detail::__waitable<_Tp>
> + && ((sizeof(_Tp) == 4 && alignof(_Tp) >= 4)
> + || (sizeof(_Tp) == 8 && alignof(_Tp) >= 8));
> #else
> // define _GLIBCX_HAVE_PLATFORM_WAIT and implement __platform_wait()
> // and __platform_notify() if there is a more efficient primitive
> supported
> diff --git a/libstdc++-v3/src/c++20/atomic.cc
> b/libstdc++-v3/src/c++20/atomic.cc
> index dfbf4a95cd62..af3b074a52c9 100644
> --- a/libstdc++-v3/src/c++20/atomic.cc
> +++ b/libstdc++-v3/src/c++20/atomic.cc
> @@ -39,6 +39,11 @@
> # include <syscall.h>
> # include <sys/time.h> // timespec
> # define _GLIBCXX_HAVE_PLATFORM_WAIT 1
> +#elif defined __FreeBSD__ && __SIZEOF_LONG__ == 8
> +# include <sys/types.h>
> +# include <sys/umtx.h>
> +# include <sys/time.h>
> +# define _GLIBCXX_HAVE_PLATFORM_WAIT 1
> #endif
>
> #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
> @@ -108,6 +113,49 @@ namespace
> }
> return true;
> }
> +#elif defined __FreeBSD__ && __SIZEOF_LONG__ == 8
> + void
> + __platform_wait(const void* addr, uint64_t val, int obj_sz) noexcept
> + {
> + const int op = obj_sz == sizeof(int) ? UMTX_OP_WAIT_UINT :
> UMTX_OP_WAIT;
>
Could you put obj_sz == sizeof(int) in parenthesis: op = (obj_sz ==
sizeof(int)) ?
I have trouble visually parsing this, due proximity of = and ==.
Also would use sizeof(unsigned) instead of sizeof(int) so it matches _UINT
in the
op name. I know they are the same, but it would remove some mental
gymnastics for me.
> + if (_umtx_op(const_cast<void*>(addr), op, val, nullptr, nullptr))
> + if (errno != EINTR)
> + __throw_system_error(errno);
> + }
> +
> + void
> + __platform_notify(const void* addr, bool all, int /* obj_sz */) noexcept
> + {
> + const int count = all ? INT_MAX : 1;
> + _umtx_op(const_cast<void*>(addr), UMTX_OP_WAKE, count, nullptr,
> nullptr);
> + }
> +
> + // 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
> + {
> + struct _umtx_time timeout = {
> + ._timeout = chrono::__to_timeout_timespec(atime),
> + ._flags = UMTX_ABSTIME,
> + ._clockid = CLOCK_MONOTONIC
> + };
> + // _umtx_op hangs if timeout._timeout is {0, 0}
> + if (atime.time_since_epoch() < chrono::nanoseconds(1))
> + return false;
> + constexpr uintptr_t timeout_sz = sizeof(timeout);
> + const int op = obj_sz == sizeof(int) ? UMTX_OP_WAIT_UINT :
> UMTX_OP_WAIT;
>
Same here.
> +
> + if (_umtx_op(const_cast<void*>(addr), op, val, (void*)timeout_sz,
> &timeout))
> + {
> + if (errno == ETIMEDOUT)
> + return false;
> + if (errno != EINTR)
> + __throw_system_error(errno);
> + }
> + return true;
> + }
> #endif // HAVE_LINUX_FUTEX
>
> // The state used by atomic waiting and notifying functions.
> --
> 2.51.1
>
>