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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // UNSUPPORTED: no-threads, c++03
10
11 // <condition_variable>
12
13 // class condition_variable_any;
14
15 // template <class Lock, class Clock, class Duration>
16 // cv_status
17 // 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
29 struct 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
nowTestClock36 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
42 template <class Mutex>
43 struct MyLock : std::unique_lock<Mutex> {
44 using std::unique_lock<Mutex>::unique_lock;
45 };
46
47 template <class Lock, class Clock>
test()48 void 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 wait
53 // again in case we get awoken spuriously. Note that it can actually
54 // happen that we get awoken spuriously and fail to recognize it
55 // (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 the
72 // 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 // spin
79 }
80
81 // Acquire the same mutex as t1. This blocks the condition variable inside its wait call
82 // 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 variable
96 // with a certain timeout, and we never awaken it. To guard against
97 // spurious wakeups, we wait again whenever we are awoken for a reason
98 // 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
main(int,char **)118 int 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