1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// UNSUPPORTED: no-threads
10// UNSUPPORTED: c++03, c++11, c++14, c++17
11
12// XFAIL: availability-synchronization_library-missing
13
14// <condition_variable>
15
16// class condition_variable_any;
17
18// template<class Lock, class Clock, class Duration, class Predicate>
19// bool wait_until(Lock& lock, stop_token stoken,
20// const chrono::time_point<Clock, Duration>& abs_time, Predicate pred);
21
22#include <atomic>
23#include <cassert>
24#include <chrono>
25#include <concepts>
26#include <condition_variable>
27#include <functional>
28#include <mutex>
29#include <shared_mutex>
30#include <stop_token>
31#include <thread>
32
33#include "helpers.h"
34#include "make_test_thread.h"
35#include "test_macros.h"
36
37template <class Mutex, class Lock>
38void test() {
39 using namespace std::chrono_literals;
40 const auto oneHourAgo = std::chrono::steady_clock::now() - 1h;
41 const auto oneHourLater = std::chrono::steady_clock::now() + 1h;
42
43 // stop_requested before hand
44 {
45 std::stop_source ss;
46 std::condition_variable_any cv;
47 Mutex mutex;
48 Lock lock{mutex};
49 ss.request_stop();
50 ElapsedTimeCheck check(1min);
51
52 // [Note 4: The returned value indicates whether the predicate evaluated to true
53 // regardless of whether the timeout was triggered or a stop request was made.]
54 std::same_as<bool> auto r1 = cv.wait_until(lock, ss.get_token(), oneHourAgo, []() { return false; });
55 assert(!r1);
56
57 std::same_as<bool> auto r2 = cv.wait_until(lock, ss.get_token(), oneHourLater, []() { return false; });
58 assert(!r2);
59
60 std::same_as<bool> auto r3 = cv.wait_until(lock, ss.get_token(), oneHourAgo, []() { return true; });
61 assert(r3);
62
63 std::same_as<bool> auto r4 = cv.wait_until(lock, ss.get_token(), oneHourLater, []() { return true; });
64 assert(r4);
65
66 // Postconditions: lock is locked by the calling thread.
67 assert(lock.owns_lock());
68 }
69
70 // no stop request, pred was true
71 {
72 std::stop_source ss;
73 std::condition_variable_any cv;
74 Mutex mutex;
75 Lock lock{mutex};
76 ElapsedTimeCheck check(1min);
77
78 std::same_as<bool> auto r1 = cv.wait_until(lock, ss.get_token(), oneHourAgo, []() { return true; });
79 assert(r1);
80
81 std::same_as<bool> auto r2 = cv.wait_until(lock, ss.get_token(), oneHourLater, []() { return true; });
82 assert(r2);
83 }
84
85 // no stop request, pred was false, abs_time was in the past
86 {
87 std::stop_source ss;
88 std::condition_variable_any cv;
89 Mutex mutex;
90 Lock lock{mutex};
91 ElapsedTimeCheck check(1min);
92
93 std::same_as<bool> auto r1 = cv.wait_until(lock, ss.get_token(), oneHourAgo, []() { return false; });
94 assert(!r1);
95 }
96
97 // no stop request, pred was false until timeout
98 {
99 std::stop_source ss;
100 std::condition_variable_any cv;
101 Mutex mutex;
102 Lock lock{mutex};
103
104 auto oldTime = std::chrono::steady_clock::now();
105
106 std::same_as<bool> auto r1 =
107 cv.wait_until(lock, ss.get_token(), oldTime + std::chrono::milliseconds(2), [&]() { return false; });
108
109 assert((std::chrono::steady_clock::now() - oldTime) >= std::chrono::milliseconds(2));
110 assert(!r1);
111 }
112
113 // no stop request, pred was false, changed to true before timeout
114 {
115 std::stop_source ss;
116 std::condition_variable_any cv;
117 Mutex mutex;
118 Lock lock{mutex};
119
120 bool flag = false;
121 auto thread = support::make_test_thread([&]() {
122 std::this_thread::sleep_for(std::chrono::milliseconds(2));
123 std::unique_lock<Mutex> lock2{mutex};
124 flag = true;
125 cv.notify_all();
126 });
127
128 ElapsedTimeCheck check(10min);
129
130 std::same_as<bool> auto r1 = cv.wait_until(lock, ss.get_token(), oneHourLater, [&]() { return flag; });
131 assert(flag);
132 assert(r1);
133
134 thread.join();
135 }
136
137 // stop request comes while waiting
138 {
139 std::stop_source ss;
140 std::condition_variable_any cv;
141 Mutex mutex;
142 Lock lock{mutex};
143
144 std::atomic_bool start = false;
145 std::atomic_bool done = false;
146 auto thread = support::make_test_thread([&]() {
147 start.wait(false);
148 ss.request_stop();
149
150 while (!done) {
151 cv.notify_all();
152 std::this_thread::sleep_for(std::chrono::milliseconds(2));
153 }
154 });
155
156 ElapsedTimeCheck check(10min);
157
158 std::same_as<bool> auto r = cv.wait_until(lock, ss.get_token(), oneHourLater, [&]() {
159 start.store(true);
160 start.notify_all();
161 return false;
162 });
163 assert(!r);
164 done = true;
165 thread.join();
166
167 assert(lock.owns_lock());
168 }
169
170 // #76807 Hangs in std::condition_variable_any when used with std::stop_token
171 {
172 class MyThread {
173 public:
174 MyThread() {
175 thread_ = support::make_test_jthread([this](std::stop_token st) {
176 while (!st.stop_requested()) {
177 std::unique_lock lock{m_};
178 cv_.wait_until(lock, st, std::chrono::steady_clock::now() + std::chrono::hours(1), [] { return false; });
179 }
180 });
181 }
182
183 private:
184 std::mutex m_;
185 std::condition_variable_any cv_;
186 std::jthread thread_;
187 };
188
189 ElapsedTimeCheck check(10min);
190
191 [[maybe_unused]] MyThread my_thread;
192 }
193
194 // request_stop potentially in-between check and wait
195 {
196 std::stop_source ss;
197 std::condition_variable_any cv;
198 Mutex mutex;
199 Lock lock{mutex};
200
201 std::atomic_bool pred_started = false;
202 std::atomic_bool request_stop_called = false;
203 auto thread = support::make_test_thread([&]() {
204 pred_started.wait(false);
205 ss.request_stop();
206 request_stop_called.store(true);
207 request_stop_called.notify_all();
208 });
209
210 ElapsedTimeCheck check(10min);
211
212 std::same_as<bool> auto r = cv.wait_until(lock, ss.get_token(), oneHourLater, [&]() {
213 pred_started.store(true);
214 pred_started.notify_all();
215 request_stop_called.wait(false);
216 return false;
217 });
218 assert(!r);
219 thread.join();
220
221 assert(lock.owns_lock());
222 }
223
224#if !defined(TEST_HAS_NO_EXCEPTIONS)
225 // Throws: Any exception thrown by pred.
226 {
227 std::stop_source ss;
228 std::condition_variable_any cv;
229 Mutex mutex;
230 Lock lock{mutex};
231
232 try {
233 cv.wait_until(lock, ss.get_token(), oneHourLater, []() -> bool { throw 5; });
234 assert(false);
235 } catch (int i) {
236 assert(i == 5);
237 }
238 }
239#endif //!defined(TEST_HAS_NO_EXCEPTIONS)
240}
241
242int main(int, char**) {
243 test<std::mutex, std::unique_lock<std::mutex>>();
244 test<std::shared_mutex, std::shared_lock<std::shared_mutex>>();
245
246 return 0;
247}
248

source code of libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until_token_pred.pass.cpp