[libcxx-commits] [libcxx] [libc++][chrono][threading] Implement LWG 3504: `condition_variable::wait_for` is overspecified (PR #222443)
Vishank Singh via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Sep 28 03:22:55 PDT 2026
https://github.com/VishankSingh updated https://github.com/llvm/llvm-project/pull/222443
>From 472ad1af4cc31f17d72b387bc71b2096cd60bb6f Mon Sep 17 00:00:00 2001
From: vishanksingh <vishanksinghh at gmail.com>
Date: Thu, 10 Sep 2026 02:11:00 +0530
Subject: [PATCH 01/11] [libc++][chrono][threading] Implement LWG 3504:
condition_variable::wait_for is overspecified
Use ceil<steady_clock::duration> for relative-to-absolute conversion to avoid floating-point precision loss.
Update all wait_for overloads and add tests.
Fixes #189807
---
libcxx/docs/Status/Cxx26Issues.csv | 2 +-
.../__condition_variable/condition_variable.h | 32 +++++--------------
libcxx/include/condition_variable | 6 ++--
.../wait_for.pass.cpp | 15 +++++++++
.../wait_for_pred.pass.cpp | 15 +++++++++
.../wait_for.pass.cpp | 15 +++++++++
.../wait_for_pred.pass.cpp | 15 +++++++++
.../wait_for_token_pred.pass.cpp | 16 ++++++++++
8 files changed, 88 insertions(+), 28 deletions(-)
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index b98f3ee12f3af..252f59a599dbc 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -262,7 +262,7 @@
"","","","","","",""
"`LWG2414 <https://wg21.link/LWG2414>`__","Member function reentrancy should be implementation-defined","2026-03 (Croydon)","","","`#189805 <https://github.com/llvm/llvm-project/issues/189805>`__",""
"`LWG2746 <https://wg21.link/LWG2746>`__","Inconsistency between requirements for ``emplace`` between ``optional`` and ``variant``","2026-03 (Croydon)","","","`#189806 <https://github.com/llvm/llvm-project/issues/189806>`__",""
-"`LWG3504 <https://wg21.link/LWG3504>`__","``condition_variable::wait_for`` is overspecified","2026-03 (Croydon)","","","`#189807 <https://github.com/llvm/llvm-project/issues/189807>`__",""
+"`LWG3504 <https://wg21.link/LWG3504>`__","``condition_variable::wait_for`` is overspecified","2026-03 (Croydon)","|Complete|","24","`#189807 <https://github.com/llvm/llvm-project/issues/189807>`__",""
"`LWG3599 <https://wg21.link/LWG3599>`__","The ``const`` overload of ``lazy_split_view::begin`` should be constrained by ``const Pattern``","2026-03 (Croydon)","|Complete|","24","`#189808 <https://github.com/llvm/llvm-project/issues/189808>`__",""
"`LWG3662 <https://wg21.link/LWG3662>`__","``basic_string::append/assign(NTBS, pos, n)`` suboptimal","2026-03 (Croydon)","|Complete|","23","`#189809 <https://github.com/llvm/llvm-project/issues/189809>`__",""
"`LWG3777 <https://wg21.link/LWG3777>`__","Common ``cartesian_product_view`` produces an invalid range if the first range is input and one of the ranges is empty","2026-03 (Croydon)","","","`#189810 <https://github.com/llvm/llvm-project/issues/189810>`__",""
diff --git a/libcxx/include/__condition_variable/condition_variable.h b/libcxx/include/__condition_variable/condition_variable.h
index e15113ced5758..470ec749c84cf 100644
--- a/libcxx/include/__condition_variable/condition_variable.h
+++ b/libcxx/include/__condition_variable/condition_variable.h
@@ -40,6 +40,12 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
_LIBCPP_DECLARE_STRONG_ENUM(cv_status){no_timeout, timeout};
_LIBCPP_DECLARE_STRONG_ENUM_EPILOG(cv_status)
+template <class _Duration>
+inline _LIBCPP_HIDE_FROM_ABI chrono::steady_clock::time_point __rel_to_abs(const _Duration& __rel_time) {
+ using namespace chrono;
+ return steady_clock::now() + ceil<steady_clock::duration>(__rel_time);
+}
+
template <class _Rep, class _Period, __enable_if_t<is_floating_point<_Rep>::value, int> = 0>
inline _LIBCPP_HIDE_FROM_ABI chrono::nanoseconds __safe_nanosecond_cast(chrono::duration<_Rep, _Period> __d) {
using namespace chrono;
@@ -141,29 +147,7 @@ class _LIBCPP_EXPORTED_FROM_ABI condition_variable {
template <class _Rep, class _Period>
_LIBCPP_HIDE_FROM_ABI cv_status wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d) {
- using namespace chrono;
- if (__d <= __d.zero())
- return cv_status::timeout;
- using __ns_rep = nanoseconds::rep;
- steady_clock::time_point __c_now = steady_clock::now();
-
-# if _LIBCPP_HAS_COND_CLOCKWAIT
- using __clock_tp_ns = time_point<steady_clock, nanoseconds>;
- __ns_rep __now_count_ns = std::__safe_nanosecond_cast(__c_now.time_since_epoch()).count();
-# else
- using __clock_tp_ns = time_point<system_clock, nanoseconds>;
- __ns_rep __now_count_ns = std::__safe_nanosecond_cast(system_clock::now().time_since_epoch()).count();
-# endif
-
- __ns_rep __d_ns_count = std::__safe_nanosecond_cast(__d).count();
-
- if (__now_count_ns > numeric_limits<__ns_rep>::max() - __d_ns_count) {
- __do_timed_wait(__lk, __clock_tp_ns::max());
- } else {
- __do_timed_wait(__lk, __clock_tp_ns(nanoseconds(__now_count_ns + __d_ns_count)));
- }
-
- return steady_clock::now() - __c_now < __d ? cv_status::no_timeout : cv_status::timeout;
+ return wait_until(__lk, std::__rel_to_abs(__d));
}
template <class _Rep, class _Period, class _Predicate>
@@ -191,7 +175,7 @@ class _LIBCPP_EXPORTED_FROM_ABI condition_variable {
template <class _Rep, class _Period, class _Predicate>
inline bool
condition_variable::wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d, _Predicate __pred) {
- return wait_until(__lk, chrono::steady_clock::now() + __d, std::move(__pred));
+ return wait_until(__lk, std::__rel_to_abs(__d), std::move(__pred));
}
# if _LIBCPP_HAS_COND_CLOCKWAIT
diff --git a/libcxx/include/condition_variable b/libcxx/include/condition_variable
index 2bb4c1b0a8ad9..9148041f8f2b4 100644
--- a/libcxx/include/condition_variable
+++ b/libcxx/include/condition_variable
@@ -249,13 +249,13 @@ condition_variable_any::wait_until(_Lock& __lock, const chrono::time_point<_Cloc
template <class _Lock, class _Rep, class _Period>
inline cv_status condition_variable_any::wait_for(_Lock& __lock, const chrono::duration<_Rep, _Period>& __d) {
- return wait_until(__lock, chrono::steady_clock::now() + __d);
+ return wait_until(__lock, std::__rel_to_abs(__d));
}
template <class _Lock, class _Rep, class _Period, class _Predicate>
inline bool
condition_variable_any::wait_for(_Lock& __lock, const chrono::duration<_Rep, _Period>& __d, _Predicate __pred) {
- return wait_until(__lock, chrono::steady_clock::now() + __d, std::move(__pred));
+ return wait_until(__lock, std::__rel_to_abs(__d), std::move(__pred));
}
# if _LIBCPP_STD_VER >= 20
@@ -336,7 +336,7 @@ bool condition_variable_any::wait_until(
template <class _Lock, class _Rep, class _Period, class _Predicate>
bool condition_variable_any::wait_for(
_Lock& __lock, stop_token __stoken, const chrono::duration<_Rep, _Period>& __rel_time, _Predicate __pred) {
- return wait_until(__lock, std::move(__stoken), chrono::steady_clock::now() + __rel_time, std::move(__pred));
+ return wait_until(__lock, std::move(__stoken), std::__rel_to_abs(__rel_time), std::move(__pred));
}
# endif // _LIBCPP_STD_VER >= 20
diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for.pass.cpp
index 6a054f74b9fb9..50c327ed04534 100644
--- a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for.pass.cpp
+++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for.pass.cpp
@@ -105,5 +105,20 @@ int main(int, char**) {
t1.join();
}
+ // Regression test for LWG 3504: wait_for() with a floating-point
+ {
+ std::condition_variable cv;
+ std::mutex mutex;
+ std::unique_lock<std::mutex> lock(mutex);
+
+ auto start = std::chrono::steady_clock::now();
+ std::cv_status status = cv.wait_for(lock, std::chrono::duration<float>(0.25f));
+ auto elapsed = std::chrono::steady_clock::now() - start;
+
+ assert(status == std::cv_status::timeout);
+ assert(elapsed > std::chrono::milliseconds(200));
+ assert(elapsed < std::chrono::milliseconds(600));
+ }
+
return 0;
}
diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for_pred.pass.cpp
index 76fc7393bc8fc..bcf22a270a453 100644
--- a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for_pred.pass.cpp
+++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for_pred.pass.cpp
@@ -155,5 +155,20 @@ int main(int, char**) {
t1.join();
}
+ // Regression test for LWG 3504: wait_for() with a floating-point
+ {
+ std::condition_variable cv;
+ std::mutex mutex;
+ std::unique_lock<std::mutex> lock(mutex);
+
+ auto start = std::chrono::steady_clock::now();
+ bool result = cv.wait_for(lock, std::chrono::duration<float>(0.25f), [] { return false; });
+ auto elapsed = std::chrono::steady_clock::now() - start;
+
+ assert(result == false);
+ assert(elapsed > std::chrono::milliseconds(200));
+ assert(elapsed < std::chrono::milliseconds(600));
+ }
+
return 0;
}
diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for.pass.cpp
index eab38081d7b7f..2f2d46a880775 100644
--- a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for.pass.cpp
+++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for.pass.cpp
@@ -110,6 +110,21 @@ void test() {
t1.join();
}
+
+ // Regression test for LWG 3504: wait_for() with a floating-point
+ {
+ std::condition_variable_any cv;
+ std::mutex mutex;
+ std::unique_lock<std::mutex> lock(mutex);
+
+ auto start = std::chrono::steady_clock::now();
+ std::cv_status status = cv.wait_for(lock, std::chrono::duration<float>(0.25f));
+ auto elapsed = std::chrono::steady_clock::now() - start;
+
+ assert(status == std::cv_status::timeout);
+ assert(elapsed > std::chrono::milliseconds(200));
+ assert(elapsed < std::chrono::milliseconds(600));
+ }
}
int main(int, char**) {
diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_pred.pass.cpp
index 67ba06b835297..fb9b3ccdd2d02 100644
--- a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_pred.pass.cpp
+++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_pred.pass.cpp
@@ -160,6 +160,21 @@ void test() {
t2.join();
t1.join();
}
+
+ // Regression test for LWG 3504: wait_for() with a floating-point
+ {
+ std::condition_variable_any cv;
+ std::mutex mutex;
+ std::unique_lock<std::mutex> lock(mutex);
+
+ auto start = std::chrono::steady_clock::now();
+ bool result = cv.wait_for(lock, std::chrono::duration<float>(0.25f), [] { return false; });
+ auto elapsed = std::chrono::steady_clock::now() - start;
+
+ assert(result == false);
+ assert(elapsed > std::chrono::milliseconds(200));
+ assert(elapsed < std::chrono::milliseconds(600));
+ }
}
int main(int, char**) {
diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_token_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_token_pred.pass.cpp
index 6e466b9f5a3df..7b991d285e76e 100644
--- a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_token_pred.pass.cpp
+++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_token_pred.pass.cpp
@@ -236,6 +236,22 @@ void test() {
}
}
#endif //!defined(TEST_HAS_NO_EXCEPTIONS)
+
+ // Regression test for LWG 3504: wait_for() with a floating-point
+ {
+ std::condition_variable_any cv;
+ std::mutex mutex;
+ std::unique_lock<std::mutex> lock(mutex);
+ std::stop_source ss;
+
+ auto start = std::chrono::steady_clock::now();
+ bool result = cv.wait_for(lock, ss.get_token(), std::chrono::duration<float>(0.25f), [] { return false; });
+ auto elapsed = std::chrono::steady_clock::now() - start;
+
+ assert(result == false);
+ assert(elapsed > std::chrono::milliseconds(200));
+ assert(elapsed < std::chrono::milliseconds(600));
+ }
}
int main(int, char**) {
>From 1f3327199adfbfa8ef50fff431c1bce83b3765d2 Mon Sep 17 00:00:00 2001
From: vishanksingh <vishanksinghh at gmail.com>
Date: Mon, 14 Sep 2026 23:52:42 +0530
Subject: [PATCH 02/11] Address review comments: add internal __ceil and update
__rel_to_abs
---
libcxx/include/__chrono/duration.h | 13 +++++++++----
.../__condition_variable/condition_variable.h | 5 ++---
2 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/libcxx/include/__chrono/duration.h b/libcxx/include/__chrono/duration.h
index fe5bbd07f6d89..f3c7bb28e4df4 100644
--- a/libcxx/include/__chrono/duration.h
+++ b/libcxx/include/__chrono/duration.h
@@ -130,6 +130,14 @@ struct duration_values {
}
};
+template <class _ToDuration, class _Rep, class _Period>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _ToDuration __ceil(const duration<_Rep, _Period>& __d) {
+ _ToDuration __t = chrono::duration_cast<_ToDuration>(__d);
+ if (__t < __d)
+ __t = __t + _ToDuration{1};
+ return __t;
+}
+
#if _LIBCPP_STD_VER >= 17
template <class _ToDuration, class _Rep, class _Period, enable_if_t<__is_duration_v<_ToDuration>, int> = 0>
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _ToDuration floor(const duration<_Rep, _Period>& __d) {
@@ -141,10 +149,7 @@ template <class _ToDuration, class _Rep, class _Period, enable_if_t<__is_duratio
template <class _ToDuration, class _Rep, class _Period, enable_if_t<__is_duration_v<_ToDuration>, int> = 0>
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _ToDuration ceil(const duration<_Rep, _Period>& __d) {
- _ToDuration __t = chrono::duration_cast<_ToDuration>(__d);
- if (__t < __d)
- __t = __t + _ToDuration{1};
- return __t;
+ return chrono::__ceil<_ToDuration>(__d);
}
template <class _ToDuration, class _Rep, class _Period, enable_if_t<__is_duration_v<_ToDuration>, int> = 0>
diff --git a/libcxx/include/__condition_variable/condition_variable.h b/libcxx/include/__condition_variable/condition_variable.h
index 470ec749c84cf..9f5a1bad17568 100644
--- a/libcxx/include/__condition_variable/condition_variable.h
+++ b/libcxx/include/__condition_variable/condition_variable.h
@@ -41,9 +41,8 @@ _LIBCPP_DECLARE_STRONG_ENUM(cv_status){no_timeout, timeout};
_LIBCPP_DECLARE_STRONG_ENUM_EPILOG(cv_status)
template <class _Duration>
-inline _LIBCPP_HIDE_FROM_ABI chrono::steady_clock::time_point __rel_to_abs(const _Duration& __rel_time) {
- using namespace chrono;
- return steady_clock::now() + ceil<steady_clock::duration>(__rel_time);
+_LIBCPP_HIDE_FROM_ABI chrono::steady_clock::time_point __rel_to_abs(const _Duration& __rel_time) {
+ return chrono::steady_clock::now() + chrono::__ceil<chrono::steady_clock::duration>(__rel_time);
}
template <class _Rep, class _Period, __enable_if_t<is_floating_point<_Rep>::value, int> = 0>
>From 0d1d44137d4f32ff43f1687a52a80c631b514b49 Mon Sep 17 00:00:00 2001
From: vishanksingh <vishanksinghh at gmail.com>
Date: Tue, 15 Sep 2026 02:13:39 +0530
Subject: [PATCH 03/11] [libc++] Fix __ceil for C++03 and restore non-pred
wait_for
---
libcxx/include/__chrono/duration.h | 2 +-
.../__condition_variable/condition_variable.h | 24 ++++++++++++++++++-
2 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/libcxx/include/__chrono/duration.h b/libcxx/include/__chrono/duration.h
index f3c7bb28e4df4..7f71d071e9067 100644
--- a/libcxx/include/__chrono/duration.h
+++ b/libcxx/include/__chrono/duration.h
@@ -134,7 +134,7 @@ template <class _ToDuration, class _Rep, class _Period>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _ToDuration __ceil(const duration<_Rep, _Period>& __d) {
_ToDuration __t = chrono::duration_cast<_ToDuration>(__d);
if (__t < __d)
- __t = __t + _ToDuration{1};
+ __t = __t + _ToDuration(1);
return __t;
}
diff --git a/libcxx/include/__condition_variable/condition_variable.h b/libcxx/include/__condition_variable/condition_variable.h
index 9f5a1bad17568..425c6db1862ac 100644
--- a/libcxx/include/__condition_variable/condition_variable.h
+++ b/libcxx/include/__condition_variable/condition_variable.h
@@ -146,7 +146,29 @@ class _LIBCPP_EXPORTED_FROM_ABI condition_variable {
template <class _Rep, class _Period>
_LIBCPP_HIDE_FROM_ABI cv_status wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d) {
- return wait_until(__lk, std::__rel_to_abs(__d));
+ using namespace chrono;
+ if (__d <= __d.zero())
+ return cv_status::timeout;
+ using __ns_rep = nanoseconds::rep;
+ steady_clock::time_point __c_now = steady_clock::now();
+
+# if _LIBCPP_HAS_COND_CLOCKWAIT
+ using __clock_tp_ns = time_point<steady_clock, nanoseconds>;
+ __ns_rep __now_count_ns = std::__safe_nanosecond_cast(__c_now.time_since_epoch()).count();
+# else
+ using __clock_tp_ns = time_point<system_clock, nanoseconds>;
+ __ns_rep __now_count_ns = std::__safe_nanosecond_cast(system_clock::now().time_since_epoch()).count();
+# endif
+
+ __ns_rep __d_ns_count = std::__safe_nanosecond_cast(__d).count();
+
+ if (__now_count_ns > numeric_limits<__ns_rep>::max() - __d_ns_count) {
+ __do_timed_wait(__lk, __clock_tp_ns::max());
+ } else {
+ __do_timed_wait(__lk, __clock_tp_ns(nanoseconds(__now_count_ns + __d_ns_count)));
+ }
+
+ return steady_clock::now() - __c_now < __d ? cv_status::no_timeout : cv_status::timeout;
}
template <class _Rep, class _Period, class _Predicate>
>From 5c03387def47d9dc7b99e4eb6e3e8502e19529b0 Mon Sep 17 00:00:00 2001
From: vishanksingh <vishanksinghh at gmail.com>
Date: Tue, 15 Sep 2026 02:48:19 +0530
Subject: [PATCH 04/11] [libc++] Apply __ceil before __safe_nanosecond_cast in
wait_for
---
libcxx/include/__condition_variable/condition_variable.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/include/__condition_variable/condition_variable.h b/libcxx/include/__condition_variable/condition_variable.h
index 425c6db1862ac..3f36d20439582 100644
--- a/libcxx/include/__condition_variable/condition_variable.h
+++ b/libcxx/include/__condition_variable/condition_variable.h
@@ -160,7 +160,7 @@ class _LIBCPP_EXPORTED_FROM_ABI condition_variable {
__ns_rep __now_count_ns = std::__safe_nanosecond_cast(system_clock::now().time_since_epoch()).count();
# endif
- __ns_rep __d_ns_count = std::__safe_nanosecond_cast(__d).count();
+ __ns_rep __d_ns_count = std::__safe_nanosecond_cast(__ceil<steady_clock::duration>(__d)).count();
if (__now_count_ns > numeric_limits<__ns_rep>::max() - __d_ns_count) {
__do_timed_wait(__lk, __clock_tp_ns::max());
>From 90be6eea72176a42e31b0035b462c72e05b2159b Mon Sep 17 00:00:00 2001
From: vishanksingh <vishanksinghh at gmail.com>
Date: Tue, 15 Sep 2026 17:36:56 +0530
Subject: [PATCH 05/11] [libc++] Qualify __ceil call to satisfy
libcpp-robust-against-adl
---
libcxx/include/__condition_variable/condition_variable.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/include/__condition_variable/condition_variable.h b/libcxx/include/__condition_variable/condition_variable.h
index 3f36d20439582..b8f243c5d9508 100644
--- a/libcxx/include/__condition_variable/condition_variable.h
+++ b/libcxx/include/__condition_variable/condition_variable.h
@@ -160,7 +160,7 @@ class _LIBCPP_EXPORTED_FROM_ABI condition_variable {
__ns_rep __now_count_ns = std::__safe_nanosecond_cast(system_clock::now().time_since_epoch()).count();
# endif
- __ns_rep __d_ns_count = std::__safe_nanosecond_cast(__ceil<steady_clock::duration>(__d)).count();
+ __ns_rep __d_ns_count = std::__safe_nanosecond_cast(chrono::__ceil<steady_clock::duration>(__d)).count();
if (__now_count_ns > numeric_limits<__ns_rep>::max() - __d_ns_count) {
__do_timed_wait(__lk, __clock_tp_ns::max());
>From 71c74ee06e18550dc0db3fd7d7b49f610c0bc6c1 Mon Sep 17 00:00:00 2001
From: vishanksingh <vishanksinghh at gmail.com>
Date: Tue, 22 Sep 2026 01:34:53 +0530
Subject: [PATCH 06/11] [libc++] Fix LWG 3504 wait_for float tests for spurious
wakeups
---
.../thread.condition.condvar/wait_for.pass.cpp | 15 ++++++++++++---
.../thread.condition.condvarany/wait_for.pass.cpp | 15 ++++++++++++---
2 files changed, 24 insertions(+), 6 deletions(-)
diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for.pass.cpp
index 50c327ed04534..d0885cb919810 100644
--- a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for.pass.cpp
+++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for.pass.cpp
@@ -111,11 +111,20 @@ int main(int, char**) {
std::mutex mutex;
std::unique_lock<std::mutex> lock(mutex);
+ using float_sec = std::chrono::duration<float>;
+ const float_sec total{0.25f};
+
auto start = std::chrono::steady_clock::now();
- std::cv_status status = cv.wait_for(lock, std::chrono::duration<float>(0.25f));
- auto elapsed = std::chrono::steady_clock::now() - start;
+ std::cv_status status = std::cv_status::no_timeout;
+
+ while (status == std::cv_status::no_timeout) {
+ auto left = total - std::chrono::duration_cast<float_sec>(std::chrono::steady_clock::now() - start);
+ if (left <= float_sec::zero())
+ break;
+ status = cv.wait_for(lock, left);
+ }
- assert(status == std::cv_status::timeout);
+ auto elapsed = std::chrono::steady_clock::now() - start;
assert(elapsed > std::chrono::milliseconds(200));
assert(elapsed < std::chrono::milliseconds(600));
}
diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for.pass.cpp
index 2f2d46a880775..37d0c8dd55c5c 100644
--- a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for.pass.cpp
+++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for.pass.cpp
@@ -117,11 +117,20 @@ void test() {
std::mutex mutex;
std::unique_lock<std::mutex> lock(mutex);
+ using float_sec = std::chrono::duration<float>;
+ const float_sec total{0.25f};
+
auto start = std::chrono::steady_clock::now();
- std::cv_status status = cv.wait_for(lock, std::chrono::duration<float>(0.25f));
- auto elapsed = std::chrono::steady_clock::now() - start;
+ std::cv_status status = std::cv_status::no_timeout;
+
+ while (status == std::cv_status::no_timeout) {
+ auto left = total - std::chrono::duration_cast<float_sec>(std::chrono::steady_clock::now() - start);
+ if (left <= float_sec::zero())
+ break;
+ status = cv.wait_for(lock, left);
+ }
- assert(status == std::cv_status::timeout);
+ auto elapsed = std::chrono::steady_clock::now() - start;
assert(elapsed > std::chrono::milliseconds(200));
assert(elapsed < std::chrono::milliseconds(600));
}
>From de00642bc129914296b3a10632b87301e10e8db5 Mon Sep 17 00:00:00 2001
From: vishanksingh <vishanksinghh at gmail.com>
Date: Wed, 23 Sep 2026 14:11:58 +0530
Subject: [PATCH 07/11] [libc++] Simplify wait_for and harden __rel_to_abs
Update __rel_to_abs to ceil the relative duration and clamp zero/overflow
cases before forming the absolute time point.
---
.../__condition_variable/condition_variable.h | 45 +++++++------------
1 file changed, 17 insertions(+), 28 deletions(-)
diff --git a/libcxx/include/__condition_variable/condition_variable.h b/libcxx/include/__condition_variable/condition_variable.h
index b8f243c5d9508..bbecbb44da861 100644
--- a/libcxx/include/__condition_variable/condition_variable.h
+++ b/libcxx/include/__condition_variable/condition_variable.h
@@ -40,11 +40,6 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
_LIBCPP_DECLARE_STRONG_ENUM(cv_status){no_timeout, timeout};
_LIBCPP_DECLARE_STRONG_ENUM_EPILOG(cv_status)
-template <class _Duration>
-_LIBCPP_HIDE_FROM_ABI chrono::steady_clock::time_point __rel_to_abs(const _Duration& __rel_time) {
- return chrono::steady_clock::now() + chrono::__ceil<chrono::steady_clock::duration>(__rel_time);
-}
-
template <class _Rep, class _Period, __enable_if_t<is_floating_point<_Rep>::value, int> = 0>
inline _LIBCPP_HIDE_FROM_ABI chrono::nanoseconds __safe_nanosecond_cast(chrono::duration<_Rep, _Period> __d) {
using namespace chrono;
@@ -92,6 +87,22 @@ inline _LIBCPP_HIDE_FROM_ABI chrono::nanoseconds __safe_nanosecond_cast(chrono::
return nanoseconds(__result);
}
+template <class _Duration>
+_LIBCPP_HIDE_FROM_ABI chrono::steady_clock::time_point __rel_to_abs(const _Duration& __rel_time) {
+ using namespace chrono;
+
+ if (__rel_time <= _Duration::zero())
+ return steady_clock::time_point::min();
+
+ steady_clock::time_point __now = steady_clock::now();
+ nanoseconds __d_ns = std::__safe_nanosecond_cast(chrono::__ceil<steady_clock::duration>(__rel_time));
+
+ if (__d_ns > nanoseconds::max() - __now.time_since_epoch())
+ return steady_clock::time_point::max();
+
+ return __now + __d_ns;
+}
+
class _LIBCPP_EXPORTED_FROM_ABI condition_variable {
__libcpp_condvar_t __cv_ = _LIBCPP_CONDVAR_INITIALIZER;
@@ -146,29 +157,7 @@ class _LIBCPP_EXPORTED_FROM_ABI condition_variable {
template <class _Rep, class _Period>
_LIBCPP_HIDE_FROM_ABI cv_status wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d) {
- using namespace chrono;
- if (__d <= __d.zero())
- return cv_status::timeout;
- using __ns_rep = nanoseconds::rep;
- steady_clock::time_point __c_now = steady_clock::now();
-
-# if _LIBCPP_HAS_COND_CLOCKWAIT
- using __clock_tp_ns = time_point<steady_clock, nanoseconds>;
- __ns_rep __now_count_ns = std::__safe_nanosecond_cast(__c_now.time_since_epoch()).count();
-# else
- using __clock_tp_ns = time_point<system_clock, nanoseconds>;
- __ns_rep __now_count_ns = std::__safe_nanosecond_cast(system_clock::now().time_since_epoch()).count();
-# endif
-
- __ns_rep __d_ns_count = std::__safe_nanosecond_cast(chrono::__ceil<steady_clock::duration>(__d)).count();
-
- if (__now_count_ns > numeric_limits<__ns_rep>::max() - __d_ns_count) {
- __do_timed_wait(__lk, __clock_tp_ns::max());
- } else {
- __do_timed_wait(__lk, __clock_tp_ns(nanoseconds(__now_count_ns + __d_ns_count)));
- }
-
- return steady_clock::now() - __c_now < __d ? cv_status::no_timeout : cv_status::timeout;
+ return wait_until(__lk, std::__rel_to_abs(__d));
}
template <class _Rep, class _Period, class _Predicate>
>From b474dfa7485501bd1456a3b2bb8fc5cee402b6a1 Mon Sep 17 00:00:00 2001
From: vishanksingh <vishanksinghh at gmail.com>
Date: Wed, 23 Sep 2026 20:03:26 +0530
Subject: [PATCH 08/11] [libc++] Fix condition_variable infinite recursion on
non-CLOCKWAIT platforms
---
.../__condition_variable/condition_variable.h | 29 +++++++++++++++----
1 file changed, 24 insertions(+), 5 deletions(-)
diff --git a/libcxx/include/__condition_variable/condition_variable.h b/libcxx/include/__condition_variable/condition_variable.h
index bbecbb44da861..3446dbd7c861f 100644
--- a/libcxx/include/__condition_variable/condition_variable.h
+++ b/libcxx/include/__condition_variable/condition_variable.h
@@ -90,16 +90,14 @@ inline _LIBCPP_HIDE_FROM_ABI chrono::nanoseconds __safe_nanosecond_cast(chrono::
template <class _Duration>
_LIBCPP_HIDE_FROM_ABI chrono::steady_clock::time_point __rel_to_abs(const _Duration& __rel_time) {
using namespace chrono;
-
if (__rel_time <= _Duration::zero())
return steady_clock::time_point::min();
steady_clock::time_point __now = steady_clock::now();
- nanoseconds __d_ns = std::__safe_nanosecond_cast(chrono::__ceil<steady_clock::duration>(__rel_time));
-
+ nanoseconds __d_ns = std::__safe_nanosecond_cast(chrono::__ceil<steady_clock::duration>(__rel_time));
if (__d_ns > nanoseconds::max() - __now.time_since_epoch())
return steady_clock::time_point::max();
-
+
return __now + __d_ns;
}
@@ -215,7 +213,28 @@ inline void condition_variable::__do_timed_wait(
template <class _Clock>
inline void condition_variable::__do_timed_wait(unique_lock<mutex>& __lk,
chrono::time_point<_Clock, chrono::nanoseconds> __tp) _NOEXCEPT {
- wait_for(__lk, __tp - _Clock::now());
+ using namespace chrono;
+ nanoseconds __d = __tp - _Clock::now();
+ if (__d <= __d.zero())
+ return;
+ using __ns_rep = nanoseconds::rep;
+
+# if _LIBCPP_HAS_COND_CLOCKWAIT
+ steady_clock::time_point __c_now = steady_clock::now();
+ using __clock_tp_ns = time_point<steady_clock, nanoseconds>;
+ __ns_rep __now_count_ns = std::__safe_nanosecond_cast(__c_now.time_since_epoch()).count();
+# else
+ using __clock_tp_ns = time_point<system_clock, nanoseconds>;
+ __ns_rep __now_count_ns = std::__safe_nanosecond_cast(system_clock::now().time_since_epoch()).count();
+# endif
+
+ __ns_rep __d_ns_count = std::__safe_nanosecond_cast(__d).count();
+
+ if (__now_count_ns > numeric_limits<__ns_rep>::max() - __d_ns_count) {
+ __do_timed_wait(__lk, __clock_tp_ns::max());
+ } else {
+ __do_timed_wait(__lk, __clock_tp_ns(nanoseconds(__now_count_ns + __d_ns_count)));
+ }
}
_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
>From cb02b09a52abb6ba5c7c7942898b00731cbec1ac Mon Sep 17 00:00:00 2001
From: vishanksingh <vishanksinghh at gmail.com>
Date: Wed, 23 Sep 2026 20:43:12 +0530
Subject: [PATCH 09/11] trigger CI
>From 434d918b546001b5734e86d80c0b14dc7d00f697 Mon Sep 17 00:00:00 2001
From: vishanksingh <vishanksinghh at gmail.com>
Date: Thu, 24 Sep 2026 17:08:10 +0530
Subject: [PATCH 10/11] trigger CI
>From 4ad8452202acbddec323da52d801d7776bbc5a67 Mon Sep 17 00:00:00 2001
From: vishanksingh <vishanksinghh at gmail.com>
Date: Fri, 25 Sep 2026 18:59:54 +0530
Subject: [PATCH 11/11] [libc++] Simplify __rel_to_abs to match LWG 3504
---
.../include/__condition_variable/condition_variable.h | 11 +----------
1 file changed, 1 insertion(+), 10 deletions(-)
diff --git a/libcxx/include/__condition_variable/condition_variable.h b/libcxx/include/__condition_variable/condition_variable.h
index 467a4fb01fe4a..0787eaf2a371b 100644
--- a/libcxx/include/__condition_variable/condition_variable.h
+++ b/libcxx/include/__condition_variable/condition_variable.h
@@ -97,16 +97,7 @@ inline _LIBCPP_HIDE_FROM_ABI chrono::nanoseconds __safe_nanosecond_cast(chrono::
template <class _Duration>
_LIBCPP_HIDE_FROM_ABI chrono::steady_clock::time_point __rel_to_abs(const _Duration& __rel_time) {
- using namespace chrono;
- if (__rel_time <= _Duration::zero())
- return steady_clock::time_point::min();
-
- steady_clock::time_point __now = steady_clock::now();
- nanoseconds __d_ns = std::__safe_nanosecond_cast(chrono::__ceil<steady_clock::duration>(__rel_time));
- if (__d_ns > nanoseconds::max() - __now.time_since_epoch())
- return steady_clock::time_point::max();
-
- return __now + __d_ns;
+ return chrono::steady_clock::now() + chrono::__ceil<chrono::steady_clock::duration>(__rel_time);
}
class _LIBCPP_EXPORTED_FROM_ABI _LIBCPP_WARN_UNUSED condition_variable {
More information about the libcxx-commits
mailing list