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