[libcxx-commits] [libcxx] [libc++] atomic timed wait (PR #172214)
via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Jan 7 13:22:02 PST 2026
https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/172214
>From 6fb754bc5b55904bd1e219747926e77526b4c902 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 14 Dec 2025 15:11:31 +0000
Subject: [PATCH 1/3] [libc++] atomic timed wait
---
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__atomic/atomic_sync_timed.h | 143 ++++++++++++++++++
.../include/__atomic/atomic_waitable_traits.h | 1 -
libcxx/include/__thread/poll_with_backoff.h | 1 +
.../include/__thread/timed_backoff_policy.h | 1 +
libcxx/include/atomic | 1 +
libcxx/include/module.modulemap.in | 1 +
libcxx/include/semaphore | 8 +-
libcxx/src/atomic.cpp | 87 ++++++++---
9 files changed, 219 insertions(+), 25 deletions(-)
create mode 100644 libcxx/include/__atomic/atomic_sync_timed.h
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index f9ae22accd687..787cfffa50195 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -214,6 +214,7 @@ set(files
__atomic/atomic_lock_free.h
__atomic/atomic_ref.h
__atomic/atomic_sync.h
+ __atomic/atomic_sync_timed.h
__atomic/atomic_waitable_traits.h
__atomic/check_memory_order.h
__atomic/contention_t.h
diff --git a/libcxx/include/__atomic/atomic_sync_timed.h b/libcxx/include/__atomic/atomic_sync_timed.h
new file mode 100644
index 0000000000000..ca7502b64c304
--- /dev/null
+++ b/libcxx/include/__atomic/atomic_sync_timed.h
@@ -0,0 +1,143 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___ATOMIC_ATOMIC_SYNC_TIMED_H
+#define _LIBCPP___ATOMIC_ATOMIC_SYNC_TIMED_H
+
+#include <__atomic/atomic_waitable_traits.h>
+#include <__atomic/contention_t.h>
+#include <__atomic/memory_order.h>
+#include <__atomic/to_gcc_order.h>
+#include <__chrono/duration.h>
+#include <__config>
+#include <__memory/addressof.h>
+#include <__thread/poll_with_backoff.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/void_t.h>
+#include <__utility/declval.h>
+#include <cstdint>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20
+# if _LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_NEW_SYNC
+
+_LIBCPP_AVAILABILITY_NEW_SYNC
+ _LIBCPP_EXPORTED_FROM_ABI __cxx_contention_t __atomic_monitor_global(void const* __address) _NOEXCEPT;
+
+// wait on the global contention state to be changed from the given value for the address
+_LIBCPP_AVAILABILITY_NEW_SYNC _LIBCPP_EXPORTED_FROM_ABI void __atomic_wait_global_table_with_timeout(
+ void const* __address, __cxx_contention_t __monitor_value, uint64_t __timeout_ns) _NOEXCEPT;
+
+// wait on the address directly with the native platform wait
+template <std::size_t _Size>
+_LIBCPP_AVAILABILITY_NEW_SYNC _LIBCPP_EXPORTED_FROM_ABI void
+__atomic_wait_native_with_timeout(void const* __address, void const* __old_value, uint64_t __timeout_ns) _NOEXCEPT;
+
+template <class _AtomicWaitable, class _Poll, class _Rep, class _Period>
+struct __atomic_wait_timed_backoff_impl {
+ const _AtomicWaitable& __a_;
+ _Poll __poll_;
+ memory_order __order_;
+ chrono::duration<_Rep, _Period> __rel_time_;
+
+ using __waitable_traits _LIBCPP_NODEBUG = __atomic_waitable_traits<__decay_t<_AtomicWaitable> >;
+ using __value_type _LIBCPP_NODEBUG = typename __waitable_traits::__value_type;
+
+ _LIBCPP_HIDE_FROM_ABI __backoff_results operator()(chrono::nanoseconds __elapsed) const {
+ if (__elapsed > chrono::microseconds(4)) {
+ auto __contention_address = const_cast<const void*>(
+ static_cast<const volatile void*>(__waitable_traits::__atomic_contention_address(__a_)));
+
+ uint64_t __timeout_ns =
+ static_cast<uint64_t>((chrono::duration_cast<chrono::nanoseconds>(__rel_time_) - __elapsed).count());
+
+ if constexpr (__has_native_atomic_wait<__value_type>) {
+ auto __atomic_value = __waitable_traits::__atomic_load(__a_, __order_);
+ if (__poll_(__atomic_value))
+ return __backoff_results::__poll_success;
+ std::__atomic_wait_native_with_timeout<sizeof(__value_type)>(
+ __contention_address, std::addressof(__atomic_value), __timeout_ns);
+ } else {
+ __cxx_contention_t __monitor_val = std::__atomic_monitor_global(__contention_address);
+ auto __atomic_value = __waitable_traits::__atomic_load(__a_, __order_);
+ if (__poll_(__atomic_value))
+ return __backoff_results::__poll_success;
+ std::__atomic_wait_global_table_with_timeout(__contention_address, __monitor_val, __timeout_ns);
+ }
+ } else {
+ } // poll
+ return __backoff_results::__continue_poll;
+ }
+};
+
+// The semantics of this function are similar to `atomic`'s
+// `.wait(T old, std::memory_order order)`, but instead of having a hardcoded
+// predicate (is the loaded value unequal to `old`?), the predicate function is
+// specified as an argument. The loaded value is given as an in-out argument to
+// the predicate. If the predicate function returns `true`,
+// `__atomic_wait_unless` will return. If the predicate function returns
+// `false`, it must set the argument to its current understanding of the atomic
+// value. The predicate function must not return `false` spuriously.
+template <class _AtomicWaitable, class _Poll, class _Rep, class _Period>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_wait_unless_with_timeout(
+ const _AtomicWaitable& __a,
+ memory_order __order,
+ _Poll&& __poll,
+ chrono::duration<_Rep, _Period> const& __rel_time) {
+ static_assert(__atomic_waitable<_AtomicWaitable>::value, "");
+ __atomic_wait_timed_backoff_impl<_AtomicWaitable, __decay_t<_Poll>, _Rep, _Period> __backoff_fn = {
+ __a, __poll, __order, __rel_time};
+ auto __poll_result = std::__libcpp_thread_poll_with_backoff(
+ /* poll */
+ [&]() {
+ auto __current_val = __atomic_waitable_traits<__decay_t<_AtomicWaitable> >::__atomic_load(__a, __order);
+ return __poll(__current_val);
+ },
+ /* backoff */ __backoff_fn,
+ __rel_time);
+
+ return __poll_result == __poll_with_backoff_results::__poll_success;
+}
+
+# else // _LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_NEW_SYNC
+
+template <class _AtomicWaitable, class _Poll, class _Rep, class _Period>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_wait_unless_with_timeout(
+ const _AtomicWaitable& __a,
+ memory_order __order,
+ _Poll&& __poll,
+ chrono::duration<_Rep, _Period> const& __rel_time) {
+ auto __res = std::__libcpp_thread_poll_with_backoff(
+ /* poll */
+ [&]() {
+ auto __current_val = __atomic_waitable_traits<__decay_t<_AtomicWaitable> >::__atomic_load(__a, __order);
+ return __poll(__current_val);
+ },
+ /* backoff */ __libcpp_timed_backoff_policy(),
+ __rel_time);
+ return __res == __poll_with_backoff_results::__poll_success;
+}
+
+# endif // _LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_NEW_SYNC
+
+#endif // C++20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_ATOMIC_SYNC_TIMED_H
diff --git a/libcxx/include/__atomic/atomic_waitable_traits.h b/libcxx/include/__atomic/atomic_waitable_traits.h
index de06fe70b3e1a..fda2780842911 100644
--- a/libcxx/include/__atomic/atomic_waitable_traits.h
+++ b/libcxx/include/__atomic/atomic_waitable_traits.h
@@ -5,7 +5,6 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-
#ifndef _LIBCPP___ATOMIC_ATOMIC_WAITABLE_TRAITS_H
#define _LIBCPP___ATOMIC_ATOMIC_WAITABLE_TRAITS_H
diff --git a/libcxx/include/__thread/poll_with_backoff.h b/libcxx/include/__thread/poll_with_backoff.h
index e007e7746ca52..97b7374779924 100644
--- a/libcxx/include/__thread/poll_with_backoff.h
+++ b/libcxx/include/__thread/poll_with_backoff.h
@@ -13,6 +13,7 @@
#include <__chrono/duration.h>
#include <__chrono/high_resolution_clock.h>
#include <__config>
+#include <cstdint>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
diff --git a/libcxx/include/__thread/timed_backoff_policy.h b/libcxx/include/__thread/timed_backoff_policy.h
index 01fe2dd045e58..3ef90bede719d 100644
--- a/libcxx/include/__thread/timed_backoff_policy.h
+++ b/libcxx/include/__thread/timed_backoff_policy.h
@@ -10,6 +10,7 @@
#ifndef _LIBCPP___THREAD_TIMED_BACKOFF_POLICY_H
#define _LIBCPP___THREAD_TIMED_BACKOFF_POLICY_H
+#include "__thread/poll_with_backoff.h"
#include <__config>
#include <__thread/poll_with_backoff.h>
diff --git a/libcxx/include/atomic b/libcxx/include/atomic
index 3323650f7f81a..23a3db5d35029 100644
--- a/libcxx/include/atomic
+++ b/libcxx/include/atomic
@@ -608,6 +608,7 @@ template <class T>
# include <__atomic/atomic_init.h>
# include <__atomic/atomic_lock_free.h>
# include <__atomic/atomic_sync.h>
+# include <__atomic/atomic_sync_timed.h>
# include <__atomic/atomic_waitable_traits.h>
# include <__atomic/check_memory_order.h>
# include <__atomic/contention_t.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 27178aa1f4378..6bee3809d7da5 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -869,6 +869,7 @@ module std [system] {
module atomic_lock_free { header "__atomic/atomic_lock_free.h" }
module atomic_ref { header "__atomic/atomic_ref.h" }
module atomic_sync { header "__atomic/atomic_sync.h" }
+ module atomic_sync_timed { header "__atomic/atomic_sync_timed.h" }
module atomic_waitable_traits { header "__atomic/atomic_waitable_traits.h" }
module atomic {
header "__atomic/atomic.h"
diff --git a/libcxx/include/semaphore b/libcxx/include/semaphore
index 7c1637356bfe5..f93f8243bb15a 100644
--- a/libcxx/include/semaphore
+++ b/libcxx/include/semaphore
@@ -55,6 +55,7 @@ using binary_semaphore = counting_semaphore<1>; // since C++20
# include <__assert>
# include <__atomic/atomic.h>
# include <__atomic/atomic_sync.h>
+# include <__atomic/atomic_sync_timed.h>
# include <__atomic/memory_order.h>
# include <__chrono/time_point.h>
# include <__cstddef/ptrdiff_t.h>
@@ -107,9 +108,10 @@ public:
_LIBCPP_HIDE_FROM_ABI bool try_acquire_for(chrono::duration<_Rep, _Period> const& __rel_time) {
if (__rel_time == chrono::duration<_Rep, _Period>::zero())
return try_acquire();
- auto const __poll_fn = [this]() { return try_acquire(); };
- return std::__libcpp_thread_poll_with_backoff(__poll_fn, __libcpp_timed_backoff_policy(), __rel_time) ==
- __poll_with_backoff_results::__poll_success;
+
+ return std::__atomic_wait_unless_with_timeout(__a_, memory_order_relaxed, [this](ptrdiff_t& __old) {
+ return __try_acquire_impl(__old);
+ }, __rel_time);
}
_LIBCPP_HIDE_FROM_ABI bool try_acquire() {
auto __old = __a_.load(memory_order_relaxed);
diff --git a/libcxx/src/atomic.cpp b/libcxx/src/atomic.cpp
index 33ca5410923b6..19101a95d0c97 100644
--- a/libcxx/src/atomic.cpp
+++ b/libcxx/src/atomic.cpp
@@ -10,6 +10,7 @@
#include <atomic>
#include <climits>
#include <cstddef>
+#include <cstdint>
#include <cstring>
#include <functional>
#include <new>
@@ -63,11 +64,18 @@ _LIBCPP_BEGIN_NAMESPACE_STD
#ifdef __linux__
template <std::size_t _Size>
-static void __platform_wait_on_address(void const* __ptr, void const* __val) {
+static void __platform_wait_on_address(void const* __ptr, void const* __val, uint64_t __timeout_ns) {
static_assert(_Size == 4, "Can only wait on 4 bytes value");
char buffer[_Size];
std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
- static constexpr timespec __timeout = {2, 0};
+ static constexpr timespec __default_timeout = {2, 0};
+ timespec __timeout;
+ if (__timeout_ns == 0) {
+ __timeout = __default_timeout;
+ } else {
+ __timeout.tv_sec = __timeout_ns / 1'000'000'000;
+ __timeout.tv_nsec = __timeout_ns % 1'000'000'000;
+ }
_LIBCPP_FUTEX(__ptr, FUTEX_WAIT_PRIVATE, *reinterpret_cast<__cxx_contention_t const*>(&buffer), &__timeout, 0, 0);
}
@@ -89,14 +97,17 @@ extern "C" int __ulock_wake(uint32_t operation, void* addr, uint64_t wake_value)
# define ULF_WAKE_ALL 0x00000100
template <std::size_t _Size>
-static void __platform_wait_on_address(void const* __ptr, void const* __val) {
+static void __platform_wait_on_address(void const* __ptr, void const* __val, uint64_t __timeout_ns) {
static_assert(_Size == 8 || _Size == 4, "Can only wait on 8 bytes or 4 bytes value");
char buffer[_Size];
std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
+ auto __timeout_us = __timeout_ns == 0 ? 0 : static_cast<uint32_t>(__timeout_ns / 1000);
if constexpr (_Size == 4)
- __ulock_wait(UL_COMPARE_AND_WAIT, const_cast<void*>(__ptr), *reinterpret_cast<uint32_t const*>(&buffer), 0);
+ __ulock_wait(
+ UL_COMPARE_AND_WAIT, const_cast<void*>(__ptr), *reinterpret_cast<uint32_t const*>(&buffer), __timeout_us);
else
- __ulock_wait(UL_COMPARE_AND_WAIT64, const_cast<void*>(__ptr), *reinterpret_cast<uint64_t const*>(&buffer), 0);
+ __ulock_wait(
+ UL_COMPARE_AND_WAIT64, const_cast<void*>(__ptr), *reinterpret_cast<uint64_t const*>(&buffer), __timeout_us);
}
template <std::size_t _Size>
@@ -117,11 +128,20 @@ static void __platform_wake_by_address(void const* __ptr, bool __notify_one) {
*/
template <std::size_t _Size>
-static void __platform_wait_on_address(void const* __ptr, void const* __val) {
+static void __platform_wait_on_address(void const* __ptr, void const* __val, uint64_t __timeout_ns) {
static_assert(_Size == 8, "Can only wait on 8 bytes value");
- char buffer[_Size];
- std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
- _umtx_op(const_cast<void*>(__ptr), UMTX_OP_WAIT, *reinterpret_cast<__cxx_contention_t*>(&buffer), nullptr, nullptr);
+ if (__timeout_ns == 0) {
+ char buffer[_Size];
+ std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
+ _umtx_op(const_cast<void*>(__ptr), UMTX_OP_WAIT, *reinterpret_cast<__cxx_contention_t*>(&buffer), nullptr, nullptr);
+ } else {
+ // TODO the doc says it supports timeout but does not say how to use it
+ // https://man.freebsd.org/cgi/man.cgi?query=_umtx_op
+ __libcpp_thread_poll_with_backoff(
+ [=]() -> bool { return std::memcmp(const_cast<const void*>(__ptr), __val, _Size) != 0; },
+ __libcpp_timed_backoff_policy(),
+ std::chrono::nanoseconds(__timeout_ns));
+ }
}
template <std::size_t _Size>
@@ -157,17 +177,21 @@ static void* win32_get_synch_api_function(const char* function_name) {
}
template <std::size_t _Size>
-static void __platform_wait_on_address(void const* __ptr, void const* __val) {
+static void __platform_wait_on_address(void const* __ptr, void const* __val, uint64_t __timeout_ns) {
static_assert(_Size == 8, "Can only wait on 8 bytes value");
// WaitOnAddress was added in Windows 8 (build 9200)
static auto wait_on_address =
reinterpret_cast<BOOL(WINAPI*)(void*, PVOID, SIZE_T, DWORD)>(win32_get_synch_api_function("WaitOnAddress"));
if (wait_on_address != nullptr) {
- wait_on_address(const_cast<void*>(__ptr), const_cast<void*>(__val), _Size, INFINITE);
+ wait_on_address(const_cast<void*>(__ptr),
+ const_cast<void*>(__val),
+ _Size,
+ __timeout_ns == 0 ? INFINITE : static_cast<DWORD>(__timeout_ns / 1'000'000));
} else {
__libcpp_thread_poll_with_backoff(
[=]() -> bool { return std::memcmp(const_cast<const void*>(__ptr), __val, _Size) != 0; },
- __libcpp_timed_backoff_policy());
+ __libcpp_timed_backoff_policy(),
+ std::chrono::nanoseconds(__timeout_ns));
}
}
@@ -202,10 +226,11 @@ static void __platform_wake_by_address(void const* __ptr, bool __notify_one) {
// Baseline is just a timed backoff
template <std::size_t _Size>
-static void __platform_wait_on_address(void const* __ptr, void const* __val) {
+static void __platform_wait_on_address(void const* __ptr, void const* __val, uint64_t __timeout_ns) {
__libcpp_thread_poll_with_backoff(
[=]() -> bool { return std::memcmp(const_cast<const void*>(__ptr), __val, _Size) != 0; },
- __libcpp_timed_backoff_policy());
+ __libcpp_timed_backoff_policy(),
+ std::chrono::nanoseconds(__timeout_ns));
}
template <std::size_t _Size>
@@ -229,14 +254,16 @@ __contention_notify(__cxx_atomic_contention_t* __waiter_count, void const* __add
}
template <std::size_t _Size>
-static void
-__contention_wait(__cxx_atomic_contention_t* __waiter_count, void const* __address_to_wait, void const* __old_value) {
+static void __contention_wait(__cxx_atomic_contention_t* __waiter_count,
+ void const* __address_to_wait,
+ void const* __old_value,
+ uint64_t __timeout_ns) {
__cxx_atomic_fetch_add(__waiter_count, __cxx_contention_t(1), memory_order_relaxed);
// https://llvm.org/PR109290
// There are no platform guarantees of a memory barrier in the platform wait implementation
__cxx_atomic_thread_fence(memory_order_seq_cst);
// We sleep as long as the monitored value hasn't changed.
- __platform_wait_on_address<_Size>(__address_to_wait, __old_value);
+ __platform_wait_on_address<_Size>(__address_to_wait, __old_value, __timeout_ns);
__cxx_atomic_fetch_sub(__waiter_count, __cxx_contention_t(1), memory_order_release);
}
@@ -299,7 +326,14 @@ _LIBCPP_EXPORTED_FROM_ABI void
__atomic_wait_global_table(void const* __location, __cxx_contention_t __old_value) noexcept {
auto const __entry = __get_global_contention_state(__location);
__contention_wait<sizeof(__cxx_atomic_contention_t)>(
- &__entry->__waiter_count, &__entry->__platform_state, &__old_value);
+ &__entry->__waiter_count, &__entry->__platform_state, &__old_value, 0);
+}
+
+_LIBCPP_AVAILABILITY_NEW_SYNC _LIBCPP_EXPORTED_FROM_ABI void __atomic_wait_global_table_with_timeout(
+ void const* __location, __cxx_contention_t __old_value, uint64_t __timeout_ns) _NOEXCEPT {
+ auto const __entry = __get_global_contention_state(__location);
+ __contention_wait<sizeof(__cxx_atomic_contention_t)>(
+ &__entry->__waiter_count, &__entry->__platform_state, &__old_value, __timeout_ns);
}
_LIBCPP_EXPORTED_FROM_ABI void __atomic_notify_one_global_table(void const* __location) noexcept {
@@ -314,7 +348,13 @@ _LIBCPP_EXPORTED_FROM_ABI void __atomic_notify_all_global_table(void const* __lo
template <std::size_t _Size>
_LIBCPP_EXPORTED_FROM_ABI void __atomic_wait_native(void const* __address, void const* __old_value) noexcept {
- __contention_wait<_Size>(__get_native_waiter_count(__address), __address, __old_value);
+ __contention_wait<_Size>(__get_native_waiter_count(__address), __address, __old_value, 0);
+}
+
+template <std::size_t _Size>
+_LIBCPP_EXPORTED_FROM_ABI void
+__atomic_wait_native_with_timeout(void const* __address, void const* __old_value, uint64_t __timeout_ns) noexcept {
+ __contention_wait<_Size>(__get_native_waiter_count(__address), __address, __old_value, __timeout_ns);
}
template <std::size_t _Size>
@@ -335,6 +375,8 @@ _LIBCPP_EXPORTED_FROM_ABI void __atomic_notify_all_native(void const* __location
# define _INSTANTIATE(_SIZE) \
template _LIBCPP_EXPORTED_FROM_ABI void __atomic_wait_native<_SIZE>(void const*, void const*) noexcept; \
+ template _LIBCPP_EXPORTED_FROM_ABI void __atomic_wait_native_with_timeout<_SIZE>( \
+ void const*, void const*, uint64_t) noexcept; \
template _LIBCPP_EXPORTED_FROM_ABI void __atomic_notify_one_native<_SIZE>(void const*) noexcept; \
template _LIBCPP_EXPORTED_FROM_ABI void __atomic_notify_all_native<_SIZE>(void const*) noexcept;
@@ -347,6 +389,9 @@ _LIBCPP_NATIVE_PLATFORM_WAIT_SIZES(_INSTANTIATE)
template _LIBCPP_EXPORTED_FROM_ABI void
__atomic_wait_native<sizeof(__cxx_contention_t)>(void const* __address, void const* __old_value) noexcept;
+template _LIBCPP_EXPORTED_FROM_ABI void __atomic_wait_native_with_timeout<sizeof(__cxx_contention_t)>(
+ void const* __address, void const* __old_value, uint64_t) noexcept;
+
template _LIBCPP_EXPORTED_FROM_ABI void
__atomic_notify_one_native<sizeof(__cxx_contention_t)>(void const* __location) noexcept;
@@ -378,7 +423,7 @@ _LIBCPP_EXPORTED_FROM_ABI void
__libcpp_atomic_wait(void const volatile* __location, __cxx_contention_t __old_value) noexcept {
auto const __entry = __get_global_contention_state(const_cast<void const*>(__location));
__contention_wait<sizeof(__cxx_atomic_contention_t)>(
- &__entry->__waiter_count, &__entry->__platform_state, &__old_value);
+ &__entry->__waiter_count, &__entry->__platform_state, &__old_value, 0);
}
_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_one(__cxx_atomic_contention_t const volatile* __location) noexcept {
@@ -397,7 +442,7 @@ _LIBCPP_EXPORTED_FROM_ABI void
__libcpp_atomic_wait(__cxx_atomic_contention_t const volatile* __location, __cxx_contention_t __old_value) noexcept {
auto __location_cast = const_cast<const void*>(static_cast<const volatile void*>(__location));
__contention_wait<sizeof(__cxx_atomic_contention_t)>(
- __get_native_waiter_count(__location_cast), __location_cast, &__old_value);
+ __get_native_waiter_count(__location_cast), __location_cast, &__old_value, 0);
}
// this function is even unused in the old ABI
>From 8d87261d56ff86e4f015632017c6cc36ccc57abb Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 20 Dec 2025 13:35:12 +0000
Subject: [PATCH 2/3] test
---
libcxx/test/benchmarks/semaphore.bench.cpp | 23 ++++++++
.../lost_wakeup.timed.pass.cpp | 53 +++++++++++++++++++
2 files changed, 76 insertions(+)
create mode 100644 libcxx/test/benchmarks/semaphore.bench.cpp
create mode 100644 libcxx/test/std/thread/thread.semaphore/lost_wakeup.timed.pass.cpp
diff --git a/libcxx/test/benchmarks/semaphore.bench.cpp b/libcxx/test/benchmarks/semaphore.bench.cpp
new file mode 100644
index 0000000000000..f8c03d8299413
--- /dev/null
+++ b/libcxx/test/benchmarks/semaphore.bench.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// ADDITIONAL_COMPILE_FLAGS: -O3
+
+#include <cstdint>
+#include <optional>
+#include <semaphore>
+#include <thread>
+
+#include "benchmark/benchmark.h"
+#include "make_test_thread.h"
+
+
+
+void BM_semaphore_timed_acquire(benchmark::State& state) {
+}
\ No newline at end of file
diff --git a/libcxx/test/std/thread/thread.semaphore/lost_wakeup.timed.pass.cpp b/libcxx/test/std/thread/thread.semaphore/lost_wakeup.timed.pass.cpp
new file mode 100644
index 0000000000000..85a8fbf1ff288
--- /dev/null
+++ b/libcxx/test/std/thread/thread.semaphore/lost_wakeup.timed.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// ADDITIONAL_COMPILE_FLAGS: -O3
+
+// <semaphore>
+
+#include <barrier>
+#include <chrono>
+#include <semaphore>
+#include <thread>
+#include <vector>
+
+#include "make_test_thread.h"
+
+static std::counting_semaphore<> s(0);
+constexpr auto num_acquirer = 100;
+constexpr auto num_iterations = 50000;
+static std::barrier<> b(num_acquirer + 1);
+
+void acquire() {
+ for (int i = 0; i < num_iterations; ++i) {
+ while (!s.try_acquire_for(std::chrono::seconds(1))) {
+ }
+ }
+}
+
+void release() {
+ for (int i = 0; i < num_iterations; ++i) {
+ s.release(num_acquirer);
+ }
+}
+
+int main(int, char**) {
+ std::vector<std::thread> threads;
+ for (int i = 0; i < num_acquirer; ++i)
+ threads.push_back(support::make_test_thread(acquire));
+
+ threads.push_back(support::make_test_thread(release));
+
+ for (auto& thread : threads)
+ thread.join();
+
+ return 0;
+}
>From 61e120a4f8168de995bc8ba0df547942020edb75 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 7 Jan 2026 21:15:05 +0000
Subject: [PATCH 3/3] rebase
---
libcxx/include/__atomic/atomic_sync_timed.h | 6 +++---
libcxx/include/__atomic/atomic_waitable_traits.h | 1 +
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/libcxx/include/__atomic/atomic_sync_timed.h b/libcxx/include/__atomic/atomic_sync_timed.h
index ca7502b64c304..3b1db1f86320c 100644
--- a/libcxx/include/__atomic/atomic_sync_timed.h
+++ b/libcxx/include/__atomic/atomic_sync_timed.h
@@ -87,11 +87,11 @@ struct __atomic_wait_timed_backoff_impl {
};
// The semantics of this function are similar to `atomic`'s
-// `.wait(T old, std::memory_order order)`, but instead of having a hardcoded
+// `.wait(T old, std::memory_order order)` with a timeout, but instead of having a hardcoded
// predicate (is the loaded value unequal to `old`?), the predicate function is
// specified as an argument. The loaded value is given as an in-out argument to
// the predicate. If the predicate function returns `true`,
-// `__atomic_wait_unless` will return. If the predicate function returns
+// `__atomic_wait_unless_with_timeout` will return. If the predicate function returns
// `false`, it must set the argument to its current understanding of the atomic
// value. The predicate function must not return `false` spuriously.
template <class _AtomicWaitable, class _Poll, class _Rep, class _Period>
@@ -100,7 +100,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_wait_unless_with_timeout(
memory_order __order,
_Poll&& __poll,
chrono::duration<_Rep, _Period> const& __rel_time) {
- static_assert(__atomic_waitable<_AtomicWaitable>::value, "");
+ static_assert(__atomic_waitable<_AtomicWaitable>, "");
__atomic_wait_timed_backoff_impl<_AtomicWaitable, __decay_t<_Poll>, _Rep, _Period> __backoff_fn = {
__a, __poll, __order, __rel_time};
auto __poll_result = std::__libcpp_thread_poll_with_backoff(
diff --git a/libcxx/include/__atomic/atomic_waitable_traits.h b/libcxx/include/__atomic/atomic_waitable_traits.h
index fda2780842911..de06fe70b3e1a 100644
--- a/libcxx/include/__atomic/atomic_waitable_traits.h
+++ b/libcxx/include/__atomic/atomic_waitable_traits.h
@@ -5,6 +5,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+
#ifndef _LIBCPP___ATOMIC_ATOMIC_WAITABLE_TRAITS_H
#define _LIBCPP___ATOMIC_ATOMIC_WAITABLE_TRAITS_H
More information about the libcxx-commits
mailing list