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 // XFAIL: availability-synchronization_library-missing
12 
13 // <condition_variable>
14 
15 // class condition_variable_any;
16 
17 // template<class Lock, class Rep, class Period, class Predicate>
18 //   bool wait_for(Lock& lock, stop_token stoken,
19 //                 const chrono::duration<Rep, Period>& rel_time, Predicate pred);
20 
21 #include <atomic>
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 
36 template <class Mutex, class Lock>
37 void test() {
38   using namespace std::chrono_literals;
39 
40   // stop_requested before hand
41   {
42     std::stop_source ss;
43     std::condition_variable_any cv;
44     Mutex mutex;
45     Lock lock{mutex};
46     ss.request_stop();
47 
48     ElapsedTimeCheck check(1min);
49 
50     // [Note 4: The returned value indicates whether the predicate evaluated to true
51     // regardless of whether the timeout was triggered or a stop request was made.]
52     std::same_as<bool> auto r1 = cv.wait_for(lock, ss.get_token(), -1h, []() { return false; });
53     assert(!r1);
54 
55     std::same_as<bool> auto r2 = cv.wait_for(lock, ss.get_token(), 1h, []() { return false; });
56     assert(!r2);
57 
58     std::same_as<bool> auto r3 = cv.wait_for(lock, ss.get_token(), -1h, []() { return true; });
59     assert(r3);
60 
61     std::same_as<bool> auto r4 = cv.wait_for(lock, ss.get_token(), 1h, []() { return true; });
62     assert(r4);
63 
64     // Postconditions: lock is locked by the calling thread.
65     assert(lock.owns_lock());
66   }
67 
68   // no stop request, pred was true
69   {
70     std::stop_source ss;
71     std::condition_variable_any cv;
72     Mutex mutex;
73     Lock lock{mutex};
74 
75     ElapsedTimeCheck check(1min);
76 
77     std::same_as<bool> auto r1 = cv.wait_for(lock, ss.get_token(), -1h, []() { return true; });
78     assert(r1);
79 
80     std::same_as<bool> auto r2 = cv.wait_for(lock, ss.get_token(), 1h, []() { 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 
91     ElapsedTimeCheck check(1min);
92 
93     std::same_as<bool> auto r1 = cv.wait_for(lock, ss.get_token(), -1h, []() { 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 old_time = std::chrono::steady_clock::now();
105 
106     std::same_as<bool> auto r1 = cv.wait_for(lock, ss.get_token(), 2ms, [&]() { return false; });
107 
108     assert((std::chrono::steady_clock::now() - old_time) >= 2ms);
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(2ms);
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_for(lock, ss.get_token(), 1h, [&]() { 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(2ms);
152       }
153     });
154 
155     ElapsedTimeCheck check(10min);
156 
157     std::same_as<bool> auto r = cv.wait_for(lock, ss.get_token(), 1h, [&]() {
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_for(lock, st, 1h, [] { 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_for(lock, ss.get_token(), 1h, [&]() {
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       ElapsedTimeCheck check(10min);
233       cv.wait_for(lock, ss.get_token(), 1h, []() -> 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 
242 int 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