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