[libcxx-commits] [libcxx] [libc++] Fix semaphore timed wait hanging on Windows (PR #180398)

via libcxx-commits libcxx-commits at lists.llvm.org
Wed Feb 11 23:00:55 PST 2026


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/180398

>From 79cc6e2cc0b253b0a34a3b5ad302341fa943170b 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/12] [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 15caf8d017581b2ac398c6e89c3b9bd9cb81180d 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/12] 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 09616457c9c0868806e5a3735ca9706f963e6066 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/12] 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 888d7f8ed7904aafb4af917131e9d8182cb309b9 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/12] 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 637c971ed8896..f16c9bf1f97c9 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");
   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,11 +98,11 @@ 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");
   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);
+  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)
     __ulock_wait(
         UL_COMPARE_AND_WAIT, const_cast<void*>(__ptr), *reinterpret_cast<uint32_t const*>(&buffer), __timeout_us);
@@ -128,16 +129,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");
   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
 
@@ -182,7 +183,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 =
@@ -191,12 +192,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)));
   }
 }
 
@@ -231,7 +232,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(),
@@ -262,7 +263,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
@@ -331,7 +332,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(
@@ -353,7 +354,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>
@@ -428,7 +429,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 {
@@ -447,7 +448,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 484dc598ecdf4451cf7109519cf8b6f7c27f4a91 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/12] 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 b19aa354842039668bf9141e0e15f462fcdd4fd2 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/12] 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 1985cfc83fd21aaed7e6ceff156f31ffdb5ab79c 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/12] 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 f16c9bf1f97c9..4d05815c76929 100644
--- a/libcxx/src/atomic.cpp
+++ b/libcxx/src/atomic.cpp
@@ -102,7 +102,8 @@ static void __platform_wait_on_address(void const* __ptr, void const* __val, opt
   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.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)
     __ulock_wait(
         UL_COMPARE_AND_WAIT, const_cast<void*>(__ptr), *reinterpret_cast<uint32_t const*>(&buffer), __timeout_us);
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 cf1a97cdd5bf18be4b8d40375dd2bae1b960508f 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/12] 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 4d05815c76929..c058dfc92be22 100644
--- a/libcxx/src/atomic.cpp
+++ b/libcxx/src/atomic.cpp
@@ -237,7 +237,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 1ad6ae9c0cf4b94ec272d5dc4f1c22b7df859e28 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/12] 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 c058dfc92be22..b5708fbcf9323 100644
--- a/libcxx/src/atomic.cpp
+++ b/libcxx/src/atomic.cpp
@@ -190,10 +190,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 791bce792aac6daa6e90fba06db944745ac2655a 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/12] 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 b5708fbcf9323..2d5f5cc7f1a13 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");
   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,13 +98,18 @@ 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");
   char buffer[_Size];
   std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
-  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)
     __ulock_wait(
         UL_COMPARE_AND_WAIT, const_cast<void*>(__ptr), *reinterpret_cast<uint32_t const*>(&buffer), __timeout_us);
@@ -129,17 +135,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");
   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
 
@@ -183,23 +189,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 {
@@ -240,12 +247,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>
@@ -268,17 +279,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);
 }
 
@@ -341,7 +352,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(
@@ -363,7 +374,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>
@@ -438,7 +449,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 {
@@ -457,7 +468,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 467db9e9593f25681ce8cf571e749637bcdc6ac9 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/12] 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 2d5f5cc7f1a13..7ab2607c051ee 100644
--- a/libcxx/src/atomic.cpp
+++ b/libcxx/src/atomic.cpp
@@ -200,20 +200,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 e54e340c06d23f830299c1cc276b000defe7c2cf 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/12] CI

---
 libcxx/src/atomic.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/src/atomic.cpp b/libcxx/src/atomic.cpp
index 7ab2607c051ee..814f5f6182d22 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>
@@ -484,3 +487,5 @@ __libcpp_atomic_monitor(__cxx_atomic_contention_t const volatile* __location) no
 _LIBCPP_DIAGNOSTIC_POP
 
 _LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS



More information about the libcxx-commits mailing list