| //===----------------------------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // UNSUPPORTED: no-threads |
| // UNSUPPORTED: c++03, c++11, c++14, c++17 |
| // XFAIL: availability-synchronization_library-missing |
| |
| // <condition_variable> |
| |
| // class condition_variable_any; |
| |
| // template<class Lock, class Predicate> |
| // bool wait(Lock& lock, stop_token stoken, Predicate pred); |
| |
| #include <atomic> |
| #include <cassert> |
| #include <concepts> |
| #include <condition_variable> |
| #include <functional> |
| #include <mutex> |
| #include <shared_mutex> |
| #include <stop_token> |
| #include <thread> |
| |
| #include "make_test_thread.h" |
| #include "test_macros.h" |
| |
| template <class Mutex, class Lock> |
| void test() { |
| // stop_requested before hand |
| { |
| std::stop_source ss; |
| std::condition_variable_any cv; |
| Mutex mutex; |
| Lock lock{mutex}; |
| ss.request_stop(); |
| |
| // [Note 1: The returned value indicates whether the predicate evaluated to true regardless of whether there was a stop request.] |
| std::same_as<bool> auto r1 = cv.wait(lock, ss.get_token(), []() { return false; }); |
| assert(!r1); |
| |
| std::same_as<bool> auto r2 = cv.wait(lock, ss.get_token(), []() { return true; }); |
| assert(r2); |
| |
| // Postconditions: lock is locked by the calling thread. |
| assert(lock.owns_lock()); |
| } |
| |
| // no stop request |
| { |
| std::stop_source ss; |
| std::condition_variable_any cv; |
| Mutex mutex; |
| Lock lock{mutex}; |
| std::same_as<bool> auto r1 = cv.wait(lock, ss.get_token(), []() { return true; }); |
| assert(r1); |
| |
| bool flag = false; |
| auto thread = support::make_test_thread([&]() { |
| std::this_thread::sleep_for(std::chrono::milliseconds(2)); |
| std::unique_lock<Mutex> lock2{mutex}; |
| flag = true; |
| cv.notify_all(); |
| }); |
| |
| std::same_as<bool> auto r2 = cv.wait(lock, ss.get_token(), [&]() { return flag; }); |
| assert(flag); |
| assert(r2); |
| thread.join(); |
| |
| assert(lock.owns_lock()); |
| } |
| |
| // stop request comes while waiting |
| { |
| std::stop_source ss; |
| std::condition_variable_any cv; |
| Mutex mutex; |
| Lock lock{mutex}; |
| |
| std::atomic_bool start = false; |
| std::atomic_bool done = false; |
| auto thread = support::make_test_thread([&]() { |
| start.wait(false); |
| ss.request_stop(); |
| |
| while (!done) { |
| cv.notify_all(); |
| std::this_thread::sleep_for(std::chrono::milliseconds(2)); |
| } |
| }); |
| |
| std::same_as<bool> auto r = cv.wait(lock, ss.get_token(), [&]() { |
| start.store(true); |
| start.notify_all(); |
| return false; |
| }); |
| assert(!r); |
| done = true; |
| thread.join(); |
| |
| assert(lock.owns_lock()); |
| } |
| |
| // #76807 Hangs in std::condition_variable_any when used with std::stop_token |
| { |
| class MyThread { |
| public: |
| MyThread() { |
| thread_ = support::make_test_jthread([this](std::stop_token st) { |
| while (!st.stop_requested()) { |
| std::unique_lock lock{m_}; |
| cv_.wait(lock, st, [] { return false; }); |
| } |
| }); |
| } |
| |
| private: |
| std::mutex m_; |
| std::condition_variable_any cv_; |
| std::jthread thread_; |
| }; |
| |
| [[maybe_unused]] MyThread my_thread; |
| } |
| |
| // request_stop potentially in-between check and wait |
| { |
| std::stop_source ss; |
| std::condition_variable_any cv; |
| Mutex mutex; |
| Lock lock{mutex}; |
| |
| std::atomic_bool pred_started = false; |
| std::atomic_bool request_stop_called = false; |
| auto thread = support::make_test_thread([&]() { |
| pred_started.wait(false); |
| ss.request_stop(); |
| request_stop_called.store(true); |
| request_stop_called.notify_all(); |
| }); |
| |
| std::same_as<bool> auto r = cv.wait(lock, ss.get_token(), [&]() { |
| pred_started.store(true); |
| pred_started.notify_all(); |
| request_stop_called.wait(false); |
| return false; |
| }); |
| assert(!r); |
| thread.join(); |
| |
| assert(lock.owns_lock()); |
| } |
| |
| #if !defined(TEST_HAS_NO_EXCEPTIONS) |
| // Throws: Any exception thrown by pred. |
| { |
| std::stop_source ss; |
| std::condition_variable_any cv; |
| Mutex mutex; |
| Lock lock{mutex}; |
| |
| try { |
| cv.wait(lock, ss.get_token(), []() -> bool { throw 5; }); |
| assert(false); |
| } catch (int i) { |
| assert(i == 5); |
| } |
| } |
| #endif //!defined(TEST_HAS_NO_EXCEPTIONS) |
| } |
| |
| int main(int, char**) { |
| test<std::mutex, std::unique_lock<std::mutex>>(); |
| test<std::shared_mutex, std::shared_lock<std::shared_mutex>>(); |
| |
| return 0; |
| } |