1 //===----------------------------------------------------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is dual licensed under the MIT and the University of Illinois Open
6 // Source Licenses. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 // Copyright (C) 2017 Austin J. Beer
10 //
11 // Distributed under the Boost Software License, Version 1.0. (See accompanying
12 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
13
14 // <boost/thread/condition_variable>
15
16 // class condition_variable;
17
18 // condition_variable(const condition_variable&) = delete;
19
20 #include <iostream>
21 #include <boost/thread/condition_variable.hpp>
22 #include <boost/thread/mutex.hpp>
23 #include <boost/thread/thread.hpp>
24 #include <boost/detail/lightweight_test.hpp>
25 #include <cassert>
26
27 // Summary of each test:
28 // 1. Start the test thread and wait for it to start up.
29 // The test thread waits for the flag to be set using a large timeout.
30 // 2. The main thread takes the lock and then sleeps for a long time while holding
31 // the lock before setting the flag and calling notify_one(). If the wait
32 // function being tested is polling pthread_cond_timedwait() internally, any
33 // notifications sent after pthread_cond_timedwait() times out but before it can
34 // reacquire the lock may be "lost". pthread_cond_timedwait() will report that
35 // it timed out and the wait function may incorrectly assume that no
36 // notification was received. This test ensures that that doesn't happen.
37 // 3. Measure how it takes the test thread to return. If it received the
38 // notification, it will return fairly quickly. If it missed the notification,
39 // the test thread won't return until the wait function being tested times out.
40
41 //------------------------------------------------------------------------------
42
43 boost::condition_variable cv;
44 boost::mutex mut;
45
46 bool flag;
47 bool waiting;
48
flagIsSet()49 bool flagIsSet()
50 {
51 return flag;
52 }
53
threadIsWaiting()54 bool threadIsWaiting()
55 {
56 return waiting;
57 }
58
59 //------------------------------------------------------------------------------
60
61 #ifdef BOOST_THREAD_USES_DATETIME
62
63 boost::posix_time::milliseconds posix_wait_time(1000);
64
65 template <typename F>
test_posix_wait_function(F f)66 void test_posix_wait_function(F f)
67 {
68 flag = false;
69 waiting = false;
70 boost::thread t(f);
71 while (!threadIsWaiting())
72 {
73 boost::this_thread::sleep(boost::posix_time::milliseconds(1));
74 }
75
76 boost::unique_lock<boost::mutex> lk(mut);
77 boost::this_thread::sleep(boost::posix_time::milliseconds(500));
78 boost::posix_time::ptime t0 = boost::posix_time::microsec_clock::universal_time();
79 flag = true;
80 cv.notify_one();
81 lk.unlock();
82 t.join();
83 boost::posix_time::ptime t1 = boost::posix_time::microsec_clock::universal_time();
84
85 BOOST_TEST(t1 - t0 < boost::posix_time::milliseconds(250));
86 }
87
88 //------------------------------------------------------------------------------
89
timed_wait_absolute_without_pred()90 void timed_wait_absolute_without_pred()
91 {
92 boost::unique_lock<boost::mutex> lk(mut);
93 waiting = true;
94 while (!flagIsSet())
95 {
96 cv.timed_wait(lk, boost::posix_time::microsec_clock::universal_time() + posix_wait_time);
97 }
98 }
99
timed_wait_absolute_with_pred()100 void timed_wait_absolute_with_pred()
101 {
102 boost::unique_lock<boost::mutex> lk(mut);
103 waiting = true;
104 cv.timed_wait(lk, boost::posix_time::microsec_clock::universal_time() + posix_wait_time, flagIsSet);
105 }
106
107 //------------------------------------------------------------------------------
108
timed_wait_relative_without_pred()109 void timed_wait_relative_without_pred()
110 {
111 boost::unique_lock<boost::mutex> lk(mut);
112 waiting = true;
113 while (!flagIsSet())
114 {
115 cv.timed_wait(lk, posix_wait_time);
116 }
117 }
118
timed_wait_relative_with_pred()119 void timed_wait_relative_with_pred()
120 {
121 boost::unique_lock<boost::mutex> lk(mut);
122 waiting = true;
123 cv.timed_wait(lk, posix_wait_time, flagIsSet);
124 }
125
126 #else
127 #error "Test not applicable: BOOST_THREAD_USES_DATETIME not defined for this platform as not supported"
128 #endif
129
130 //------------------------------------------------------------------------------
131
132 #ifdef BOOST_THREAD_USES_CHRONO
133
134 boost::chrono::milliseconds chrono_wait_time(1000);
135
136 template <typename F>
test_chrono_wait_function(F f)137 void test_chrono_wait_function(F f)
138 {
139 flag = false;
140 waiting = false;
141 boost::thread t(f);
142 while (!threadIsWaiting())
143 {
144 boost::this_thread::sleep_for(boost::chrono::milliseconds(1));
145 }
146
147 boost::unique_lock<boost::mutex> lk(mut);
148 boost::this_thread::sleep_for(boost::chrono::milliseconds(500));
149 boost::chrono::steady_clock::time_point t0 = boost::chrono::steady_clock::now();
150 flag = true;
151 cv.notify_one();
152 lk.unlock();
153 t.join();
154 boost::chrono::steady_clock::time_point t1 = boost::chrono::steady_clock::now();
155
156 BOOST_TEST(t1 - t0 < boost::chrono::milliseconds(250));
157 }
158
159 //------------------------------------------------------------------------------
160
wait_until_system_without_pred()161 void wait_until_system_without_pred()
162 {
163 boost::unique_lock<boost::mutex> lk(mut);
164 waiting = true;
165 while (!flagIsSet())
166 {
167 cv.wait_until(lk, boost::chrono::system_clock::now() + chrono_wait_time);
168 }
169 }
170
wait_until_system_with_pred()171 void wait_until_system_with_pred()
172 {
173 boost::unique_lock<boost::mutex> lk(mut);
174 waiting = true;
175 cv.wait_until(lk, boost::chrono::system_clock::now() + chrono_wait_time, flagIsSet);
176 }
177
178 //------------------------------------------------------------------------------
179
wait_until_steady_without_pred()180 void wait_until_steady_without_pred()
181 {
182 boost::unique_lock<boost::mutex> lk(mut);
183 waiting = true;
184 while (!flagIsSet())
185 {
186 cv.wait_until(lk, boost::chrono::steady_clock::now() + chrono_wait_time);
187 }
188 }
189
wait_until_steady_with_pred()190 void wait_until_steady_with_pred()
191 {
192 boost::unique_lock<boost::mutex> lk(mut);
193 waiting = true;
194 cv.wait_until(lk, boost::chrono::steady_clock::now() + chrono_wait_time, flagIsSet);
195 }
196
197 //------------------------------------------------------------------------------
198
wait_for_without_pred()199 void wait_for_without_pred()
200 {
201 boost::unique_lock<boost::mutex> lk(mut);
202 waiting = true;
203 while (!flagIsSet())
204 {
205 cv.wait_for(lk, chrono_wait_time);
206 }
207 }
208
wait_for_with_pred()209 void wait_for_with_pred()
210 {
211 boost::unique_lock<boost::mutex> lk(mut);
212 waiting = true;
213 cv.wait_for(lk, chrono_wait_time, flagIsSet);
214 }
215
216 #else
217 #error "Test not applicable: BOOST_THREAD_USES_CHRONO not defined for this platform as not supported"
218 #endif
219
220 //------------------------------------------------------------------------------
221
main()222 int main()
223 {
224 #ifdef BOOST_THREAD_USES_DATETIME
225 test_posix_wait_function(timed_wait_absolute_without_pred);
226 test_posix_wait_function(timed_wait_absolute_with_pred);
227 test_posix_wait_function(timed_wait_relative_without_pred);
228 test_posix_wait_function(timed_wait_relative_with_pred);
229 #endif
230
231 #ifdef BOOST_THREAD_USES_CHRONO
232 test_chrono_wait_function(wait_until_system_without_pred);
233 test_chrono_wait_function(wait_until_system_with_pred);
234 test_chrono_wait_function(wait_until_steady_without_pred);
235 test_chrono_wait_function(wait_until_steady_with_pred);
236 test_chrono_wait_function(wait_for_without_pred);
237 test_chrono_wait_function(wait_for_with_pred);
238 #endif
239
240 return boost::report_errors();
241 }
242