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; |
14 | |
15 | // template <class Clock, class Duration> |
16 | // cv_status |
17 | // 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 | |
30 | struct 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>(d: steady_clock::now().time_since_epoch())); |
40 | } |
41 | }; |
42 | |
43 | template <class Clock> |
44 | void 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 wait |
48 | // again in case we get awoken spuriously. Note that it can actually |
49 | // happen that we get awoken spuriously and fail to recognize it |
50 | // (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 the |
67 | // 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 | // spin |
74 | } |
75 | |
76 | // Acquire the same mutex as t1. This blocks the condition variable inside its wait call |
77 | // 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 variable |
91 | // with a certain timeout, and we never awaken it. To guard against |
92 | // spurious wakeups, we wait again whenever we are awoken for a reason |
93 | // 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 | |
113 | int main(int, char**) { |
114 | test<TestClock>(); |
115 | test<std::chrono::steady_clock>(); |
116 | return 0; |
117 | } |
118 | |