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
114fa812bbSHui // XFAIL: availability-synchronization_library-missing
124fa812bbSHui 
134fa812bbSHui // <condition_variable>
144fa812bbSHui 
154fa812bbSHui // class condition_variable_any;
164fa812bbSHui 
174fa812bbSHui // template<class Lock, class Predicate>
184fa812bbSHui //   bool wait(Lock& lock, stop_token stoken, Predicate pred);
194fa812bbSHui 
20*09e3a360SLouis Dionne #include <atomic>
214fa812bbSHui #include <cassert>
224fa812bbSHui #include <concepts>
234fa812bbSHui #include <condition_variable>
244fa812bbSHui #include <functional>
254fa812bbSHui #include <mutex>
264fa812bbSHui #include <shared_mutex>
274fa812bbSHui #include <stop_token>
284fa812bbSHui #include <thread>
294fa812bbSHui 
304fa812bbSHui #include "make_test_thread.h"
314fa812bbSHui #include "test_macros.h"
324fa812bbSHui 
334fa812bbSHui template <class Mutex, class Lock>
344fa812bbSHui void test() {
354fa812bbSHui   // stop_requested before hand
364fa812bbSHui   {
374fa812bbSHui     std::stop_source ss;
384fa812bbSHui     std::condition_variable_any cv;
394fa812bbSHui     Mutex mutex;
404fa812bbSHui     Lock lock{mutex};
414fa812bbSHui     ss.request_stop();
424fa812bbSHui 
434fa812bbSHui     // [Note 1: The returned value indicates whether the predicate evaluated to true regardless of whether there was a stop request.]
444fa812bbSHui     std::same_as<bool> auto r1 = cv.wait(lock, ss.get_token(), []() { return false; });
454fa812bbSHui     assert(!r1);
464fa812bbSHui 
474fa812bbSHui     std::same_as<bool> auto r2 = cv.wait(lock, ss.get_token(), []() { return true; });
484fa812bbSHui     assert(r2);
494fa812bbSHui 
504fa812bbSHui     // Postconditions: lock is locked by the calling thread.
514fa812bbSHui     assert(lock.owns_lock());
524fa812bbSHui   }
534fa812bbSHui 
544fa812bbSHui   // no stop request
554fa812bbSHui   {
564fa812bbSHui     std::stop_source ss;
574fa812bbSHui     std::condition_variable_any cv;
584fa812bbSHui     Mutex mutex;
594fa812bbSHui     Lock lock{mutex};
604fa812bbSHui     std::same_as<bool> auto r1 = cv.wait(lock, ss.get_token(), []() { return true; });
614fa812bbSHui     assert(r1);
624fa812bbSHui 
634fa812bbSHui     bool flag   = false;
644fa812bbSHui     auto thread = support::make_test_thread([&]() {
654fa812bbSHui       std::this_thread::sleep_for(std::chrono::milliseconds(2));
6681e2693cSHui       std::unique_lock<Mutex> lock2{mutex};
674fa812bbSHui       flag = true;
684fa812bbSHui       cv.notify_all();
694fa812bbSHui     });
704fa812bbSHui 
714fa812bbSHui     std::same_as<bool> auto r2 = cv.wait(lock, ss.get_token(), [&]() { return flag; });
724fa812bbSHui     assert(flag);
734fa812bbSHui     assert(r2);
744fa812bbSHui     thread.join();
754fa812bbSHui 
764fa812bbSHui     assert(lock.owns_lock());
774fa812bbSHui   }
784fa812bbSHui 
794fa812bbSHui   // stop request comes while waiting
804fa812bbSHui   {
814fa812bbSHui     std::stop_source ss;
824fa812bbSHui     std::condition_variable_any cv;
834fa812bbSHui     Mutex mutex;
844fa812bbSHui     Lock lock{mutex};
854fa812bbSHui 
864fa812bbSHui     std::atomic_bool start = false;
874fa812bbSHui     std::atomic_bool done  = false;
884fa812bbSHui     auto thread            = support::make_test_thread([&]() {
894fa812bbSHui       start.wait(false);
904fa812bbSHui       ss.request_stop();
914fa812bbSHui 
924fa812bbSHui       while (!done) {
934fa812bbSHui         cv.notify_all();
944fa812bbSHui         std::this_thread::sleep_for(std::chrono::milliseconds(2));
954fa812bbSHui       }
964fa812bbSHui     });
974fa812bbSHui 
984fa812bbSHui     std::same_as<bool> auto r = cv.wait(lock, ss.get_token(), [&]() {
994fa812bbSHui       start.store(true);
1004fa812bbSHui       start.notify_all();
1014fa812bbSHui       return false;
1024fa812bbSHui     });
1034fa812bbSHui     assert(!r);
1044fa812bbSHui     done = true;
1054fa812bbSHui     thread.join();
1064fa812bbSHui 
1074fa812bbSHui     assert(lock.owns_lock());
1084fa812bbSHui   }
1094fa812bbSHui 
11085a8e5c3SHui   // #76807 Hangs in std::condition_variable_any when used with std::stop_token
11185a8e5c3SHui   {
11285a8e5c3SHui     class MyThread {
11385a8e5c3SHui     public:
11485a8e5c3SHui       MyThread() {
11585a8e5c3SHui         thread_ = support::make_test_jthread([this](std::stop_token st) {
11685a8e5c3SHui           while (!st.stop_requested()) {
11785a8e5c3SHui             std::unique_lock lock{m_};
11885a8e5c3SHui             cv_.wait(lock, st, [] { return false; });
11985a8e5c3SHui           }
12085a8e5c3SHui         });
12185a8e5c3SHui       }
12285a8e5c3SHui 
12385a8e5c3SHui     private:
12485a8e5c3SHui       std::mutex m_;
12585a8e5c3SHui       std::condition_variable_any cv_;
12685a8e5c3SHui       std::jthread thread_;
12785a8e5c3SHui     };
12885a8e5c3SHui 
12985a8e5c3SHui     [[maybe_unused]] MyThread my_thread;
13085a8e5c3SHui   }
13185a8e5c3SHui 
13285a8e5c3SHui   // request_stop potentially in-between check and wait
13385a8e5c3SHui   {
13485a8e5c3SHui     std::stop_source ss;
13585a8e5c3SHui     std::condition_variable_any cv;
13685a8e5c3SHui     Mutex mutex;
13785a8e5c3SHui     Lock lock{mutex};
13885a8e5c3SHui 
13985a8e5c3SHui     std::atomic_bool pred_started        = false;
14085a8e5c3SHui     std::atomic_bool request_stop_called = false;
14185a8e5c3SHui     auto thread                          = support::make_test_thread([&]() {
14285a8e5c3SHui       pred_started.wait(false);
14385a8e5c3SHui       ss.request_stop();
14485a8e5c3SHui       request_stop_called.store(true);
14585a8e5c3SHui       request_stop_called.notify_all();
14685a8e5c3SHui     });
14785a8e5c3SHui 
14885a8e5c3SHui     std::same_as<bool> auto r = cv.wait(lock, ss.get_token(), [&]() {
14985a8e5c3SHui       pred_started.store(true);
15085a8e5c3SHui       pred_started.notify_all();
15185a8e5c3SHui       request_stop_called.wait(false);
15285a8e5c3SHui       return false;
15385a8e5c3SHui     });
15485a8e5c3SHui     assert(!r);
15585a8e5c3SHui     thread.join();
15685a8e5c3SHui 
15785a8e5c3SHui     assert(lock.owns_lock());
15885a8e5c3SHui   }
15985a8e5c3SHui 
1604fa812bbSHui #if !defined(TEST_HAS_NO_EXCEPTIONS)
1614fa812bbSHui   // Throws: Any exception thrown by pred.
1624fa812bbSHui   {
1634fa812bbSHui     std::stop_source ss;
1644fa812bbSHui     std::condition_variable_any cv;
1654fa812bbSHui     Mutex mutex;
1664fa812bbSHui     Lock lock{mutex};
1674fa812bbSHui 
1684fa812bbSHui     try {
1694fa812bbSHui       cv.wait(lock, ss.get_token(), []() -> bool { throw 5; });
1704fa812bbSHui       assert(false);
1714fa812bbSHui     } catch (int i) {
1724fa812bbSHui       assert(i == 5);
1734fa812bbSHui     }
1744fa812bbSHui   }
1754fa812bbSHui #endif //!defined(TEST_HAS_NO_EXCEPTIONS)
1764fa812bbSHui }
1774fa812bbSHui 
1784fa812bbSHui int main(int, char**) {
1794fa812bbSHui   test<std::mutex, std::unique_lock<std::mutex>>();
1804fa812bbSHui   test<std::shared_mutex, std::shared_lock<std::shared_mutex>>();
1814fa812bbSHui 
1824fa812bbSHui   return 0;
1834fa812bbSHui }
184