[libcxx-commits] [libcxx] [libc++] Fix semaphore timed wait hanging on Windows (PR #180398)
via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Feb 13 05:05:30 PST 2026
https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/180398
>From d62f2ea0952a9fd8a7ae6c1a5463013ebb622b22 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 7 Feb 2026 16:19:16 +0000
Subject: [PATCH 01/14] [libc++] debug semaaphore
---
.../thread.semaphore/timed.debug.pass.cpp | 84 +++++++++++++++++++
1 file changed, 84 insertions(+)
create mode 100644 libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp
diff --git a/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp b/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp
new file mode 100644
index 0000000000000..d7833194927e4
--- /dev/null
+++ b/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <semaphore>
+
+#include <semaphore>
+#include <thread>
+#include <chrono>
+#include <cassert>
+#include <iostream>
+#include <iomanip>
+
+
+#include "make_test_thread.h"
+#include "test_macros.h"
+
+int main(int, char**)
+{
+ auto const start = std::chrono::steady_clock::now();
+ auto log = [start] ()-> auto& {
+ using namespace std::chrono;
+
+ auto elapsed = steady_clock::now() - start;
+
+ auto hours = duration_cast<std::chrono::hours>(elapsed);
+ elapsed -= hours;
+
+ auto minutes = duration_cast<std::chrono::minutes>(elapsed);
+ elapsed -= minutes;
+
+ auto seconds = duration_cast<std::chrono::seconds>(elapsed);
+ elapsed -= seconds;
+
+ auto milliseconds = duration_cast<std::chrono::milliseconds>(elapsed);
+
+ std::cerr << "["
+ << std::setw(2) << std::setfill('0') << hours.count() << ":"
+ << std::setw(2) << std::setfill('0') << minutes.count() << ":"
+ << std::setw(2) << std::setfill('0') << seconds.count() << "."
+ << std::setw(3) << std::setfill('0') << milliseconds.count()
+ << "] ";
+
+ return std::cerr;
+ };
+
+ std::counting_semaphore<> s(0);
+
+ log() << "try_acquire_until: start + " << std::chrono::milliseconds(250) << "\n";
+ assert(!s.try_acquire_until(start + std::chrono::milliseconds(250)));
+ log() << "done: try_acquire_until: start + " << std::chrono::milliseconds(250) << "\n";
+
+ log() << "try_acquire_for: " << std::chrono::milliseconds(250) << "\n";
+ assert(!s.try_acquire_for(std::chrono::milliseconds(250)));
+ log() << "done: try_acquire_for: " << std::chrono::milliseconds(250) << "\n";
+
+ std::thread t = support::make_test_thread([&](){
+ std::this_thread::sleep_for(std::chrono::milliseconds(250));
+ s.release();
+ std::this_thread::sleep_for(std::chrono::milliseconds(250));
+ s.release();
+ });
+
+ log() << "try_acquire_until: start + " << std::chrono::seconds(2) << "\n";
+ assert(s.try_acquire_until(start + std::chrono::seconds(2)));
+ log() << "done: try_acquire_until: start + " << std::chrono::seconds(2) << "\n";
+
+ log() << "try_acquire_for: " << std::chrono::seconds(2) << "\n";
+ assert(s.try_acquire_for(std::chrono::seconds(2)));
+ log() << "done: try_acquire_for: " << std::chrono::seconds(2) << "\n";
+ t.join();
+
+ auto const end = std::chrono::steady_clock::now();
+ assert(end - start < std::chrono::seconds(10));
+
+ return 1;
+}
>From 41530db0bb27df86b324e3c0c2e490fb38e748d3 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 7 Feb 2026 16:23:48 +0000
Subject: [PATCH 02/14] code to debug
---
.../thread.semaphore/timed.debug.pass.cpp | 34 ++++++++++++-------
1 file changed, 21 insertions(+), 13 deletions(-)
diff --git a/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp b/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp
index d7833194927e4..904da25a49940 100644
--- a/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp
+++ b/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp
@@ -22,13 +22,11 @@
#include "make_test_thread.h"
#include "test_macros.h"
-int main(int, char**)
-{
- auto const start = std::chrono::steady_clock::now();
- auto log = [start] ()-> auto& {
+void test(auto log_start){
+ auto log = [log_start] ()-> auto& {
using namespace std::chrono;
- auto elapsed = steady_clock::now() - start;
+ auto elapsed = steady_clock::now() - log_start;
auto hours = duration_cast<std::chrono::hours>(elapsed);
elapsed -= hours;
@@ -51,15 +49,16 @@ int main(int, char**)
return std::cerr;
};
+ auto const start = std::chrono::steady_clock::now();
std::counting_semaphore<> s(0);
- log() << "try_acquire_until: start + " << std::chrono::milliseconds(250) << "\n";
+ log() << "start: try_acquire_until: start + " << std::chrono::milliseconds(250) << "\n";
assert(!s.try_acquire_until(start + std::chrono::milliseconds(250)));
- log() << "done: try_acquire_until: start + " << std::chrono::milliseconds(250) << "\n";
+ log() << "done: try_acquire_until: start + " << std::chrono::milliseconds(250) << "\n";
- log() << "try_acquire_for: " << std::chrono::milliseconds(250) << "\n";
+ log() << "start: try_acquire_for: " << std::chrono::milliseconds(250) << "\n";
assert(!s.try_acquire_for(std::chrono::milliseconds(250)));
- log() << "done: try_acquire_for: " << std::chrono::milliseconds(250) << "\n";
+ log() << "done: try_acquire_for: " << std::chrono::milliseconds(250) << "\n";
std::thread t = support::make_test_thread([&](){
std::this_thread::sleep_for(std::chrono::milliseconds(250));
@@ -68,17 +67,26 @@ int main(int, char**)
s.release();
});
- log() << "try_acquire_until: start + " << std::chrono::seconds(2) << "\n";
+ log() << "start: try_acquire_until: start + " << std::chrono::seconds(2) << "\n";
assert(s.try_acquire_until(start + std::chrono::seconds(2)));
- log() << "done: try_acquire_until: start + " << std::chrono::seconds(2) << "\n";
+ log() << "done: try_acquire_until: start + " << std::chrono::seconds(2) << "\n";
- log() << "try_acquire_for: " << std::chrono::seconds(2) << "\n";
+ log() << "start: try_acquire_for: " << std::chrono::seconds(2) << "\n";
assert(s.try_acquire_for(std::chrono::seconds(2)));
- log() << "done: try_acquire_for: " << std::chrono::seconds(2) << "\n";
+ log() << "done: try_acquire_for: " << std::chrono::seconds(2) << "\n";
t.join();
auto const end = std::chrono::steady_clock::now();
assert(end - start < std::chrono::seconds(10));
+}
+
+int main(int, char**)
+{
+ auto const log_start = std::chrono::steady_clock::now();
+ for (auto i = 0; i < 10; ++i) {
+ std::cerr << "=== Iteration " << i << " ===\n";
+ test(log_start);
+ }
return 1;
}
>From 2864a848f4716767852e3c2c4e7a5aba35498efb Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 7 Feb 2026 16:44:11 +0000
Subject: [PATCH 03/14] stage3
---
libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp b/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp
index 904da25a49940..d032145f9824d 100644
--- a/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp
+++ b/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp
@@ -88,5 +88,10 @@ int main(int, char**)
test(log_start);
}
+#if defined(_WIN32)
return 1;
+#else
+ return 0;
+#endif
+
}
>From a37266255f5ff9a05fc6347f3808d37c4887f499 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 7 Feb 2026 17:11:07 +0000
Subject: [PATCH 04/14] debug more
---
libcxx/src/atomic.cpp | 39 ++++++++++---------
.../thread/thread.semaphore/timed.pass.cpp | 5 +--
2 files changed, 22 insertions(+), 22 deletions(-)
diff --git a/libcxx/src/atomic.cpp b/libcxx/src/atomic.cpp
index 1277b20caf641..57b90838b45f6 100644
--- a/libcxx/src/atomic.cpp
+++ b/libcxx/src/atomic.cpp
@@ -14,6 +14,7 @@
#include <cstring>
#include <functional>
#include <new>
+#include <optional>
#include <thread>
#include <type_traits>
@@ -64,17 +65,17 @@ _LIBCPP_BEGIN_NAMESPACE_STD
#ifdef __linux__
template <std::size_t _Size>
-static void __platform_wait_on_address(void const* __ptr, void const* __val, uint64_t __timeout_ns) {
+static void __platform_wait_on_address(void const* __ptr, void const* __val, optional<uint64_t> __timeout_ns) {
static_assert(_Size == 4, "Can only wait on 4 bytes value");
alignas(__cxx_contention_t) char buffer[_Size];
std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
static constexpr timespec __default_timeout = {2, 0};
timespec __timeout;
- if (__timeout_ns == 0) {
+ if (!__timeout_ns.has_value()) {
__timeout = __default_timeout;
} else {
- __timeout.tv_sec = __timeout_ns / 1'000'000'000;
- __timeout.tv_nsec = __timeout_ns % 1'000'000'000;
+ __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);
}
@@ -97,9 +98,9 @@ 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, uint64_t __timeout_ns) {
+static void __platform_wait_on_address(void const* __ptr, void const* __val, optional<uint64_t> __timeout_ns) {
static_assert(_Size == 8 || _Size == 4, "Can only wait on 8 bytes or 4 bytes value");
- auto __timeout_us = __timeout_ns == 0 ? 0 : static_cast<uint32_t>(__timeout_ns / 1000);
+ auto __timeout_us = !__timeout_ns.has_value() ? uint32_t(0) : std::max(static_cast<uint32_t>(*__timeout_ns / 1000), uint32_t(1));
if constexpr (_Size == 4) {
alignas(uint32_t) char buffer[_Size];
std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
@@ -131,16 +132,16 @@ 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, uint64_t __timeout_ns) {
+static void __platform_wait_on_address(void const* __ptr, void const* __val, optional<uint64_t> __timeout_ns) {
static_assert(_Size == 8, "Can only wait on 8 bytes value");
alignas(__cxx_contention_t) char buffer[_Size];
std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
- if (__timeout_ns == 0) {
+ if (!__timeout_ns.has_value()) {
_umtx_op(const_cast<void*>(__ptr), UMTX_OP_WAIT, *reinterpret_cast<__cxx_contention_t*>(&buffer), nullptr, nullptr);
} else {
_umtx_time ut;
- ut._timeout.tv_sec = __timeout_ns / 1'000'000'000;
- ut._timeout.tv_nsec = __timeout_ns % 1'000'000'000;
+ ut._timeout.tv_sec = *__timeout_ns / 1'000'000'000;
+ ut._timeout.tv_nsec = *__timeout_ns % 1'000'000'000;
ut._flags = 0; // Relative time (not absolute)
ut._clockid = CLOCK_MONOTONIC; // Use monotonic clock
@@ -185,7 +186,7 @@ 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, uint64_t __timeout_ns) {
+static void __platform_wait_on_address(void const* __ptr, void const* __val, optional<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 =
@@ -194,12 +195,12 @@ static void __platform_wait_on_address(void const* __ptr, void const* __val, uin
wait_on_address(const_cast<void*>(__ptr),
const_cast<void*>(__val),
_Size,
- __timeout_ns == 0 ? INFINITE : static_cast<DWORD>(__timeout_ns / 1'000'000));
+ !__timeout_ns.has_value() ? 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(),
- std::chrono::nanoseconds(__timeout_ns));
+ std::chrono::nanoseconds(__timeout_ns.value_or(0)));
}
}
@@ -234,7 +235,7 @@ 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, uint64_t __timeout_ns) {
+static void __platform_wait_on_address(void const* __ptr, void const* __val, optional<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(),
@@ -265,7 +266,7 @@ template <std::size_t _Size>
static void __contention_wait(__cxx_atomic_contention_t* __waiter_count,
void const* __address_to_wait,
void const* __old_value,
- uint64_t __timeout_ns) {
+ optional<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
@@ -334,7 +335,7 @@ _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, 0);
+ &__entry->__waiter_count, &__entry->__platform_state, &__old_value, nullopt);
}
_LIBCPP_EXPORTED_FROM_ABI void __atomic_wait_global_table_with_timeout(
@@ -356,7 +357,7 @@ _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, 0);
+ __contention_wait<_Size>(__get_native_waiter_count(__address), __address, __old_value, nullopt);
}
template <std::size_t _Size>
@@ -431,7 +432,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, 0);
+ &__entry->__waiter_count, &__entry->__platform_state, &__old_value, nullopt);
}
_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_one(__cxx_atomic_contention_t const volatile* __location) noexcept {
@@ -450,7 +451,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, 0);
+ __get_native_waiter_count(__location_cast), __location_cast, &__old_value, nullopt);
}
// this function is even unused in the old ABI
diff --git a/libcxx/test/std/thread/thread.semaphore/timed.pass.cpp b/libcxx/test/std/thread/thread.semaphore/timed.pass.cpp
index d0aa1f46072b9..b570abf6940d5 100644
--- a/libcxx/test/std/thread/thread.semaphore/timed.pass.cpp
+++ b/libcxx/test/std/thread/thread.semaphore/timed.pass.cpp
@@ -19,8 +19,7 @@
#include "make_test_thread.h"
#include "test_macros.h"
-int main(int, char**)
-{
+int main(int, char**) {
auto const start = std::chrono::steady_clock::now();
std::counting_semaphore<> s(0);
@@ -28,7 +27,7 @@ int main(int, char**)
assert(!s.try_acquire_until(start + std::chrono::milliseconds(250)));
assert(!s.try_acquire_for(std::chrono::milliseconds(250)));
- std::thread t = support::make_test_thread([&](){
+ std::thread t = support::make_test_thread([&]() {
std::this_thread::sleep_for(std::chrono::milliseconds(250));
s.release();
std::this_thread::sleep_for(std::chrono::milliseconds(250));
>From d7ab17180c6876cf5f4e08d6b1456fbeb6e8fb91 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 7 Feb 2026 19:44:00 +0000
Subject: [PATCH 05/14] return 0
---
.../thread.semaphore/timed.debug.pass.cpp | 32 +++++++------------
1 file changed, 11 insertions(+), 21 deletions(-)
diff --git a/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp b/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp
index d032145f9824d..4094645c42155 100644
--- a/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp
+++ b/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp
@@ -18,12 +18,11 @@
#include <iostream>
#include <iomanip>
-
#include "make_test_thread.h"
#include "test_macros.h"
-void test(auto log_start){
- auto log = [log_start] ()-> auto& {
+void test(auto log_start) {
+ auto log = [log_start]() -> auto& {
using namespace std::chrono;
auto elapsed = steady_clock::now() - log_start;
@@ -39,12 +38,9 @@ void test(auto log_start){
auto milliseconds = duration_cast<std::chrono::milliseconds>(elapsed);
- std::cerr << "["
- << std::setw(2) << std::setfill('0') << hours.count() << ":"
- << std::setw(2) << std::setfill('0') << minutes.count() << ":"
- << std::setw(2) << std::setfill('0') << seconds.count() << "."
- << std::setw(3) << std::setfill('0') << milliseconds.count()
- << "] ";
+ std::cerr << "[" << std::setw(2) << std::setfill('0') << hours.count() << ":" << std::setw(2) << std::setfill('0')
+ << minutes.count() << ":" << std::setw(2) << std::setfill('0') << seconds.count() << "." << std::setw(3)
+ << std::setfill('0') << milliseconds.count() << "] ";
return std::cerr;
};
@@ -52,24 +48,24 @@ void test(auto log_start){
auto const start = std::chrono::steady_clock::now();
std::counting_semaphore<> s(0);
- log() << "start: try_acquire_until: start + " << std::chrono::milliseconds(250) << "\n";
+ log() << "start: try_acquire_until: start + " << std::chrono::milliseconds(250) << "\n";
assert(!s.try_acquire_until(start + std::chrono::milliseconds(250)));
- log() << "done: try_acquire_until: start + " << std::chrono::milliseconds(250) << "\n";
+ log() << "done: try_acquire_until: start + " << std::chrono::milliseconds(250) << "\n";
log() << "start: try_acquire_for: " << std::chrono::milliseconds(250) << "\n";
assert(!s.try_acquire_for(std::chrono::milliseconds(250)));
log() << "done: try_acquire_for: " << std::chrono::milliseconds(250) << "\n";
- std::thread t = support::make_test_thread([&](){
+ std::thread t = support::make_test_thread([&]() {
std::this_thread::sleep_for(std::chrono::milliseconds(250));
s.release();
std::this_thread::sleep_for(std::chrono::milliseconds(250));
s.release();
});
- log() << "start: try_acquire_until: start + " << std::chrono::seconds(2) << "\n";
+ log() << "start: try_acquire_until: start + " << std::chrono::seconds(2) << "\n";
assert(s.try_acquire_until(start + std::chrono::seconds(2)));
- log() << "done: try_acquire_until: start + " << std::chrono::seconds(2) << "\n";
+ log() << "done: try_acquire_until: start + " << std::chrono::seconds(2) << "\n";
log() << "start: try_acquire_for: " << std::chrono::seconds(2) << "\n";
assert(s.try_acquire_for(std::chrono::seconds(2)));
@@ -80,18 +76,12 @@ void test(auto log_start){
assert(end - start < std::chrono::seconds(10));
}
-int main(int, char**)
-{
+int main(int, char**) {
auto const log_start = std::chrono::steady_clock::now();
for (auto i = 0; i < 10; ++i) {
std::cerr << "=== Iteration " << i << " ===\n";
test(log_start);
}
-#if defined(_WIN32)
- return 1;
-#else
return 0;
-#endif
-
}
>From 1a785ea35a0fa9a319b1426b048d461e3e402e32 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 7 Feb 2026 19:45:26 +0000
Subject: [PATCH 06/14] increase
---
libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp b/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp
index 4094645c42155..1a6d790b051a0 100644
--- a/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp
+++ b/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp
@@ -78,7 +78,7 @@ void test(auto log_start) {
int main(int, char**) {
auto const log_start = std::chrono::steady_clock::now();
- for (auto i = 0; i < 10; ++i) {
+ for (auto i = 0; i < 30; ++i) {
std::cerr << "=== Iteration " << i << " ===\n";
test(log_start);
}
>From b860dd32b11225f8452e7a25a9b9ee3c580d7cf0 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 8 Feb 2026 09:24:01 +0000
Subject: [PATCH 07/14] test
---
libcxx/src/atomic.cpp | 3 +-
.../thread.semaphore/timed.debug.pass.cpp | 87 -------------------
.../thread.semaphore/timed.hang.pass.cpp | 45 ++++++++++
.../thread/thread.semaphore/timed.pass.cpp | 2 +-
4 files changed, 48 insertions(+), 89 deletions(-)
delete mode 100644 libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp
create mode 100644 libcxx/test/std/thread/thread.semaphore/timed.hang.pass.cpp
diff --git a/libcxx/src/atomic.cpp b/libcxx/src/atomic.cpp
index 57b90838b45f6..2205c25a8d82c 100644
--- a/libcxx/src/atomic.cpp
+++ b/libcxx/src/atomic.cpp
@@ -100,7 +100,8 @@ extern "C" int __ulock_wake(uint32_t operation, void* addr, uint64_t wake_value)
template <std::size_t _Size>
static void __platform_wait_on_address(void const* __ptr, void const* __val, optional<uint64_t> __timeout_ns) {
static_assert(_Size == 8 || _Size == 4, "Can only wait on 8 bytes or 4 bytes value");
- auto __timeout_us = !__timeout_ns.has_value() ? uint32_t(0) : std::max(static_cast<uint32_t>(*__timeout_ns / 1000), uint32_t(1));
+ auto __timeout_us =
+ !__timeout_ns.has_value() ? uint32_t(0) : std::max(static_cast<uint32_t>(*__timeout_ns / 1000), uint32_t(1));
if constexpr (_Size == 4) {
alignas(uint32_t) char buffer[_Size];
std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
diff --git a/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp b/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp
deleted file mode 100644
index 1a6d790b051a0..0000000000000
--- a/libcxx/test/std/thread/thread.semaphore/timed.debug.pass.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// <semaphore>
-
-#include <semaphore>
-#include <thread>
-#include <chrono>
-#include <cassert>
-#include <iostream>
-#include <iomanip>
-
-#include "make_test_thread.h"
-#include "test_macros.h"
-
-void test(auto log_start) {
- auto log = [log_start]() -> auto& {
- using namespace std::chrono;
-
- auto elapsed = steady_clock::now() - log_start;
-
- auto hours = duration_cast<std::chrono::hours>(elapsed);
- elapsed -= hours;
-
- auto minutes = duration_cast<std::chrono::minutes>(elapsed);
- elapsed -= minutes;
-
- auto seconds = duration_cast<std::chrono::seconds>(elapsed);
- elapsed -= seconds;
-
- auto milliseconds = duration_cast<std::chrono::milliseconds>(elapsed);
-
- std::cerr << "[" << std::setw(2) << std::setfill('0') << hours.count() << ":" << std::setw(2) << std::setfill('0')
- << minutes.count() << ":" << std::setw(2) << std::setfill('0') << seconds.count() << "." << std::setw(3)
- << std::setfill('0') << milliseconds.count() << "] ";
-
- return std::cerr;
- };
-
- auto const start = std::chrono::steady_clock::now();
- std::counting_semaphore<> s(0);
-
- log() << "start: try_acquire_until: start + " << std::chrono::milliseconds(250) << "\n";
- assert(!s.try_acquire_until(start + std::chrono::milliseconds(250)));
- log() << "done: try_acquire_until: start + " << std::chrono::milliseconds(250) << "\n";
-
- log() << "start: try_acquire_for: " << std::chrono::milliseconds(250) << "\n";
- assert(!s.try_acquire_for(std::chrono::milliseconds(250)));
- log() << "done: try_acquire_for: " << std::chrono::milliseconds(250) << "\n";
-
- std::thread t = support::make_test_thread([&]() {
- std::this_thread::sleep_for(std::chrono::milliseconds(250));
- s.release();
- std::this_thread::sleep_for(std::chrono::milliseconds(250));
- s.release();
- });
-
- log() << "start: try_acquire_until: start + " << std::chrono::seconds(2) << "\n";
- assert(s.try_acquire_until(start + std::chrono::seconds(2)));
- log() << "done: try_acquire_until: start + " << std::chrono::seconds(2) << "\n";
-
- log() << "start: try_acquire_for: " << std::chrono::seconds(2) << "\n";
- assert(s.try_acquire_for(std::chrono::seconds(2)));
- log() << "done: try_acquire_for: " << std::chrono::seconds(2) << "\n";
- t.join();
-
- auto const end = std::chrono::steady_clock::now();
- assert(end - start < std::chrono::seconds(10));
-}
-
-int main(int, char**) {
- auto const log_start = std::chrono::steady_clock::now();
- for (auto i = 0; i < 30; ++i) {
- std::cerr << "=== Iteration " << i << " ===\n";
- test(log_start);
- }
-
- return 0;
-}
diff --git a/libcxx/test/std/thread/thread.semaphore/timed.hang.pass.cpp b/libcxx/test/std/thread/thread.semaphore/timed.hang.pass.cpp
new file mode 100644
index 0000000000000..75366bd4f5397
--- /dev/null
+++ b/libcxx/test/std/thread/thread.semaphore/timed.hang.pass.cpp
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <semaphore>
+
+// This is a regression test for a bug in semaphore::try_acquire_for
+// where it can wait indefinitely
+// https://github.com/llvm/llvm-project/issues/180334
+
+#include <semaphore>
+#include <thread>
+#include <chrono>
+#include <cassert>
+
+#include "make_test_thread.h"
+#include "test_macros.h"
+
+void test() {
+ auto const start = std::chrono::steady_clock::now();
+ std::counting_semaphore<> s(0);
+
+ assert(!s.try_acquire_for(std::chrono::nanoseconds(1)));
+ assert(!s.try_acquire_for(std::chrono::microseconds(1)));
+ assert(!s.try_acquire_for(std::chrono::milliseconds(1)));
+ assert(!s.try_acquire_for(std::chrono::milliseconds(100)));
+
+ auto const end = std::chrono::steady_clock::now();
+ assert(end - start < std::chrono::seconds(10));
+}
+
+int main(int, char**) {
+ for (auto i = 0; i < 10; ++i) {
+ test();
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.semaphore/timed.pass.cpp b/libcxx/test/std/thread/thread.semaphore/timed.pass.cpp
index b570abf6940d5..5b3aac6718075 100644
--- a/libcxx/test/std/thread/thread.semaphore/timed.pass.cpp
+++ b/libcxx/test/std/thread/thread.semaphore/timed.pass.cpp
@@ -27,7 +27,7 @@ int main(int, char**) {
assert(!s.try_acquire_until(start + std::chrono::milliseconds(250)));
assert(!s.try_acquire_for(std::chrono::milliseconds(250)));
- std::thread t = support::make_test_thread([&]() {
+ std::thread t = support::make_test_thread([&](){
std::this_thread::sleep_for(std::chrono::milliseconds(250));
s.release();
std::this_thread::sleep_for(std::chrono::milliseconds(250));
>From 17620205aa0a87bae164e3c8ef36cd7079ce706d Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 8 Feb 2026 12:33:26 +0000
Subject: [PATCH 08/14] ci
---
libcxx/src/atomic.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/src/atomic.cpp b/libcxx/src/atomic.cpp
index 2205c25a8d82c..0028f7f308653 100644
--- a/libcxx/src/atomic.cpp
+++ b/libcxx/src/atomic.cpp
@@ -240,7 +240,7 @@ static void __platform_wait_on_address(void const* __ptr, void const* __val, opt
__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));
+ std::chrono::nanoseconds(__timeout_ns.value_or(0)));
}
template <std::size_t _Size>
>From b3bf14b9584065871cef937439c8ded8c540ba1d Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 11 Feb 2026 17:57:21 +0000
Subject: [PATCH 09/14] round up
---
libcxx/src/atomic.cpp | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/libcxx/src/atomic.cpp b/libcxx/src/atomic.cpp
index 0028f7f308653..6ecb236450213 100644
--- a/libcxx/src/atomic.cpp
+++ b/libcxx/src/atomic.cpp
@@ -193,10 +193,18 @@ static void __platform_wait_on_address(void const* __ptr, void const* __val, opt
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,
- !__timeout_ns.has_value() ? INFINITE : static_cast<DWORD>(*__timeout_ns / 1'000'000));
+ auto timeout_ms = [&]() -> DWORD {
+ if (!__timeout_ns.has_value()) {
+ return INFINITE;
+ }
+ auto ms = *__timeout_ns / 1'000'000;
+ if (ms == 0 && *__timeout_ns > 100'000)
+ // Round up to 1ms if requested between 100us - 1ms
+ return 1;
+
+ return ms > static_cast<uint64_t>(INFINITE) ? INFINITE : static_cast<DWORD>(ms);
+ }();
+ wait_on_address(const_cast<void*>(__ptr), const_cast<void*>(__val), _Size, timeout_ms);
} else {
__libcpp_thread_poll_with_backoff(
[=]() -> bool { return std::memcmp(const_cast<const void*>(__ptr), __val, _Size) != 0; },
>From 048cba8d3775a36a02fa8012afd31ddf5df3e8ad Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 11 Feb 2026 21:47:46 +0000
Subject: [PATCH 10/14] static type
---
libcxx/src/atomic.cpp | 79 ++++++++++++++++++++++++-------------------
1 file changed, 45 insertions(+), 34 deletions(-)
diff --git a/libcxx/src/atomic.cpp b/libcxx/src/atomic.cpp
index 6ecb236450213..32cae9dc2c1ac 100644
--- a/libcxx/src/atomic.cpp
+++ b/libcxx/src/atomic.cpp
@@ -14,7 +14,6 @@
#include <cstring>
#include <functional>
#include <new>
-#include <optional>
#include <thread>
#include <type_traits>
@@ -62,20 +61,22 @@
_LIBCPP_BEGIN_NAMESPACE_STD
+struct NoTimeout {};
+
#ifdef __linux__
-template <std::size_t _Size>
-static void __platform_wait_on_address(void const* __ptr, void const* __val, optional<uint64_t> __timeout_ns) {
+template <std::size_t _Size, class MaybeTimeout>
+static void __platform_wait_on_address(void const* __ptr, void const* __val, MaybeTimeout maybe_timeout_ns) {
static_assert(_Size == 4, "Can only wait on 4 bytes value");
alignas(__cxx_contention_t) char buffer[_Size];
std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
static constexpr timespec __default_timeout = {2, 0};
timespec __timeout;
- if (!__timeout_ns.has_value()) {
+ if constexpr (is_same_v<MaybeTimeout, NoTimeout>) {
__timeout = __default_timeout;
} else {
- __timeout.tv_sec = *__timeout_ns / 1'000'000'000;
- __timeout.tv_nsec = *__timeout_ns % 1'000'000'000;
+ __timeout.tv_sec = maybe_timeout_ns / 1'000'000'000;
+ __timeout.tv_nsec = maybe_timeout_ns % 1'000'000'000;
}
_LIBCPP_FUTEX(__ptr, FUTEX_WAIT_PRIVATE, *reinterpret_cast<__cxx_contention_t const*>(&buffer), &__timeout, 0, 0);
}
@@ -97,11 +98,16 @@ extern "C" int __ulock_wake(uint32_t operation, void* addr, uint64_t wake_value)
# define UL_COMPARE_AND_WAIT64 5
# define ULF_WAKE_ALL 0x00000100
-template <std::size_t _Size>
-static void __platform_wait_on_address(void const* __ptr, void const* __val, optional<uint64_t> __timeout_ns) {
+template <std::size_t _Size, class MaybeTimeout>
+static void __platform_wait_on_address(void const* __ptr, void const* __val, MaybeTimeout maybe_timeout_ns) {
static_assert(_Size == 8 || _Size == 4, "Can only wait on 8 bytes or 4 bytes value");
- auto __timeout_us =
- !__timeout_ns.has_value() ? uint32_t(0) : std::max(static_cast<uint32_t>(*__timeout_ns / 1000), uint32_t(1));
+ auto __timeout_us = [&] {
+ if constexpr (is_same_v<MaybeTimeout, NoTimeout>) {
+ return uint32_t(0);
+ } else {
+ return std::max(static_cast<uint32_t>(maybe_timeout_ns / 1000), uint32_t(1));
+ }
+ }();
if constexpr (_Size == 4) {
alignas(uint32_t) char buffer[_Size];
std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
@@ -132,17 +138,17 @@ static void __platform_wake_by_address(void const* __ptr, bool __notify_one) {
* limit its use to architectures where long and int64_t are synonyms.
*/
-template <std::size_t _Size>
-static void __platform_wait_on_address(void const* __ptr, void const* __val, optional<uint64_t> __timeout_ns) {
+template <std::size_t _Size, class MaybeTimeout>
+static void __platform_wait_on_address(void const* __ptr, void const* __val, MaybeTimeout maybe_timeout_ns) {
static_assert(_Size == 8, "Can only wait on 8 bytes value");
alignas(__cxx_contention_t) char buffer[_Size];
std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
- if (!__timeout_ns.has_value()) {
+ if constexpr (is_same_v<MaybeTimeout, NoTimeout>) {
_umtx_op(const_cast<void*>(__ptr), UMTX_OP_WAIT, *reinterpret_cast<__cxx_contention_t*>(&buffer), nullptr, nullptr);
} else {
_umtx_time ut;
- ut._timeout.tv_sec = *__timeout_ns / 1'000'000'000;
- ut._timeout.tv_nsec = *__timeout_ns % 1'000'000'000;
+ ut._timeout.tv_sec = maybe_timeout_ns / 1'000'000'000;
+ ut._timeout.tv_nsec = maybe_timeout_ns % 1'000'000'000;
ut._flags = 0; // Relative time (not absolute)
ut._clockid = CLOCK_MONOTONIC; // Use monotonic clock
@@ -186,23 +192,24 @@ static void* win32_get_synch_api_function(const char* function_name) {
return reinterpret_cast<void*>(GetProcAddress(module_handle, function_name));
}
-template <std::size_t _Size>
-static void __platform_wait_on_address(void const* __ptr, void const* __val, optional<uint64_t> __timeout_ns) {
+template <std::size_t _Size, class MaybeTimeout>
+static void __platform_wait_on_address(void const* __ptr, void const* __val, MaybeTimeout maybe_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) {
auto timeout_ms = [&]() -> DWORD {
- if (!__timeout_ns.has_value()) {
+ if constexpr (is_same_v<MaybeTimeout, NoTimeout>) {
return INFINITE;
- }
- auto ms = *__timeout_ns / 1'000'000;
- if (ms == 0 && *__timeout_ns > 100'000)
- // Round up to 1ms if requested between 100us - 1ms
- return 1;
+ } else {
+ auto ms = maybe_timeout_ns / 1'000'000;
+ if (ms == 0 && maybe_timeout_ns > 100'000)
+ // Round up to 1ms if requested between 100us - 1ms
+ return 1;
- return ms > static_cast<uint64_t>(INFINITE) ? INFINITE : static_cast<DWORD>(ms);
+ return ms > static_cast<uint64_t>(INFINITE) ? INFINITE : static_cast<DWORD>(ms);
+ }
}();
wait_on_address(const_cast<void*>(__ptr), const_cast<void*>(__val), _Size, timeout_ms);
} else {
@@ -243,12 +250,16 @@ 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, optional<uint64_t> __timeout_ns) {
+template <std::size_t _Size, class MaybeTimeout>
+static void __platform_wait_on_address(void const* __ptr, void const* __val, MaybeTimeout maybe_timeout_ns) {
+ std::chrono::nanoseconds timeout = std::chrono::nanoseconds(0);
+ if constexpr (!is_same_v<MaybeTimeout, NoTimeout>) {
+ timeout = std::chrono::nanoseconds(maybe_timeout_ns);
+ }
__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.value_or(0)));
+ timeout);
}
template <std::size_t _Size>
@@ -271,17 +282,17 @@ __contention_notify(__cxx_atomic_contention_t* __waiter_count, void const* __add
__platform_wake_by_address<_Size>(__address_to_notify, __notify_one);
}
-template <std::size_t _Size>
+template <std::size_t _Size, class MaybeTimeout>
static void __contention_wait(__cxx_atomic_contention_t* __waiter_count,
void const* __address_to_wait,
void const* __old_value,
- optional<uint64_t> __timeout_ns) {
+ MaybeTimeout maybe_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, __timeout_ns);
+ __platform_wait_on_address<_Size>(__address_to_wait, __old_value, maybe_timeout_ns);
__cxx_atomic_fetch_sub(__waiter_count, __cxx_contention_t(1), memory_order_release);
}
@@ -344,7 +355,7 @@ _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, nullopt);
+ &__entry->__waiter_count, &__entry->__platform_state, &__old_value, NoTimeout{});
}
_LIBCPP_EXPORTED_FROM_ABI void __atomic_wait_global_table_with_timeout(
@@ -366,7 +377,7 @@ _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, nullopt);
+ __contention_wait<_Size>(__get_native_waiter_count(__address), __address, __old_value, NoTimeout{});
}
template <std::size_t _Size>
@@ -441,7 +452,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, nullopt);
+ &__entry->__waiter_count, &__entry->__platform_state, &__old_value, NoTimeout{});
}
_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_one(__cxx_atomic_contention_t const volatile* __location) noexcept {
@@ -460,7 +471,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, nullopt);
+ __get_native_waiter_count(__location_cast), __location_cast, &__old_value, NoTimeout{});
}
// this function is even unused in the old ABI
>From 525d21d80b961cf78d8f215c07db90c6f4b8ba4f Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 11 Feb 2026 22:56:49 +0000
Subject: [PATCH 11/14] review and ci
---
libcxx/src/atomic.cpp | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/libcxx/src/atomic.cpp b/libcxx/src/atomic.cpp
index 32cae9dc2c1ac..9c2925845d512 100644
--- a/libcxx/src/atomic.cpp
+++ b/libcxx/src/atomic.cpp
@@ -203,20 +203,24 @@ static void __platform_wait_on_address(void const* __ptr, void const* __val, May
if constexpr (is_same_v<MaybeTimeout, NoTimeout>) {
return INFINITE;
} else {
- auto ms = maybe_timeout_ns / 1'000'000;
+ uint64_t ms = maybe_timeout_ns / 1'000'000;
if (ms == 0 && maybe_timeout_ns > 100'000)
// Round up to 1ms if requested between 100us - 1ms
return 1;
- return ms > static_cast<uint64_t>(INFINITE) ? INFINITE : static_cast<DWORD>(ms);
+ return static_cast<DWORD>(std::min(static_cast<uint64_t>(INFINITE), ms));
}
}();
wait_on_address(const_cast<void*>(__ptr), const_cast<void*>(__val), _Size, timeout_ms);
} else {
+ std::chrono::nanoseconds timeout = std::chrono::nanoseconds(0);
+ if constexpr (!is_same_v<MaybeTimeout, NoTimeout>) {
+ timeout = std::chrono::nanoseconds(maybe_timeout_ns);
+ }
__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.value_or(0)));
+ timeout);
}
}
>From 66c72b2328417e36ae39a90a6c20ed099ba0652f Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Thu, 12 Feb 2026 07:00:36 +0000
Subject: [PATCH 12/14] CI
---
libcxx/src/atomic.cpp | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/libcxx/src/atomic.cpp b/libcxx/src/atomic.cpp
index 9c2925845d512..614ca2d4465a2 100644
--- a/libcxx/src/atomic.cpp
+++ b/libcxx/src/atomic.cpp
@@ -19,6 +19,9 @@
#include "include/apple_availability.h"
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
#ifdef __linux__
# include <linux/futex.h>
@@ -487,3 +490,5 @@ __libcpp_atomic_monitor(__cxx_atomic_contention_t const volatile* __location) no
_LIBCPP_DIAGNOSTIC_POP
_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
>From 69ffd72a0d530264b7f2fcabb06e1051fdf3aaeb Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Thu, 12 Feb 2026 19:53:49 +0000
Subject: [PATCH 13/14] rebase and fix min/max macro
---
libcxx/src/atomic.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/libcxx/src/atomic.cpp b/libcxx/src/atomic.cpp
index 614ca2d4465a2..9f2e0403f2318 100644
--- a/libcxx/src/atomic.cpp
+++ b/libcxx/src/atomic.cpp
@@ -19,9 +19,6 @@
#include "include/apple_availability.h"
-_LIBCPP_PUSH_MACROS
-#include <__undef_macros>
-
#ifdef __linux__
# include <linux/futex.h>
@@ -62,6 +59,9 @@ _LIBCPP_PUSH_MACROS
#endif
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
_LIBCPP_BEGIN_NAMESPACE_STD
struct NoTimeout {};
>From f458812478e8e8e8660dd98f69706d210c593f8c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 13 Feb 2026 13:05:10 +0000
Subject: [PATCH 14/14] review comments
---
libcxx/test/std/thread/thread.semaphore/timed.pass.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libcxx/test/std/thread/thread.semaphore/timed.pass.cpp b/libcxx/test/std/thread/thread.semaphore/timed.pass.cpp
index 5b3aac6718075..d0aa1f46072b9 100644
--- a/libcxx/test/std/thread/thread.semaphore/timed.pass.cpp
+++ b/libcxx/test/std/thread/thread.semaphore/timed.pass.cpp
@@ -19,7 +19,8 @@
#include "make_test_thread.h"
#include "test_macros.h"
-int main(int, char**) {
+int main(int, char**)
+{
auto const start = std::chrono::steady_clock::now();
std::counting_semaphore<> s(0);
More information about the libcxx-commits
mailing list