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