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.
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;
+ 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;
+
+ 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