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 37 template <class Mutex, class Lock> 38 void 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 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