1 | |
2 | // Copyright Oliver Kowalke 2013. |
3 | // Distributed under the Boost Software License, Version 1.0. |
4 | // (See accompanying file LICENSE_1_0.txt or copy at |
5 | // http://www.boost.org/LICENSE_1_0.txt) |
6 | // |
7 | // This test is based on the tests of Boost.Thread |
8 | |
9 | #include <chrono> |
10 | #include <cstdlib> |
11 | #include <cstdio> |
12 | #include <iostream> |
13 | #include <map> |
14 | #include <stdexcept> |
15 | #include <vector> |
16 | |
17 | #include <boost/test/unit_test.hpp> |
18 | |
19 | #include <boost/fiber/all.hpp> |
20 | |
21 | typedef std::chrono::nanoseconds ns; |
22 | typedef std::chrono::milliseconds ms; |
23 | |
24 | int value1 = 0; |
25 | |
26 | inline |
27 | std::chrono::system_clock::time_point delay(int secs, int msecs = 0, int /*nsecs*/ = 0) { |
28 | std::chrono::system_clock::time_point t = std::chrono::system_clock::now(); |
29 | t += std::chrono::seconds( secs); |
30 | t += std::chrono::milliseconds( msecs); |
31 | //t += std::chrono::nanoseconds( nsecs); |
32 | |
33 | return t; |
34 | } |
35 | |
36 | struct condition_test_data { |
37 | condition_test_data() : notified(0), awoken(0) { } |
38 | |
39 | boost::fibers::mutex mutex; |
40 | boost::fibers::condition_variable_any cond; |
41 | int notified; |
42 | int awoken; |
43 | }; |
44 | |
45 | void condition_test_fiber(condition_test_data* data) { |
46 | try { |
47 | data->mutex.lock(); |
48 | while (!(data->notified > 0)) |
49 | data->cond.wait(lt&: data->mutex); |
50 | data->awoken++; |
51 | } catch ( ... ) { |
52 | } |
53 | data->mutex.unlock(); |
54 | } |
55 | |
56 | struct cond_predicate { |
57 | cond_predicate(int& var, int val) : _var(var), _val(val) { } |
58 | |
59 | bool operator()() { return _var == _val; } |
60 | |
61 | int& _var; |
62 | int _val; |
63 | private: |
64 | void operator=(cond_predicate&); |
65 | |
66 | }; |
67 | |
68 | void notify_one_fn( boost::fibers::condition_variable_any & cond) { |
69 | cond.notify_one(); |
70 | } |
71 | |
72 | void notify_all_fn( boost::fibers::condition_variable_any & cond) { |
73 | cond.notify_all(); |
74 | } |
75 | |
76 | void wait_fn( |
77 | boost::fibers::mutex & mtx, |
78 | boost::fibers::condition_variable_any & cond) { |
79 | mtx.lock(); |
80 | cond.wait( lt&: mtx); |
81 | ++value1; |
82 | mtx.unlock(); |
83 | } |
84 | |
85 | void test_one_waiter_notify_one() { |
86 | value1 = 0; |
87 | boost::fibers::mutex mtx; |
88 | boost::fibers::condition_variable_any cond; |
89 | |
90 | boost::fibers::fiber f1( |
91 | boost::fibers::launch::dispatch, |
92 | wait_fn, |
93 | std::ref( t&: mtx), |
94 | std::ref( t&: cond) ); |
95 | BOOST_CHECK_EQUAL( 0, value1); |
96 | |
97 | boost::fibers::fiber f2( |
98 | boost::fibers::launch::dispatch, |
99 | notify_one_fn, |
100 | std::ref( t&: cond) ); |
101 | |
102 | BOOST_CHECK_EQUAL( 0, value1); |
103 | |
104 | f1.join(); |
105 | f2.join(); |
106 | |
107 | BOOST_CHECK_EQUAL( 1, value1); |
108 | } |
109 | |
110 | void test_two_waiter_notify_one() { |
111 | value1 = 0; |
112 | boost::fibers::mutex mtx; |
113 | boost::fibers::condition_variable_any cond; |
114 | |
115 | boost::fibers::fiber f1( |
116 | boost::fibers::launch::dispatch, |
117 | wait_fn, |
118 | std::ref( t&: mtx), |
119 | std::ref( t&: cond) ); |
120 | BOOST_CHECK_EQUAL( 0, value1); |
121 | |
122 | boost::fibers::fiber f2( |
123 | boost::fibers::launch::dispatch, |
124 | wait_fn, |
125 | std::ref( t&: mtx), |
126 | std::ref( t&: cond) ); |
127 | BOOST_CHECK_EQUAL( 0, value1); |
128 | |
129 | boost::fibers::fiber f3( |
130 | boost::fibers::launch::dispatch, |
131 | notify_one_fn, |
132 | std::ref( t&: cond) ); |
133 | BOOST_CHECK_EQUAL( 0, value1); |
134 | |
135 | boost::fibers::fiber f4( |
136 | boost::fibers::launch::dispatch, |
137 | notify_one_fn, |
138 | std::ref( t&: cond) ); |
139 | BOOST_CHECK_EQUAL( 1, value1); |
140 | |
141 | f1.join(); |
142 | f2.join(); |
143 | f3.join(); |
144 | f4.join(); |
145 | |
146 | BOOST_CHECK_EQUAL( 2, value1); |
147 | } |
148 | |
149 | void test_two_waiter_notify_all() { |
150 | value1 = 0; |
151 | boost::fibers::mutex mtx; |
152 | boost::fibers::condition_variable_any cond; |
153 | |
154 | boost::fibers::fiber f1( |
155 | boost::fibers::launch::dispatch, |
156 | wait_fn, |
157 | std::ref( t&: mtx), |
158 | std::ref( t&: cond) ); |
159 | BOOST_CHECK_EQUAL( 0, value1); |
160 | |
161 | boost::fibers::fiber f2( |
162 | boost::fibers::launch::dispatch, |
163 | wait_fn, |
164 | std::ref( t&: mtx), |
165 | std::ref( t&: cond) ); |
166 | BOOST_CHECK_EQUAL( 0, value1); |
167 | |
168 | boost::fibers::fiber f3( |
169 | boost::fibers::launch::dispatch, |
170 | notify_all_fn, |
171 | std::ref( t&: cond) ); |
172 | BOOST_CHECK_EQUAL( 0, value1); |
173 | |
174 | boost::fibers::fiber f4( |
175 | boost::fibers::launch::dispatch, |
176 | wait_fn, |
177 | std::ref( t&: mtx), |
178 | std::ref( t&: cond) ); |
179 | BOOST_CHECK_EQUAL( 2, value1); |
180 | |
181 | boost::fibers::fiber f5( |
182 | boost::fibers::launch::dispatch, |
183 | notify_all_fn, |
184 | std::ref( t&: cond) ); |
185 | BOOST_CHECK_EQUAL( 2, value1); |
186 | |
187 | f1.join(); |
188 | f2.join(); |
189 | f3.join(); |
190 | f4.join(); |
191 | f5.join(); |
192 | |
193 | BOOST_CHECK_EQUAL( 3, value1); |
194 | } |
195 | |
196 | int test1 = 0; |
197 | int test2 = 0; |
198 | |
199 | int runs = 0; |
200 | |
201 | void fn1( boost::fibers::mutex & m, boost::fibers::condition_variable_any & cv) { |
202 | m.lock(); |
203 | BOOST_CHECK(test2 == 0); |
204 | test1 = 1; |
205 | cv.notify_one(); |
206 | while (test2 == 0) { |
207 | cv.wait(lt&: m); |
208 | } |
209 | BOOST_CHECK(test2 != 0); |
210 | m.unlock(); |
211 | } |
212 | |
213 | void fn2( boost::fibers::mutex & m, boost::fibers::condition_variable_any & cv) { |
214 | m.lock(); |
215 | BOOST_CHECK(test2 == 0); |
216 | test1 = 1; |
217 | cv.notify_one(); |
218 | std::chrono::system_clock::time_point t0 = std::chrono::system_clock::now(); |
219 | std::chrono::system_clock::time_point t = t0 + ms(250); |
220 | int count=0; |
221 | while (test2 == 0 && cv.wait_until(lt&: m, timeout_time_: t) == boost::fibers::cv_status::no_timeout) |
222 | count++; |
223 | std::chrono::system_clock::time_point t1 = std::chrono::system_clock::now(); |
224 | if (runs == 0) { |
225 | BOOST_CHECK(t1 - t0 < ms(250)); |
226 | BOOST_CHECK(test2 != 0); |
227 | } else { |
228 | BOOST_CHECK(t1 - t0 - ms(250) < ms(count*250+100+1000)); |
229 | BOOST_CHECK(test2 == 0); |
230 | } |
231 | ++runs; |
232 | m.unlock(); |
233 | } |
234 | |
235 | class Pred { |
236 | int & i_; |
237 | |
238 | public: |
239 | explicit Pred(int& i) : |
240 | i_(i) |
241 | {} |
242 | |
243 | bool operator()() |
244 | { return i_ != 0; } |
245 | }; |
246 | |
247 | void fn3( boost::fibers::mutex & m, boost::fibers::condition_variable_any & cv) { |
248 | m.lock(); |
249 | BOOST_CHECK(test2 == 0); |
250 | test1 = 1; |
251 | cv.notify_one(); |
252 | std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); |
253 | std::chrono::steady_clock::time_point t = t0 + ms(250); |
254 | bool r = cv.wait_until(lt&: m, timeout_time: t, pred: Pred(test2)); |
255 | std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); |
256 | if (runs == 0) { |
257 | BOOST_CHECK(t1 - t0 < ms(250)); |
258 | BOOST_CHECK(test2 != 0); |
259 | BOOST_CHECK(r); |
260 | } else { |
261 | BOOST_CHECK(t1 - t0 - ms(250) < ms(250+100)); |
262 | BOOST_CHECK(test2 == 0); |
263 | BOOST_CHECK(!r); |
264 | } |
265 | ++runs; |
266 | m.unlock(); |
267 | } |
268 | |
269 | void fn4( boost::fibers::mutex & m, boost::fibers::condition_variable_any & cv) { |
270 | m.lock(); |
271 | BOOST_CHECK(test2 == 0); |
272 | test1 = 1; |
273 | cv.notify_one(); |
274 | std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); |
275 | int count=0; |
276 | while (test2 == 0 && cv.wait_for(lt&: m, timeout_duration: ms(250)) == boost::fibers::cv_status::no_timeout) |
277 | count++; |
278 | std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); |
279 | if (runs == 0) { |
280 | BOOST_CHECK(t1 - t0 < ms(250)); |
281 | BOOST_CHECK(test2 != 0); |
282 | } else { |
283 | BOOST_CHECK(t1 - t0 - ms(250) < ms(count*250+100+1000)); |
284 | BOOST_CHECK(test2 == 0); |
285 | } |
286 | ++runs; |
287 | m.unlock(); |
288 | } |
289 | |
290 | void fn5( boost::fibers::mutex & m, boost::fibers::condition_variable_any & cv) { |
291 | m.lock(); |
292 | BOOST_CHECK(test2 == 0); |
293 | test1 = 1; |
294 | cv.notify_one(); |
295 | std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); |
296 | int count=0; |
297 | cv.wait_for(lt&: m, timeout_duration: ms(250), pred: Pred(test2)); |
298 | count++; |
299 | std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); |
300 | if (runs == 0) { |
301 | BOOST_CHECK(t1 - t0 < ms(250+1000)); |
302 | BOOST_CHECK(test2 != 0); |
303 | } else { |
304 | BOOST_CHECK(t1 - t0 - ms(250) < ms(count*250+100)); |
305 | BOOST_CHECK(test2 == 0); |
306 | } |
307 | ++runs; |
308 | m.unlock(); |
309 | } |
310 | |
311 | void do_test_condition_wait() { |
312 | test1 = 0; |
313 | test2 = 0; |
314 | runs = 0; |
315 | |
316 | boost::fibers::mutex m; |
317 | boost::fibers::condition_variable_any cv; |
318 | m.lock(); |
319 | boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn1, std::ref( t&: m), std::ref( t&: cv) ); |
320 | BOOST_CHECK(test1 == 0); |
321 | while (test1 == 0) |
322 | cv.wait(lt&: m); |
323 | BOOST_CHECK(test1 != 0); |
324 | test2 = 1; |
325 | m.unlock(); |
326 | cv.notify_one(); |
327 | f.join(); |
328 | } |
329 | |
330 | void test_condition_wait() { |
331 | boost::fibers::fiber( boost::fibers::launch::dispatch, & do_test_condition_wait).join(); |
332 | do_test_condition_wait(); |
333 | } |
334 | |
335 | void do_test_condition_wait_until() { |
336 | test1 = 0; |
337 | test2 = 0; |
338 | runs = 0; |
339 | |
340 | boost::fibers::mutex m; |
341 | boost::fibers::condition_variable_any cv; |
342 | { |
343 | m.lock(); |
344 | boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn2, std::ref( t&: m), std::ref( t&: cv) ); |
345 | BOOST_CHECK(test1 == 0); |
346 | while (test1 == 0) |
347 | cv.wait(lt&: m); |
348 | BOOST_CHECK(test1 != 0); |
349 | test2 = 1; |
350 | m.unlock(); |
351 | cv.notify_one(); |
352 | f.join(); |
353 | } |
354 | test1 = 0; |
355 | test2 = 0; |
356 | { |
357 | m.lock(); |
358 | boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn2, std::ref( t&: m), std::ref( t&: cv) ); |
359 | BOOST_CHECK(test1 == 0); |
360 | while (test1 == 0) |
361 | cv.wait(lt&: m); |
362 | BOOST_CHECK(test1 != 0); |
363 | m.unlock(); |
364 | f.join(); |
365 | } |
366 | } |
367 | |
368 | void test_condition_wait_until() { |
369 | boost::fibers::fiber( boost::fibers::launch::dispatch, & do_test_condition_wait_until).join(); |
370 | do_test_condition_wait_until(); |
371 | } |
372 | |
373 | void do_test_condition_wait_until_pred() { |
374 | test1 = 0; |
375 | test2 = 0; |
376 | runs = 0; |
377 | |
378 | boost::fibers::mutex m; |
379 | boost::fibers::condition_variable_any cv; |
380 | { |
381 | m.lock(); |
382 | boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn3, std::ref( t&: m), std::ref( t&: cv) ); |
383 | BOOST_CHECK(test1 == 0); |
384 | while (test1 == 0) |
385 | cv.wait(lt&: m); |
386 | BOOST_CHECK(test1 != 0); |
387 | test2 = 1; |
388 | m.unlock(); |
389 | cv.notify_one(); |
390 | f.join(); |
391 | } |
392 | test1 = 0; |
393 | test2 = 0; |
394 | { |
395 | m.lock(); |
396 | boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn3, std::ref( t&: m), std::ref( t&: cv) ); |
397 | BOOST_CHECK(test1 == 0); |
398 | while (test1 == 0) |
399 | cv.wait(lt&: m); |
400 | BOOST_CHECK(test1 != 0); |
401 | m.unlock(); |
402 | f.join(); |
403 | } |
404 | } |
405 | |
406 | void test_condition_wait_until_pred() { |
407 | boost::fibers::fiber( boost::fibers::launch::dispatch, & do_test_condition_wait_until_pred).join(); |
408 | do_test_condition_wait_until_pred(); |
409 | } |
410 | |
411 | void do_test_condition_wait_for() { |
412 | test1 = 0; |
413 | test2 = 0; |
414 | runs = 0; |
415 | |
416 | boost::fibers::mutex m; |
417 | boost::fibers::condition_variable_any cv; |
418 | { |
419 | m.lock(); |
420 | boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn4, std::ref( t&: m), std::ref( t&: cv) ); |
421 | BOOST_CHECK(test1 == 0); |
422 | while (test1 == 0) |
423 | cv.wait(lt&: m); |
424 | BOOST_CHECK(test1 != 0); |
425 | test2 = 1; |
426 | m.unlock(); |
427 | cv.notify_one(); |
428 | f.join(); |
429 | } |
430 | test1 = 0; |
431 | test2 = 0; |
432 | { |
433 | m.lock(); |
434 | boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn4, std::ref( t&: m), std::ref( t&: cv) ); |
435 | BOOST_CHECK(test1 == 0); |
436 | while (test1 == 0) |
437 | cv.wait(lt&: m); |
438 | BOOST_CHECK(test1 != 0); |
439 | m.unlock(); |
440 | f.join(); |
441 | } |
442 | } |
443 | |
444 | void test_condition_wait_for() { |
445 | boost::fibers::fiber( boost::fibers::launch::dispatch, & do_test_condition_wait_for).join(); |
446 | do_test_condition_wait_for(); |
447 | } |
448 | |
449 | void do_test_condition_wait_for_pred() { |
450 | test1 = 0; |
451 | test2 = 0; |
452 | runs = 0; |
453 | |
454 | boost::fibers::mutex m; |
455 | boost::fibers::condition_variable_any cv; |
456 | { |
457 | m.lock(); |
458 | boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn5, std::ref( t&: m), std::ref( t&: cv) ); |
459 | BOOST_CHECK(test1 == 0); |
460 | while (test1 == 0) |
461 | cv.wait(lt&: m); |
462 | BOOST_CHECK(test1 != 0); |
463 | test2 = 1; |
464 | m.unlock(); |
465 | cv.notify_one(); |
466 | f.join(); |
467 | } |
468 | test1 = 0; |
469 | test2 = 0; |
470 | { |
471 | m.lock(); |
472 | boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn5, std::ref( t&: m), std::ref( t&: cv) ); |
473 | BOOST_CHECK(test1 == 0); |
474 | while (test1 == 0) |
475 | cv.wait(lt&: m); |
476 | BOOST_CHECK(test1 != 0); |
477 | m.unlock(); |
478 | f.join(); |
479 | } |
480 | } |
481 | |
482 | void test_condition_wait_for_pred() { |
483 | boost::fibers::fiber( boost::fibers::launch::dispatch, & do_test_condition_wait_for_pred).join(); |
484 | do_test_condition_wait_for_pred(); |
485 | } |
486 | |
487 | boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { |
488 | boost::unit_test::test_suite * test = |
489 | BOOST_TEST_SUITE("Boost.Fiber: condition_variable_any test suite" ); |
490 | |
491 | test->add( BOOST_TEST_CASE( & test_one_waiter_notify_one) ); |
492 | test->add( BOOST_TEST_CASE( & test_two_waiter_notify_one) ); |
493 | test->add( BOOST_TEST_CASE( & test_two_waiter_notify_all) ); |
494 | test->add( BOOST_TEST_CASE( & test_condition_wait) ); |
495 | test->add( BOOST_TEST_CASE( & test_condition_wait_until) ); |
496 | test->add( BOOST_TEST_CASE( & test_condition_wait_until_pred) ); |
497 | test->add( BOOST_TEST_CASE( & test_condition_wait_for) ); |
498 | test->add( BOOST_TEST_CASE( & test_condition_wait_for_pred) ); |
499 | |
500 | return test; |
501 | } |
502 | |