blob: 6cdcbe36d98598bb365d25f68ce382974a429d2b [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-has-no-experimental-stop_token
// XFAIL: availability-synchronization_library-missing
// <condition_variable>
// class condition_variable_any;
// template<class Lock, class Clock, class Duration, class Predicate>
// bool wait_until(Lock& lock, stop_token stoken,
// const chrono::time_point<Clock, Duration>& abs_time, Predicate pred);
#include <cassert>
#include <chrono>
#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() {
const auto past = std::chrono::steady_clock::now() - std::chrono::hours(1);
const auto future = std::chrono::steady_clock::now() + std::chrono::hours(1);
// stop_requested before hand
{
std::stop_source ss;
std::condition_variable_any cv;
Mutex mutex;
Lock lock{mutex};
ss.request_stop();
// [Note 4: The returned value indicates whether the predicate evaluated to true
// regardless of whether the timeout was triggered or a stop request was made.]
std::same_as<bool> auto r1 = cv.wait_until(lock, ss.get_token(), past, []() { return false; });
assert(!r1);
std::same_as<bool> auto r2 = cv.wait_until(lock, ss.get_token(), future, []() { return false; });
assert(!r2);
std::same_as<bool> auto r3 = cv.wait_until(lock, ss.get_token(), past, []() { return true; });
assert(r3);
std::same_as<bool> auto r4 = cv.wait_until(lock, ss.get_token(), future, []() { return true; });
assert(r4);
// Postconditions: lock is locked by the calling thread.
assert(lock.owns_lock());
}
// no stop request, pred was true
{
std::stop_source ss;
std::condition_variable_any cv;
Mutex mutex;
Lock lock{mutex};
std::same_as<bool> auto r1 = cv.wait_until(lock, ss.get_token(), past, []() { return true; });
assert(r1);
std::same_as<bool> auto r2 = cv.wait_until(lock, ss.get_token(), future, []() { return true; });
assert(r2);
}
// no stop request, pred was false, abs_time was in the past
{
std::stop_source ss;
std::condition_variable_any cv;
Mutex mutex;
Lock lock{mutex};
std::same_as<bool> auto r1 = cv.wait_until(lock, ss.get_token(), past, []() { return false; });
assert(!r1);
}
// no stop request, pred was false until timeout
{
std::stop_source ss;
std::condition_variable_any cv;
Mutex mutex;
Lock lock{mutex};
auto oldTime = std::chrono::steady_clock::now();
std::same_as<bool> auto r1 =
cv.wait_until(lock, ss.get_token(), oldTime + std::chrono::milliseconds(2), [&]() { return false; });
assert((std::chrono::steady_clock::now() - oldTime) >= std::chrono::milliseconds(2));
assert(!r1);
}
// no stop request, pred was false, changed to true before timeout
{
std::stop_source ss;
std::condition_variable_any cv;
Mutex mutex;
Lock lock{mutex};
bool flag = false;
auto thread = support::make_test_thread([&]() {
std::this_thread::sleep_for(std::chrono::milliseconds(2));
Lock lock2{mutex};
flag = true;
cv.notify_all();
});
std::same_as<bool> auto r1 = cv.wait_until(lock, ss.get_token(), future, [&]() { return flag; });
assert(flag);
assert(r1);
thread.join();
}
// 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_until(lock, ss.get_token(), future, [&]() {
start.store(true);
start.notify_all();
return false;
});
assert(!r);
done = true;
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_until(lock, ss.get_token(), future, []() -> 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;
}