132 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// UNSUPPORTED: no-threads, c++0310 11// <condition_variable>12 13// class condition_variable_any;14 15// template <class Lock, class Clock, class Duration>16// cv_status17// wait_until(Lock& lock, const chrono::time_point<Clock, Duration>& abs_time);18 19#include <condition_variable>20#include <atomic>21#include <cassert>22#include <chrono>23#include <mutex>24#include <thread>25 26#include "make_test_thread.h"27#include "test_macros.h"28 29struct TestClock {30 typedef std::chrono::milliseconds duration;31 typedef duration::rep rep;32 typedef duration::period period;33 typedef std::chrono::time_point<TestClock> time_point;34 static const bool is_steady = true;35 36 static time_point now() {37 using namespace std::chrono;38 return time_point(duration_cast<duration>(steady_clock::now().time_since_epoch()));39 }40};41 42template <class Mutex>43struct MyLock : std::unique_lock<Mutex> {44 using std::unique_lock<Mutex>::unique_lock;45};46 47template <class Lock, class Clock>48void test() {49 using Mutex = typename Lock::mutex_type;50 // Test unblocking via a call to notify_one() in another thread.51 //52 // To test this, we set a very long timeout in wait_until() and we wait53 // again in case we get awoken spuriously. Note that it can actually54 // happen that we get awoken spuriously and fail to recognize it55 // (making this test useless), but the likelihood should be small.56 {57 std::atomic<bool> ready(false);58 std::atomic<bool> likely_spurious(true);59 auto timeout = Clock::now() + std::chrono::seconds(3600);60 std::condition_variable_any cv;61 Mutex mutex;62 63 std::thread t1 = support::make_test_thread([&] {64 Lock lock(mutex);65 ready = true;66 do {67 std::cv_status result = cv.wait_until(lock, timeout);68 assert(result == std::cv_status::no_timeout);69 } while (likely_spurious);70 71 // This can technically fail if we have many spurious awakenings, but in practice the72 // tolerance is so high that it shouldn't be a problem.73 assert(Clock::now() < timeout);74 });75 76 std::thread t2 = support::make_test_thread([&] {77 while (!ready) {78 // spin79 }80 81 // Acquire the same mutex as t1. This blocks the condition variable inside its wait call82 // so we can notify it while it is waiting.83 Lock lock(mutex);84 cv.notify_one();85 likely_spurious = false;86 lock.unlock();87 });88 89 t2.join();90 t1.join();91 }92 93 // Test unblocking via a timeout.94 //95 // To test this, we create a thread that waits on a condition variable96 // with a certain timeout, and we never awaken it. To guard against97 // spurious wakeups, we wait again whenever we are awoken for a reason98 // other than a timeout.99 {100 auto timeout = Clock::now() + std::chrono::milliseconds(250);101 std::condition_variable_any cv;102 Mutex mutex;103 104 std::thread t1 = support::make_test_thread([&] {105 Lock lock(mutex);106 std::cv_status result;107 do {108 result = cv.wait_until(lock, timeout);109 if (result == std::cv_status::timeout)110 assert(Clock::now() >= timeout);111 } while (result != std::cv_status::timeout);112 });113 114 t1.join();115 }116}117 118int main(int, char**) {119 test<std::unique_lock<std::mutex>, TestClock>();120 test<std::unique_lock<std::mutex>, std::chrono::steady_clock>();121 122 test<std::unique_lock<std::timed_mutex>, TestClock>();123 test<std::unique_lock<std::timed_mutex>, std::chrono::steady_clock>();124 125 test<MyLock<std::mutex>, TestClock>();126 test<MyLock<std::mutex>, std::chrono::steady_clock>();127 128 test<MyLock<std::timed_mutex>, TestClock>();129 test<MyLock<std::timed_mutex>, std::chrono::steady_clock>();130 return 0;131}132