118 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;14 15// template <class Clock, class Duration>16// cv_status17// wait_until(unique_lock<mutex>& lock,18// const chrono::time_point<Clock, Duration>& abs_time);19 20#include <condition_variable>21#include <atomic>22#include <cassert>23#include <chrono>24#include <mutex>25#include <thread>26 27#include "make_test_thread.h"28#include "test_macros.h"29 30struct TestClock {31 typedef std::chrono::milliseconds duration;32 typedef duration::rep rep;33 typedef duration::period period;34 typedef std::chrono::time_point<TestClock> time_point;35 static const bool is_steady = true;36 37 static time_point now() {38 using namespace std::chrono;39 return time_point(duration_cast<duration>(steady_clock::now().time_since_epoch()));40 }41};42 43template <class Clock>44void test() {45 // Test unblocking via a call to notify_one() in another thread.46 //47 // To test this, we set a very long timeout in wait_until() and we wait48 // again in case we get awoken spuriously. Note that it can actually49 // happen that we get awoken spuriously and fail to recognize it50 // (making this test useless), but the likelihood should be small.51 {52 std::atomic<bool> ready(false);53 std::atomic<bool> likely_spurious(true);54 auto timeout = Clock::now() + std::chrono::seconds(3600);55 std::condition_variable cv;56 std::mutex mutex;57 58 std::thread t1 = support::make_test_thread([&] {59 std::unique_lock<std::mutex> lock(mutex);60 ready = true;61 do {62 std::cv_status result = cv.wait_until(lock, timeout);63 assert(result == std::cv_status::no_timeout);64 } while (likely_spurious);65 66 // This can technically fail if we have many spurious awakenings, but in practice the67 // tolerance is so high that it shouldn't be a problem.68 assert(Clock::now() < timeout);69 });70 71 std::thread t2 = support::make_test_thread([&] {72 while (!ready) {73 // spin74 }75 76 // Acquire the same mutex as t1. This blocks the condition variable inside its wait call77 // so we can notify it while it is waiting.78 std::unique_lock<std::mutex> lock(mutex);79 cv.notify_one();80 likely_spurious = false;81 lock.unlock();82 });83 84 t2.join();85 t1.join();86 }87 88 // Test unblocking via a timeout.89 //90 // To test this, we create a thread that waits on a condition variable91 // with a certain timeout, and we never awaken it. To guard against92 // spurious wakeups, we wait again whenever we are awoken for a reason93 // other than a timeout.94 {95 auto timeout = Clock::now() + std::chrono::milliseconds(250);96 std::condition_variable cv;97 std::mutex mutex;98 99 std::thread t1 = support::make_test_thread([&] {100 std::unique_lock<std::mutex> lock(mutex);101 std::cv_status result;102 do {103 result = cv.wait_until(lock, timeout);104 if (result == std::cv_status::timeout)105 assert(Clock::now() >= timeout);106 } while (result != std::cv_status::timeout);107 });108 109 t1.join();110 }111}112 113int main(int, char**) {114 test<TestClock>();115 test<std::chrono::steady_clock>();116 return 0;117}118