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