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