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 Rep, class Period, class Predicate>
16 //     bool
17 //     wait_for(unique_lock<mutex>& lock,
18 //              const chrono::duration<Rep, Period>& rel_time,
19 //              Predicate pred);
20 
21 #include <condition_variable>
22 #include <atomic>
23 #include <cassert>
24 #include <chrono>
25 #include <mutex>
26 #include <thread>
27 
28 #include "make_test_thread.h"
29 #include "test_macros.h"
30 
31 template <class Function>
measure(Function f)32 std::chrono::microseconds measure(Function f) {
33   std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
34   f();
35   std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
36   return std::chrono::duration_cast<std::chrono::microseconds>(end - start);
37 }
38 
main(int,char **)39 int main(int, char**) {
40   // Test unblocking via a call to notify_one() in another thread.
41   //
42   // To test this, we set a very long timeout in wait_for() and we try to minimize
43   // the likelihood that we got awoken by a spurious wakeup by updating the
44   // likely_spurious flag only immediately before we perform the notification.
45   {
46     std::atomic<bool> ready(false);
47     std::atomic<bool> likely_spurious(true);
48     auto timeout = std::chrono::seconds(3600);
49     std::condition_variable cv;
50     std::mutex mutex;
51 
52     std::thread t1 = support::make_test_thread([&] {
53       std::unique_lock<std::mutex> lock(mutex);
54       auto elapsed = measure([&] {
55         ready       = true;
56         bool result = cv.wait_for(lock, timeout, [&] { return !likely_spurious; });
57         assert(result); // return value should be true since we didn't time out
58       });
59       assert(elapsed < timeout);
60     });
61 
62     std::thread t2 = support::make_test_thread([&] {
63       while (!ready) {
64         // spin
65       }
66 
67       // Acquire the same mutex as t1. This ensures that the condition variable has started
68       // waiting (and hence released that mutex).
69       std::unique_lock<std::mutex> lock(mutex);
70 
71       likely_spurious = false;
72       lock.unlock();
73       cv.notify_one();
74     });
75 
76     t2.join();
77     t1.join();
78   }
79 
80   // Test unblocking via a timeout.
81   //
82   // To test this, we create a thread that waits on a condition variable with a certain
83   // timeout, and we never awaken it. The "stop waiting" predicate always returns false,
84   // which means that we can't get out of the wait via a spurious wakeup.
85   {
86     auto timeout = std::chrono::milliseconds(250);
87     std::condition_variable cv;
88     std::mutex mutex;
89 
90     std::thread t1 = support::make_test_thread([&] {
91       std::unique_lock<std::mutex> lock(mutex);
92       auto elapsed = measure([&] {
93         bool result = cv.wait_for(lock, timeout, [] { return false; }); // never stop waiting (until timeout)
94         assert(!result); // return value should be false since the predicate returns false after the timeout
95       });
96       assert(elapsed >= timeout);
97     });
98 
99     t1.join();
100   }
101 
102   // Test unblocking via a spurious wakeup.
103   //
104   // To test this, we set a fairly long timeout in wait_for() and we basically never
105   // wake up the condition variable. This way, we are hoping to get out of the wait
106   // via a spurious wakeup.
107   //
108   // However, since spurious wakeups are not required to even happen, this test is
109   // only trying to trigger that code path, but not actually asserting that it is
110   // taken. In particular, we do need to eventually ensure we get out of the wait
111   // by standard means, so we actually wake up the thread at the end.
112   {
113     std::atomic<bool> ready(false);
114     std::atomic<bool> awoken(false);
115     auto timeout = std::chrono::seconds(3600);
116     std::condition_variable cv;
117     std::mutex mutex;
118 
119     std::thread t1 = support::make_test_thread([&] {
120       std::unique_lock<std::mutex> lock(mutex);
121       auto elapsed = measure([&] {
122         ready       = true;
123         bool result = cv.wait_for(lock, timeout, [&] { return true; });
124         awoken      = true;
125         assert(result); // return value should be true since we didn't time out
126       });
127       assert(elapsed < timeout); // can technically fail if t2 never executes and we timeout, but very unlikely
128     });
129 
130     std::thread t2 = support::make_test_thread([&] {
131       while (!ready) {
132         // spin
133       }
134 
135       // Acquire the same mutex as t1. This ensures that the condition variable has started
136       // waiting (and hence released that mutex).
137       std::unique_lock<std::mutex> lock(mutex);
138       lock.unlock();
139 
140       // Give some time for t1 to be awoken spuriously so that code path is used.
141       std::this_thread::sleep_for(std::chrono::seconds(1));
142 
143       // We would want to assert that the thread has been awoken after this time,
144       // however nothing guarantees us that it ever gets spuriously awoken, so
145       // we can't really check anything. This is still left here as documentation.
146       bool woke = awoken.load();
147       assert(woke || !woke);
148 
149       // Whatever happened, actually awaken the condition variable to ensure the test
150       // doesn't keep running until the timeout.
151       cv.notify_one();
152     });
153 
154     t2.join();
155     t1.join();
156   }
157 
158   return 0;
159 }
160