14fa812bbSHui //===----------------------------------------------------------------------===// 24fa812bbSHui // 34fa812bbSHui // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 44fa812bbSHui // See https://llvm.org/LICENSE.txt for license information. 54fa812bbSHui // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 64fa812bbSHui // 74fa812bbSHui //===----------------------------------------------------------------------===// 84fa812bbSHui // 94fa812bbSHui // UNSUPPORTED: no-threads 104fa812bbSHui // UNSUPPORTED: c++03, c++11, c++14, c++17 11*121ed5c1SLouis Dionne 124fa812bbSHui // XFAIL: availability-synchronization_library-missing 134fa812bbSHui 144fa812bbSHui // <condition_variable> 154fa812bbSHui 164fa812bbSHui // class condition_variable_any; 174fa812bbSHui 184fa812bbSHui // template<class Lock, class Clock, class Duration, class Predicate> 194fa812bbSHui // bool wait_until(Lock& lock, stop_token stoken, 204fa812bbSHui // const chrono::time_point<Clock, Duration>& abs_time, Predicate pred); 214fa812bbSHui 2209e3a360SLouis Dionne #include <atomic> 234fa812bbSHui #include <cassert> 244fa812bbSHui #include <chrono> 254fa812bbSHui #include <concepts> 264fa812bbSHui #include <condition_variable> 274fa812bbSHui #include <functional> 284fa812bbSHui #include <mutex> 294fa812bbSHui #include <shared_mutex> 304fa812bbSHui #include <stop_token> 314fa812bbSHui #include <thread> 324fa812bbSHui 3385a8e5c3SHui #include "helpers.h" 344fa812bbSHui #include "make_test_thread.h" 354fa812bbSHui #include "test_macros.h" 364fa812bbSHui 374fa812bbSHui template <class Mutex, class Lock> 384fa812bbSHui void test() { 3985a8e5c3SHui using namespace std::chrono_literals; 4085a8e5c3SHui const auto oneHourAgo = std::chrono::steady_clock::now() - 1h; 4185a8e5c3SHui const auto oneHourLater = std::chrono::steady_clock::now() + 1h; 424fa812bbSHui 434fa812bbSHui // stop_requested before hand 444fa812bbSHui { 454fa812bbSHui std::stop_source ss; 464fa812bbSHui std::condition_variable_any cv; 474fa812bbSHui Mutex mutex; 484fa812bbSHui Lock lock{mutex}; 494fa812bbSHui ss.request_stop(); 5085a8e5c3SHui ElapsedTimeCheck check(1min); 514fa812bbSHui 524fa812bbSHui // [Note 4: The returned value indicates whether the predicate evaluated to true 534fa812bbSHui // regardless of whether the timeout was triggered or a stop request was made.] 5485a8e5c3SHui std::same_as<bool> auto r1 = cv.wait_until(lock, ss.get_token(), oneHourAgo, []() { return false; }); 554fa812bbSHui assert(!r1); 564fa812bbSHui 5785a8e5c3SHui std::same_as<bool> auto r2 = cv.wait_until(lock, ss.get_token(), oneHourLater, []() { return false; }); 584fa812bbSHui assert(!r2); 594fa812bbSHui 6085a8e5c3SHui std::same_as<bool> auto r3 = cv.wait_until(lock, ss.get_token(), oneHourAgo, []() { return true; }); 614fa812bbSHui assert(r3); 624fa812bbSHui 6385a8e5c3SHui std::same_as<bool> auto r4 = cv.wait_until(lock, ss.get_token(), oneHourLater, []() { return true; }); 644fa812bbSHui assert(r4); 654fa812bbSHui 664fa812bbSHui // Postconditions: lock is locked by the calling thread. 674fa812bbSHui assert(lock.owns_lock()); 684fa812bbSHui } 694fa812bbSHui 704fa812bbSHui // no stop request, pred was true 714fa812bbSHui { 724fa812bbSHui std::stop_source ss; 734fa812bbSHui std::condition_variable_any cv; 744fa812bbSHui Mutex mutex; 754fa812bbSHui Lock lock{mutex}; 7685a8e5c3SHui ElapsedTimeCheck check(1min); 774fa812bbSHui 7885a8e5c3SHui std::same_as<bool> auto r1 = cv.wait_until(lock, ss.get_token(), oneHourAgo, []() { return true; }); 794fa812bbSHui assert(r1); 804fa812bbSHui 8185a8e5c3SHui std::same_as<bool> auto r2 = cv.wait_until(lock, ss.get_token(), oneHourLater, []() { return true; }); 824fa812bbSHui assert(r2); 834fa812bbSHui } 844fa812bbSHui 854fa812bbSHui // no stop request, pred was false, abs_time was in the past 864fa812bbSHui { 874fa812bbSHui std::stop_source ss; 884fa812bbSHui std::condition_variable_any cv; 894fa812bbSHui Mutex mutex; 904fa812bbSHui Lock lock{mutex}; 9185a8e5c3SHui ElapsedTimeCheck check(1min); 924fa812bbSHui 9385a8e5c3SHui std::same_as<bool> auto r1 = cv.wait_until(lock, ss.get_token(), oneHourAgo, []() { return false; }); 944fa812bbSHui assert(!r1); 954fa812bbSHui } 964fa812bbSHui 974fa812bbSHui // no stop request, pred was false until timeout 984fa812bbSHui { 994fa812bbSHui std::stop_source ss; 1004fa812bbSHui std::condition_variable_any cv; 1014fa812bbSHui Mutex mutex; 1024fa812bbSHui Lock lock{mutex}; 1034fa812bbSHui 1044fa812bbSHui auto oldTime = std::chrono::steady_clock::now(); 1054fa812bbSHui 1064fa812bbSHui std::same_as<bool> auto r1 = 1074fa812bbSHui cv.wait_until(lock, ss.get_token(), oldTime + std::chrono::milliseconds(2), [&]() { return false; }); 1084fa812bbSHui 1094fa812bbSHui assert((std::chrono::steady_clock::now() - oldTime) >= std::chrono::milliseconds(2)); 1104fa812bbSHui assert(!r1); 1114fa812bbSHui } 1124fa812bbSHui 1134fa812bbSHui // no stop request, pred was false, changed to true before timeout 1144fa812bbSHui { 1154fa812bbSHui std::stop_source ss; 1164fa812bbSHui std::condition_variable_any cv; 1174fa812bbSHui Mutex mutex; 1184fa812bbSHui Lock lock{mutex}; 1194fa812bbSHui 1204fa812bbSHui bool flag = false; 1214fa812bbSHui auto thread = support::make_test_thread([&]() { 1224fa812bbSHui std::this_thread::sleep_for(std::chrono::milliseconds(2)); 12381e2693cSHui std::unique_lock<Mutex> lock2{mutex}; 1244fa812bbSHui flag = true; 1254fa812bbSHui cv.notify_all(); 1264fa812bbSHui }); 1274fa812bbSHui 12885a8e5c3SHui ElapsedTimeCheck check(10min); 12985a8e5c3SHui 13085a8e5c3SHui std::same_as<bool> auto r1 = cv.wait_until(lock, ss.get_token(), oneHourLater, [&]() { return flag; }); 1314fa812bbSHui assert(flag); 1324fa812bbSHui assert(r1); 1334fa812bbSHui 1344fa812bbSHui thread.join(); 1354fa812bbSHui } 1364fa812bbSHui 1374fa812bbSHui // stop request comes while waiting 1384fa812bbSHui { 1394fa812bbSHui std::stop_source ss; 1404fa812bbSHui std::condition_variable_any cv; 1414fa812bbSHui Mutex mutex; 1424fa812bbSHui Lock lock{mutex}; 1434fa812bbSHui 1444fa812bbSHui std::atomic_bool start = false; 1454fa812bbSHui std::atomic_bool done = false; 1464fa812bbSHui auto thread = support::make_test_thread([&]() { 1474fa812bbSHui start.wait(false); 1484fa812bbSHui ss.request_stop(); 1494fa812bbSHui 1504fa812bbSHui while (!done) { 1514fa812bbSHui cv.notify_all(); 1524fa812bbSHui std::this_thread::sleep_for(std::chrono::milliseconds(2)); 1534fa812bbSHui } 1544fa812bbSHui }); 1554fa812bbSHui 15685a8e5c3SHui ElapsedTimeCheck check(10min); 15785a8e5c3SHui 15885a8e5c3SHui std::same_as<bool> auto r = cv.wait_until(lock, ss.get_token(), oneHourLater, [&]() { 1594fa812bbSHui start.store(true); 1604fa812bbSHui start.notify_all(); 1614fa812bbSHui return false; 1624fa812bbSHui }); 1634fa812bbSHui assert(!r); 1644fa812bbSHui done = true; 1654fa812bbSHui thread.join(); 1664fa812bbSHui 1674fa812bbSHui assert(lock.owns_lock()); 1684fa812bbSHui } 1694fa812bbSHui 17085a8e5c3SHui // #76807 Hangs in std::condition_variable_any when used with std::stop_token 17185a8e5c3SHui { 17285a8e5c3SHui class MyThread { 17385a8e5c3SHui public: 17485a8e5c3SHui MyThread() { 17585a8e5c3SHui thread_ = support::make_test_jthread([this](std::stop_token st) { 17685a8e5c3SHui while (!st.stop_requested()) { 17785a8e5c3SHui std::unique_lock lock{m_}; 17885a8e5c3SHui cv_.wait_until(lock, st, std::chrono::steady_clock::now() + std::chrono::hours(1), [] { return false; }); 17985a8e5c3SHui } 18085a8e5c3SHui }); 18185a8e5c3SHui } 18285a8e5c3SHui 18385a8e5c3SHui private: 18485a8e5c3SHui std::mutex m_; 18585a8e5c3SHui std::condition_variable_any cv_; 18685a8e5c3SHui std::jthread thread_; 18785a8e5c3SHui }; 18885a8e5c3SHui 18985a8e5c3SHui ElapsedTimeCheck check(10min); 19085a8e5c3SHui 19185a8e5c3SHui [[maybe_unused]] MyThread my_thread; 19285a8e5c3SHui } 19385a8e5c3SHui 19485a8e5c3SHui // request_stop potentially in-between check and wait 19585a8e5c3SHui { 19685a8e5c3SHui std::stop_source ss; 19785a8e5c3SHui std::condition_variable_any cv; 19885a8e5c3SHui Mutex mutex; 19985a8e5c3SHui Lock lock{mutex}; 20085a8e5c3SHui 20185a8e5c3SHui std::atomic_bool pred_started = false; 20285a8e5c3SHui std::atomic_bool request_stop_called = false; 20385a8e5c3SHui auto thread = support::make_test_thread([&]() { 20485a8e5c3SHui pred_started.wait(false); 20585a8e5c3SHui ss.request_stop(); 20685a8e5c3SHui request_stop_called.store(true); 20785a8e5c3SHui request_stop_called.notify_all(); 20885a8e5c3SHui }); 20985a8e5c3SHui 21085a8e5c3SHui ElapsedTimeCheck check(10min); 21185a8e5c3SHui 21285a8e5c3SHui std::same_as<bool> auto r = cv.wait_until(lock, ss.get_token(), oneHourLater, [&]() { 21385a8e5c3SHui pred_started.store(true); 21485a8e5c3SHui pred_started.notify_all(); 21585a8e5c3SHui request_stop_called.wait(false); 21685a8e5c3SHui return false; 21785a8e5c3SHui }); 21885a8e5c3SHui assert(!r); 21985a8e5c3SHui thread.join(); 22085a8e5c3SHui 22185a8e5c3SHui assert(lock.owns_lock()); 22285a8e5c3SHui } 22385a8e5c3SHui 2244fa812bbSHui #if !defined(TEST_HAS_NO_EXCEPTIONS) 2254fa812bbSHui // Throws: Any exception thrown by pred. 2264fa812bbSHui { 2274fa812bbSHui std::stop_source ss; 2284fa812bbSHui std::condition_variable_any cv; 2294fa812bbSHui Mutex mutex; 2304fa812bbSHui Lock lock{mutex}; 2314fa812bbSHui 2324fa812bbSHui try { 23385a8e5c3SHui cv.wait_until(lock, ss.get_token(), oneHourLater, []() -> bool { throw 5; }); 2344fa812bbSHui assert(false); 2354fa812bbSHui } catch (int i) { 2364fa812bbSHui assert(i == 5); 2374fa812bbSHui } 2384fa812bbSHui } 2394fa812bbSHui #endif //!defined(TEST_HAS_NO_EXCEPTIONS) 2404fa812bbSHui } 2414fa812bbSHui 2424fa812bbSHui int main(int, char**) { 2434fa812bbSHui test<std::mutex, std::unique_lock<std::mutex>>(); 2444fa812bbSHui test<std::shared_mutex, std::shared_lock<std::shared_mutex>>(); 2454fa812bbSHui 2464fa812bbSHui return 0; 2474fa812bbSHui } 248