1 | // (C) Copyright 2006-8 Anthony Williams |
2 | // Distributed under the Boost Software License, Version 1.0. (See |
3 | // accompanying file LICENSE_1_0.txt or copy at |
4 | // http://www.boost.org/LICENSE_1_0.txt) |
5 | |
6 | #define BOOST_THREAD_VERSION 2 |
7 | #define BOOST_TEST_MODULE Boost.Threads: lock_concept test suite |
8 | |
9 | #include <boost/test/unit_test.hpp> |
10 | #include <boost/test/test_case_template.hpp> |
11 | #include <boost/mpl/vector.hpp> |
12 | #include <boost/thread/mutex.hpp> |
13 | #include <boost/thread/lock_types.hpp> |
14 | #include <boost/thread/shared_mutex.hpp> |
15 | #include <boost/thread/thread_only.hpp> |
16 | #include <boost/thread/recursive_mutex.hpp> |
17 | #include <boost/thread/condition_variable.hpp> |
18 | |
19 | template<typename Mutex,typename Lock> |
20 | struct test_initially_locked |
21 | { |
22 | void operator()() const |
23 | { |
24 | Mutex m; |
25 | Lock lock(m); |
26 | |
27 | BOOST_CHECK(lock); |
28 | BOOST_CHECK(lock.owns_lock()); |
29 | } |
30 | }; |
31 | |
32 | template<typename Mutex,typename Lock> |
33 | struct test_initially_unlocked_if_other_thread_has_lock |
34 | { |
35 | Mutex m; |
36 | boost::mutex done_mutex; |
37 | bool done; |
38 | bool locked; |
39 | boost::condition_variable done_cond; |
40 | |
41 | test_initially_unlocked_if_other_thread_has_lock(): |
42 | done(false),locked(false) |
43 | {} |
44 | |
45 | void locking_thread() |
46 | { |
47 | Lock lock(m); |
48 | |
49 | boost::lock_guard<boost::mutex> lk(done_mutex); |
50 | locked=lock.owns_lock(); |
51 | done=true; |
52 | done_cond.notify_one(); |
53 | } |
54 | |
55 | bool is_done() const |
56 | { |
57 | return done; |
58 | } |
59 | |
60 | |
61 | void operator()() |
62 | { |
63 | Lock lock(m); |
64 | |
65 | typedef test_initially_unlocked_if_other_thread_has_lock<Mutex,Lock> this_type; |
66 | |
67 | boost::thread t(&this_type::locking_thread,this); |
68 | |
69 | try |
70 | { |
71 | { |
72 | boost::unique_lock<boost::mutex> lk(done_mutex); |
73 | BOOST_CHECK(done_cond.timed_wait(lk,boost::posix_time::seconds(2), |
74 | boost::bind(&this_type::is_done,this))); |
75 | BOOST_CHECK(!locked); |
76 | } |
77 | |
78 | lock.unlock(); |
79 | t.join(); |
80 | } |
81 | catch(...) |
82 | { |
83 | lock.unlock(); |
84 | t.join(); |
85 | throw; |
86 | } |
87 | } |
88 | }; |
89 | |
90 | template<typename Mutex,typename Lock> |
91 | struct test_initially_unlocked_with_try_lock_if_other_thread_has_unique_lock |
92 | { |
93 | Mutex m; |
94 | boost::mutex done_mutex; |
95 | bool done; |
96 | bool locked; |
97 | boost::condition_variable done_cond; |
98 | |
99 | test_initially_unlocked_with_try_lock_if_other_thread_has_unique_lock(): |
100 | done(false),locked(false) |
101 | {} |
102 | |
103 | void locking_thread() |
104 | { |
105 | Lock lock(m,boost::try_to_lock); |
106 | |
107 | boost::lock_guard<boost::mutex> lk(done_mutex); |
108 | locked=lock.owns_lock(); |
109 | done=true; |
110 | done_cond.notify_one(); |
111 | } |
112 | |
113 | bool is_done() const |
114 | { |
115 | return done; |
116 | } |
117 | |
118 | |
119 | void operator()() |
120 | { |
121 | boost::unique_lock<Mutex> lock(m); |
122 | |
123 | typedef test_initially_unlocked_with_try_lock_if_other_thread_has_unique_lock<Mutex,Lock> this_type; |
124 | |
125 | boost::thread t(&this_type::locking_thread,this); |
126 | |
127 | try |
128 | { |
129 | { |
130 | boost::unique_lock<boost::mutex> lk(done_mutex); |
131 | BOOST_CHECK(done_cond.timed_wait(lk,boost::posix_time::seconds(2), |
132 | boost::bind(&this_type::is_done,this))); |
133 | BOOST_CHECK(!locked); |
134 | } |
135 | |
136 | lock.unlock(); |
137 | t.join(); |
138 | } |
139 | catch(...) |
140 | { |
141 | lock.unlock(); |
142 | t.join(); |
143 | throw; |
144 | } |
145 | } |
146 | }; |
147 | |
148 | template<typename Mutex,typename Lock> |
149 | struct test_initially_locked_if_other_thread_has_shared_lock |
150 | { |
151 | Mutex m; |
152 | boost::mutex done_mutex; |
153 | bool done; |
154 | bool locked; |
155 | boost::condition_variable done_cond; |
156 | |
157 | test_initially_locked_if_other_thread_has_shared_lock(): |
158 | done(false),locked(false) |
159 | {} |
160 | |
161 | void locking_thread() |
162 | { |
163 | Lock lock(m); |
164 | |
165 | boost::lock_guard<boost::mutex> lk(done_mutex); |
166 | locked=lock.owns_lock(); |
167 | done=true; |
168 | done_cond.notify_one(); |
169 | } |
170 | |
171 | bool is_done() const |
172 | { |
173 | return done; |
174 | } |
175 | |
176 | |
177 | void operator()() |
178 | { |
179 | boost::shared_lock<Mutex> lock(m); |
180 | |
181 | typedef test_initially_locked_if_other_thread_has_shared_lock<Mutex,Lock> this_type; |
182 | |
183 | boost::thread t(&this_type::locking_thread,this); |
184 | |
185 | try |
186 | { |
187 | { |
188 | boost::unique_lock<boost::mutex> lk(done_mutex); |
189 | BOOST_CHECK(done_cond.timed_wait(lk,boost::posix_time::seconds(2), |
190 | boost::bind(&this_type::is_done,this))); |
191 | BOOST_CHECK(locked); |
192 | } |
193 | |
194 | lock.unlock(); |
195 | t.join(); |
196 | } |
197 | catch(...) |
198 | { |
199 | lock.unlock(); |
200 | t.join(); |
201 | throw; |
202 | } |
203 | } |
204 | }; |
205 | |
206 | template<typename Mutex,typename Lock> |
207 | struct test_initially_unlocked_with_defer_lock_parameter |
208 | { |
209 | void operator()() const |
210 | { |
211 | Mutex m; |
212 | Lock lock(m,boost::defer_lock); |
213 | |
214 | BOOST_CHECK(!lock); |
215 | BOOST_CHECK(!lock.owns_lock()); |
216 | } |
217 | }; |
218 | |
219 | template<typename Mutex,typename Lock> |
220 | struct test_initially_locked_with_adopt_lock_parameter |
221 | { |
222 | void operator()() const |
223 | { |
224 | Mutex m; |
225 | m.lock(); |
226 | Lock lock(m,boost::adopt_lock); |
227 | |
228 | BOOST_CHECK(lock); |
229 | BOOST_CHECK(lock.owns_lock()); |
230 | } |
231 | }; |
232 | template<typename Mutex,typename Lock> |
233 | struct test_initially_lock_shared_with_adopt_lock_parameter |
234 | { |
235 | void operator()() const |
236 | { |
237 | Mutex m; |
238 | m.lock_shared(); |
239 | Lock lock(m,boost::adopt_lock); |
240 | |
241 | BOOST_CHECK(lock); |
242 | BOOST_CHECK(lock.owns_lock()); |
243 | } |
244 | }; |
245 | |
246 | |
247 | template<typename Mutex,typename Lock> |
248 | struct test_unlocked_after_unlock_called |
249 | { |
250 | void operator()() const |
251 | { |
252 | Mutex m; |
253 | Lock lock(m); |
254 | lock.unlock(); |
255 | BOOST_CHECK(!lock); |
256 | BOOST_CHECK(!lock.owns_lock()); |
257 | } |
258 | }; |
259 | |
260 | template<typename Mutex,typename Lock> |
261 | struct test_locked_after_lock_called |
262 | { |
263 | void operator()() const |
264 | { |
265 | Mutex m; |
266 | Lock lock(m,boost::defer_lock); |
267 | lock.lock(); |
268 | BOOST_CHECK(lock); |
269 | BOOST_CHECK(lock.owns_lock()); |
270 | } |
271 | }; |
272 | |
273 | template<typename Mutex,typename Lock> |
274 | struct test_locked_after_try_lock_called |
275 | { |
276 | void operator()() const |
277 | { |
278 | Mutex m; |
279 | Lock lock(m,boost::defer_lock); |
280 | lock.try_lock(); |
281 | BOOST_CHECK(lock); |
282 | BOOST_CHECK(lock.owns_lock()); |
283 | } |
284 | }; |
285 | |
286 | template<typename Mutex,typename Lock> |
287 | struct test_unlocked_after_try_lock_if_other_thread_has_lock |
288 | { |
289 | Mutex m; |
290 | boost::mutex done_mutex; |
291 | bool done; |
292 | bool locked; |
293 | boost::condition_variable done_cond; |
294 | |
295 | test_unlocked_after_try_lock_if_other_thread_has_lock(): |
296 | done(false),locked(false) |
297 | {} |
298 | |
299 | void locking_thread() |
300 | { |
301 | Lock lock(m,boost::defer_lock); |
302 | |
303 | boost::lock_guard<boost::mutex> lk(done_mutex); |
304 | locked=lock.owns_lock(); |
305 | done=true; |
306 | done_cond.notify_one(); |
307 | } |
308 | |
309 | bool is_done() const |
310 | { |
311 | return done; |
312 | } |
313 | |
314 | |
315 | void operator()() |
316 | { |
317 | Lock lock(m); |
318 | |
319 | typedef test_unlocked_after_try_lock_if_other_thread_has_lock<Mutex,Lock> this_type; |
320 | |
321 | boost::thread t(&this_type::locking_thread,this); |
322 | |
323 | try |
324 | { |
325 | { |
326 | boost::unique_lock<boost::mutex> lk(done_mutex); |
327 | BOOST_CHECK(done_cond.timed_wait(lk,boost::posix_time::seconds(2), |
328 | boost::bind(&this_type::is_done,this))); |
329 | BOOST_CHECK(!locked); |
330 | } |
331 | |
332 | lock.unlock(); |
333 | t.join(); |
334 | } |
335 | catch(...) |
336 | { |
337 | lock.unlock(); |
338 | t.join(); |
339 | throw; |
340 | } |
341 | } |
342 | }; |
343 | |
344 | template<typename Mutex,typename Lock> |
345 | struct test_throws_if_lock_called_when_already_locked |
346 | { |
347 | void operator()() const |
348 | { |
349 | Mutex m; |
350 | Lock lock(m); |
351 | |
352 | BOOST_CHECK_THROW( lock.lock(), boost::lock_error ); |
353 | } |
354 | }; |
355 | |
356 | template<typename Mutex,typename Lock> |
357 | struct test_throws_if_try_lock_called_when_already_locked |
358 | { |
359 | void operator()() const |
360 | { |
361 | Mutex m; |
362 | Lock lock(m); |
363 | |
364 | BOOST_CHECK_THROW( lock.try_lock(), boost::lock_error ); |
365 | } |
366 | }; |
367 | |
368 | template<typename Mutex,typename Lock> |
369 | struct test_throws_if_unlock_called_when_already_unlocked |
370 | { |
371 | void operator()() const |
372 | { |
373 | Mutex m; |
374 | Lock lock(m); |
375 | lock.unlock(); |
376 | |
377 | BOOST_CHECK_THROW( lock.unlock(), boost::lock_error ); |
378 | } |
379 | }; |
380 | template<typename Lock> |
381 | struct test_default_constructed_has_no_mutex_and_unlocked |
382 | { |
383 | void operator()() const |
384 | { |
385 | Lock l; |
386 | BOOST_CHECK(!l.mutex()); |
387 | BOOST_CHECK(!l.owns_lock()); |
388 | } |
389 | }; |
390 | |
391 | |
392 | template<typename Mutex,typename Lock> |
393 | struct test_locks_can_be_swapped |
394 | { |
395 | void operator()() const |
396 | { |
397 | Mutex m1; |
398 | Mutex m2; |
399 | Mutex m3; |
400 | |
401 | Lock l1(m1); |
402 | Lock l2(m2); |
403 | |
404 | BOOST_CHECK_EQUAL(l1.mutex(),&m1); |
405 | BOOST_CHECK_EQUAL(l2.mutex(),&m2); |
406 | |
407 | l1.swap(l2); |
408 | |
409 | BOOST_CHECK_EQUAL(l1.mutex(),&m2); |
410 | BOOST_CHECK_EQUAL(l2.mutex(),&m1); |
411 | |
412 | swap(l1,l2); |
413 | |
414 | BOOST_CHECK_EQUAL(l1.mutex(),&m1); |
415 | BOOST_CHECK_EQUAL(l2.mutex(),&m2); |
416 | |
417 | #if 0 |
418 | l1.swap(Lock(m3)); |
419 | |
420 | BOOST_CHECK_EQUAL(l1.mutex(),&m3); |
421 | #endif |
422 | |
423 | } |
424 | }; |
425 | |
426 | template<typename Mutex,typename Lock> |
427 | void test_lock_is_scoped_lock_concept_for_mutex() |
428 | { |
429 | test_default_constructed_has_no_mutex_and_unlocked<Lock>()(); |
430 | test_initially_locked<Mutex,Lock>()(); |
431 | test_initially_unlocked_with_defer_lock_parameter<Mutex,Lock>()(); |
432 | test_initially_locked_with_adopt_lock_parameter<Mutex,Lock>()(); |
433 | test_unlocked_after_unlock_called<Mutex,Lock>()(); |
434 | test_locked_after_lock_called<Mutex,Lock>()(); |
435 | test_throws_if_lock_called_when_already_locked<Mutex,Lock>()(); |
436 | test_throws_if_unlock_called_when_already_unlocked<Mutex,Lock>()(); |
437 | test_locks_can_be_swapped<Mutex,Lock>()(); |
438 | test_locked_after_try_lock_called<Mutex,Lock>()(); |
439 | test_throws_if_try_lock_called_when_already_locked<Mutex,Lock>()(); |
440 | test_unlocked_after_try_lock_if_other_thread_has_lock<Mutex,Lock>()(); |
441 | } |
442 | |
443 | typedef boost::mpl::vector<boost::mutex,boost::timed_mutex, |
444 | boost::recursive_mutex,boost::recursive_timed_mutex> mutex_types_with_scoped_lock; |
445 | |
446 | BOOST_AUTO_TEST_CASE_TEMPLATE(test_scoped_lock_concept,Mutex,mutex_types_with_scoped_lock) |
447 | { |
448 | typedef typename Mutex::scoped_lock Lock; |
449 | |
450 | test_lock_is_scoped_lock_concept_for_mutex<Mutex,Lock>(); |
451 | } |
452 | |
453 | typedef boost::mpl::vector<boost::mutex,boost::timed_mutex, |
454 | boost::recursive_mutex,boost::recursive_timed_mutex,boost::shared_mutex> all_mutex_types; |
455 | |
456 | BOOST_AUTO_TEST_CASE_TEMPLATE(test_unique_lock_is_scoped_lock,Mutex,all_mutex_types) |
457 | { |
458 | typedef boost::unique_lock<Mutex> Lock; |
459 | |
460 | test_lock_is_scoped_lock_concept_for_mutex<Mutex,Lock>(); |
461 | } |
462 | |
463 | typedef boost::mpl::vector<boost::try_mutex,boost::timed_mutex, |
464 | boost::recursive_try_mutex,boost::recursive_timed_mutex> mutex_types_with_scoped_try_lock; |
465 | |
466 | BOOST_AUTO_TEST_CASE_TEMPLATE(test_scoped_try_lock_concept,Mutex,mutex_types_with_scoped_try_lock) |
467 | { |
468 | typedef typename Mutex::scoped_try_lock Lock; |
469 | |
470 | test_default_constructed_has_no_mutex_and_unlocked<Lock>()(); |
471 | test_initially_locked<Mutex,Lock>()(); |
472 | test_initially_unlocked_if_other_thread_has_lock<Mutex,Lock>()(); |
473 | test_initially_unlocked_with_defer_lock_parameter<Mutex,Lock>()(); |
474 | test_initially_locked_with_adopt_lock_parameter<Mutex,Lock>()(); |
475 | test_unlocked_after_unlock_called<Mutex,Lock>()(); |
476 | test_locked_after_lock_called<Mutex,Lock>()(); |
477 | test_locked_after_try_lock_called<Mutex,Lock>()(); |
478 | test_unlocked_after_try_lock_if_other_thread_has_lock<Mutex,Lock>()(); |
479 | test_throws_if_lock_called_when_already_locked<Mutex,Lock>()(); |
480 | test_throws_if_try_lock_called_when_already_locked<Mutex,Lock>()(); |
481 | test_throws_if_unlock_called_when_already_unlocked<Mutex,Lock>()(); |
482 | test_locks_can_be_swapped<Mutex,Lock>()(); |
483 | } |
484 | |
485 | struct dummy_shared_mutex |
486 | { |
487 | bool locked; |
488 | bool shared_locked; |
489 | bool shared_unlocked; |
490 | bool shared_timed_locked_relative; |
491 | bool shared_timed_locked_absolute; |
492 | bool timed_locked_relative; |
493 | bool timed_locked_absolute; |
494 | |
495 | dummy_shared_mutex(): |
496 | locked(false),shared_locked(false),shared_unlocked(false), |
497 | shared_timed_locked_relative(false), |
498 | shared_timed_locked_absolute(false), |
499 | timed_locked_relative(false), |
500 | timed_locked_absolute(false) |
501 | {} |
502 | |
503 | void lock() |
504 | { |
505 | locked=true; |
506 | } |
507 | |
508 | void lock_shared() |
509 | { |
510 | shared_locked=true; |
511 | } |
512 | |
513 | void unlock() |
514 | {} |
515 | |
516 | void unlock_shared() |
517 | { |
518 | shared_unlocked=true; |
519 | } |
520 | |
521 | bool timed_lock_shared(boost::system_time) |
522 | { |
523 | shared_timed_locked_absolute=true; |
524 | return false; |
525 | } |
526 | template<typename Duration> |
527 | bool timed_lock_shared(Duration) |
528 | { |
529 | shared_timed_locked_relative=true; |
530 | return false; |
531 | } |
532 | bool timed_lock(boost::system_time) |
533 | { |
534 | timed_locked_absolute=true; |
535 | return false; |
536 | } |
537 | template<typename Duration> |
538 | bool timed_lock(Duration) |
539 | { |
540 | timed_locked_relative=true; |
541 | return false; |
542 | } |
543 | |
544 | }; |
545 | |
546 | |
547 | BOOST_AUTO_TEST_CASE(test_shared_lock) |
548 | { |
549 | typedef boost::shared_mutex Mutex; |
550 | typedef boost::shared_lock<Mutex> Lock; |
551 | |
552 | test_default_constructed_has_no_mutex_and_unlocked<Lock>()(); |
553 | test_initially_locked<Mutex,Lock>()(); |
554 | test_initially_unlocked_with_try_lock_if_other_thread_has_unique_lock<Mutex,Lock>()(); |
555 | test_initially_locked_if_other_thread_has_shared_lock<Mutex,Lock>()(); |
556 | test_initially_unlocked_with_defer_lock_parameter<Mutex,Lock>()(); |
557 | test_initially_lock_shared_with_adopt_lock_parameter<Mutex,Lock>()(); |
558 | test_unlocked_after_unlock_called<Mutex,Lock>()(); |
559 | test_locked_after_lock_called<Mutex,Lock>()(); |
560 | test_locked_after_try_lock_called<Mutex,Lock>()(); |
561 | test_throws_if_lock_called_when_already_locked<Mutex,Lock>()(); |
562 | test_throws_if_try_lock_called_when_already_locked<Mutex,Lock>()(); |
563 | test_throws_if_unlock_called_when_already_unlocked<Mutex,Lock>()(); |
564 | test_locks_can_be_swapped<Mutex,Lock>()(); |
565 | |
566 | dummy_shared_mutex dummy; |
567 | boost::shared_lock<dummy_shared_mutex> lk(dummy); |
568 | BOOST_CHECK(dummy.shared_locked); |
569 | lk.unlock(); |
570 | BOOST_CHECK(dummy.shared_unlocked); |
571 | lk.timed_lock(target_time: boost::posix_time::milliseconds(5)); |
572 | BOOST_CHECK(dummy.shared_timed_locked_relative); |
573 | lk.timed_lock(target_time: boost::get_system_time()); |
574 | BOOST_CHECK(dummy.shared_timed_locked_absolute); |
575 | } |
576 | |
577 | //boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) |
578 | //{ |
579 | // boost::unit_test::test_suite* test = |
580 | // BOOST_TEST_SUITE("Boost.Threads: lock concept test suite"); |
581 | // |
582 | // typedef boost::mpl::vector<boost::mutex,boost::try_mutex,boost::timed_mutex, |
583 | // boost::recursive_mutex,boost::recursive_try_mutex,boost::recursive_timed_mutex> mutex_types_with_scoped_lock; |
584 | // |
585 | // test->add(BOOST_TEST_CASE_TEMPLATE(test_scoped_lock_concept,mutex_types_with_scoped_lock)); |
586 | // |
587 | // typedef boost::mpl::vector<boost::try_mutex,boost::timed_mutex, |
588 | // boost::recursive_try_mutex,boost::recursive_timed_mutex> mutex_types_with_scoped_try_lock; |
589 | // |
590 | // test->add(BOOST_TEST_CASE_TEMPLATE(test_scoped_try_lock_concept,mutex_types_with_scoped_try_lock)); |
591 | // |
592 | // typedef boost::mpl::vector<boost::mutex,boost::try_mutex,boost::timed_mutex, |
593 | // boost::recursive_mutex,boost::recursive_try_mutex,boost::recursive_timed_mutex,boost::shared_mutex> all_mutex_types; |
594 | // |
595 | // test->add(BOOST_TEST_CASE_TEMPLATE(test_unique_lock_is_scoped_lock,all_mutex_types)); |
596 | // test->add(BOOST_TEST_CASE(&test_shared_lock)); |
597 | // |
598 | // return test; |
599 | //} |
600 | |
601 | |