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

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