1 | // (C) Copyright 2008 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 | |
8 | #define BOOST_TEST_MODULE Boost.Threads: generic locks test suite |
9 | |
10 | #include <boost/test/unit_test.hpp> |
11 | #include <boost/thread/mutex.hpp> |
12 | #include <boost/thread/thread_only.hpp> |
13 | #include <boost/thread/locks.hpp> |
14 | #include <boost/thread/condition_variable.hpp> |
15 | #include <iterator> |
16 | #include <cstddef> |
17 | |
18 | BOOST_AUTO_TEST_CASE(test_lock_two_uncontended) |
19 | { |
20 | boost::mutex m1,m2; |
21 | |
22 | boost::unique_lock<boost::mutex> l1(m1,boost::defer_lock), |
23 | l2(m2,boost::defer_lock); |
24 | |
25 | BOOST_CHECK(!l1.owns_lock()); |
26 | BOOST_CHECK(!l2.owns_lock()); |
27 | |
28 | boost::lock(m1&: l1,m2&: l2); |
29 | |
30 | BOOST_CHECK(l1.owns_lock()); |
31 | BOOST_CHECK(l2.owns_lock()); |
32 | } |
33 | |
34 | struct wait_data |
35 | { |
36 | boost::mutex m; |
37 | bool flag; |
38 | boost::condition_variable cond; |
39 | |
40 | wait_data(): |
41 | flag(false) |
42 | {} |
43 | |
44 | void wait() |
45 | { |
46 | boost::unique_lock<boost::mutex> l(m); |
47 | while(!flag) |
48 | { |
49 | cond.wait(m&: l); |
50 | } |
51 | } |
52 | |
53 | template<typename Duration> |
54 | bool timed_wait(Duration d) |
55 | { |
56 | boost::system_time const target=boost::get_system_time()+d; |
57 | |
58 | boost::unique_lock<boost::mutex> l(m); |
59 | while(!flag) |
60 | { |
61 | if(!cond.timed_wait(m&: l,abs_time: target)) |
62 | { |
63 | return flag; |
64 | } |
65 | } |
66 | return true; |
67 | } |
68 | |
69 | void signal() |
70 | { |
71 | boost::unique_lock<boost::mutex> l(m); |
72 | flag=true; |
73 | cond.notify_all(); |
74 | } |
75 | }; |
76 | |
77 | |
78 | void lock_mutexes_slowly(boost::mutex* m1,boost::mutex* m2,wait_data* locked,wait_data* quit) |
79 | { |
80 | boost::lock_guard<boost::mutex> l1(*m1); |
81 | boost::this_thread::sleep(rel_time: boost::posix_time::milliseconds(500)); |
82 | boost::lock_guard<boost::mutex> l2(*m2); |
83 | locked->signal(); |
84 | quit->wait(); |
85 | } |
86 | |
87 | void lock_pair(boost::mutex* m1,boost::mutex* m2) |
88 | { |
89 | boost::lock(m1&: *m1,m2&: *m2); |
90 | boost::unique_lock<boost::mutex> l1(*m1,boost::adopt_lock), |
91 | l2(*m2,boost::adopt_lock); |
92 | } |
93 | |
94 | BOOST_AUTO_TEST_CASE(test_lock_two_other_thread_locks_in_order) |
95 | { |
96 | boost::mutex m1,m2; |
97 | wait_data locked; |
98 | wait_data release; |
99 | |
100 | boost::thread t(lock_mutexes_slowly,&m1,&m2,&locked,&release); |
101 | boost::this_thread::sleep(rel_time: boost::posix_time::milliseconds(10)); |
102 | |
103 | boost::thread t2(lock_pair,&m1,&m2); |
104 | BOOST_CHECK(locked.timed_wait(boost::posix_time::seconds(1))); |
105 | |
106 | release.signal(); |
107 | |
108 | BOOST_CHECK(t2.timed_join(boost::posix_time::seconds(1))); |
109 | |
110 | t.join(); |
111 | } |
112 | |
113 | BOOST_AUTO_TEST_CASE(test_lock_two_other_thread_locks_in_opposite_order) |
114 | { |
115 | boost::mutex m1,m2; |
116 | wait_data locked; |
117 | wait_data release; |
118 | |
119 | boost::thread t(lock_mutexes_slowly,&m1,&m2,&locked,&release); |
120 | boost::this_thread::sleep(rel_time: boost::posix_time::milliseconds(10)); |
121 | |
122 | boost::thread t2(lock_pair,&m2,&m1); |
123 | BOOST_CHECK(locked.timed_wait(boost::posix_time::seconds(1))); |
124 | |
125 | release.signal(); |
126 | |
127 | BOOST_CHECK(t2.timed_join(boost::posix_time::seconds(1))); |
128 | |
129 | t.join(); |
130 | } |
131 | |
132 | BOOST_AUTO_TEST_CASE(test_lock_five_uncontended) |
133 | { |
134 | boost::mutex m1,m2,m3,m4,m5; |
135 | |
136 | boost::unique_lock<boost::mutex> l1(m1,boost::defer_lock), |
137 | l2(m2,boost::defer_lock), |
138 | l3(m3,boost::defer_lock), |
139 | l4(m4,boost::defer_lock), |
140 | l5(m5,boost::defer_lock); |
141 | |
142 | BOOST_CHECK(!l1.owns_lock()); |
143 | BOOST_CHECK(!l2.owns_lock()); |
144 | BOOST_CHECK(!l3.owns_lock()); |
145 | BOOST_CHECK(!l4.owns_lock()); |
146 | BOOST_CHECK(!l5.owns_lock()); |
147 | |
148 | boost::lock(m1&: l1,m2&: l2,m3&: l3,m4&: l4,m5&: l5); |
149 | |
150 | BOOST_CHECK(l1.owns_lock()); |
151 | BOOST_CHECK(l2.owns_lock()); |
152 | BOOST_CHECK(l3.owns_lock()); |
153 | BOOST_CHECK(l4.owns_lock()); |
154 | BOOST_CHECK(l5.owns_lock()); |
155 | } |
156 | |
157 | void lock_five_mutexes_slowly(boost::mutex* m1,boost::mutex* m2,boost::mutex* m3,boost::mutex* m4,boost::mutex* m5, |
158 | wait_data* locked,wait_data* quit) |
159 | { |
160 | boost::lock_guard<boost::mutex> l1(*m1); |
161 | boost::this_thread::sleep(rel_time: boost::posix_time::milliseconds(500)); |
162 | boost::lock_guard<boost::mutex> l2(*m2); |
163 | boost::this_thread::sleep(rel_time: boost::posix_time::milliseconds(500)); |
164 | boost::lock_guard<boost::mutex> l3(*m3); |
165 | boost::this_thread::sleep(rel_time: boost::posix_time::milliseconds(500)); |
166 | boost::lock_guard<boost::mutex> l4(*m4); |
167 | boost::this_thread::sleep(rel_time: boost::posix_time::milliseconds(500)); |
168 | boost::lock_guard<boost::mutex> l5(*m5); |
169 | locked->signal(); |
170 | quit->wait(); |
171 | } |
172 | |
173 | void lock_five(boost::mutex* m1,boost::mutex* m2,boost::mutex* m3,boost::mutex* m4,boost::mutex* m5) |
174 | { |
175 | boost::lock(m1&: *m1,m2&: *m2,m3&: *m3,m4&: *m4,m5&: *m5); |
176 | m1->unlock(); |
177 | m2->unlock(); |
178 | m3->unlock(); |
179 | m4->unlock(); |
180 | m5->unlock(); |
181 | } |
182 | |
183 | BOOST_AUTO_TEST_CASE(test_lock_five_other_thread_locks_in_order) |
184 | { |
185 | boost::mutex m1,m2,m3,m4,m5; |
186 | wait_data locked; |
187 | wait_data release; |
188 | |
189 | boost::thread t(lock_five_mutexes_slowly,&m1,&m2,&m3,&m4,&m5,&locked,&release); |
190 | boost::this_thread::sleep(rel_time: boost::posix_time::milliseconds(10)); |
191 | |
192 | boost::thread t2(lock_five,&m1,&m2,&m3,&m4,&m5); |
193 | BOOST_CHECK(locked.timed_wait(boost::posix_time::seconds(3))); |
194 | |
195 | release.signal(); |
196 | |
197 | BOOST_CHECK(t2.timed_join(boost::posix_time::seconds(3))); |
198 | |
199 | t.join(); |
200 | } |
201 | |
202 | BOOST_AUTO_TEST_CASE(test_lock_five_other_thread_locks_in_different_order) |
203 | { |
204 | boost::mutex m1,m2,m3,m4,m5; |
205 | wait_data locked; |
206 | wait_data release; |
207 | |
208 | boost::thread t(lock_five_mutexes_slowly,&m1,&m2,&m3,&m4,&m5,&locked,&release); |
209 | boost::this_thread::sleep(rel_time: boost::posix_time::milliseconds(10)); |
210 | |
211 | boost::thread t2(lock_five,&m5,&m1,&m4,&m2,&m3); |
212 | BOOST_CHECK(locked.timed_wait(boost::posix_time::seconds(3))); |
213 | |
214 | release.signal(); |
215 | |
216 | BOOST_CHECK(t2.timed_join(boost::posix_time::seconds(3))); |
217 | |
218 | t.join(); |
219 | } |
220 | |
221 | void lock_n(boost::mutex* mutexes,unsigned count) |
222 | { |
223 | boost::lock(m1&: mutexes,m2: mutexes+count); |
224 | for(unsigned i=0;i<count;++i) |
225 | { |
226 | mutexes[i].unlock(); |
227 | } |
228 | } |
229 | |
230 | |
231 | BOOST_AUTO_TEST_CASE(test_lock_ten_other_thread_locks_in_different_order) |
232 | { |
233 | unsigned const num_mutexes=10; |
234 | |
235 | boost::mutex mutexes[num_mutexes]; |
236 | wait_data locked; |
237 | wait_data release; |
238 | |
239 | boost::thread t(lock_five_mutexes_slowly,&mutexes[6],&mutexes[3],&mutexes[8],&mutexes[0],&mutexes[2],&locked,&release); |
240 | boost::this_thread::sleep(rel_time: boost::posix_time::milliseconds(10)); |
241 | |
242 | boost::thread t2(lock_n,mutexes,num_mutexes); |
243 | BOOST_CHECK(locked.timed_wait(boost::posix_time::seconds(3))); |
244 | |
245 | release.signal(); |
246 | |
247 | BOOST_CHECK(t2.timed_join(boost::posix_time::seconds(3))); |
248 | |
249 | t.join(); |
250 | } |
251 | |
252 | struct dummy_mutex |
253 | { |
254 | bool is_locked; |
255 | |
256 | dummy_mutex(): |
257 | is_locked(false) |
258 | {} |
259 | |
260 | void lock() |
261 | { |
262 | is_locked=true; |
263 | } |
264 | |
265 | bool try_lock() |
266 | { |
267 | if(is_locked) |
268 | { |
269 | return false; |
270 | } |
271 | is_locked=true; |
272 | return true; |
273 | } |
274 | |
275 | void unlock() |
276 | { |
277 | is_locked=false; |
278 | } |
279 | }; |
280 | |
281 | namespace boost |
282 | { |
283 | template<> |
284 | struct is_mutex_type<dummy_mutex> |
285 | { |
286 | BOOST_STATIC_CONSTANT(bool, value = true); |
287 | }; |
288 | } |
289 | |
290 | |
291 | |
292 | BOOST_AUTO_TEST_CASE(test_lock_five_in_range) |
293 | { |
294 | unsigned const num_mutexes=5; |
295 | dummy_mutex mutexes[num_mutexes]; |
296 | |
297 | boost::lock(m1&: mutexes,m2: mutexes+num_mutexes); |
298 | |
299 | for(unsigned i=0;i<num_mutexes;++i) |
300 | { |
301 | BOOST_CHECK(mutexes[i].is_locked); |
302 | } |
303 | } |
304 | |
305 | class dummy_iterator |
306 | { |
307 | private: |
308 | dummy_mutex* p; |
309 | public: |
310 | typedef std::forward_iterator_tag iterator_category; |
311 | typedef dummy_mutex value_type; |
312 | typedef std::ptrdiff_t difference_type; |
313 | typedef dummy_mutex* pointer; |
314 | typedef dummy_mutex& reference; |
315 | |
316 | explicit dummy_iterator(dummy_mutex* p_): |
317 | p(p_) |
318 | {} |
319 | |
320 | bool operator==(dummy_iterator const& other) const |
321 | { |
322 | return p==other.p; |
323 | } |
324 | |
325 | bool operator!=(dummy_iterator const& other) const |
326 | { |
327 | return p!=other.p; |
328 | } |
329 | |
330 | bool operator<(dummy_iterator const& other) const |
331 | { |
332 | return p<other.p; |
333 | } |
334 | |
335 | dummy_mutex& operator*() const |
336 | { |
337 | return *p; |
338 | } |
339 | |
340 | dummy_mutex* operator->() const |
341 | { |
342 | return p; |
343 | } |
344 | |
345 | dummy_iterator operator++(int) |
346 | { |
347 | dummy_iterator temp(*this); |
348 | ++p; |
349 | return temp; |
350 | } |
351 | |
352 | dummy_iterator& operator++() |
353 | { |
354 | ++p; |
355 | return *this; |
356 | } |
357 | |
358 | }; |
359 | |
360 | |
361 | BOOST_AUTO_TEST_CASE(test_lock_five_in_range_custom_iterator) |
362 | { |
363 | unsigned const num_mutexes=5; |
364 | dummy_mutex mutexes[num_mutexes]; |
365 | |
366 | boost::lock(m1: dummy_iterator(mutexes),m2: dummy_iterator(mutexes+num_mutexes)); |
367 | |
368 | for(unsigned i=0;i<num_mutexes;++i) |
369 | { |
370 | BOOST_CHECK(mutexes[i].is_locked); |
371 | } |
372 | } |
373 | |
374 | class dummy_mutex2: |
375 | public dummy_mutex |
376 | {}; |
377 | |
378 | |
379 | BOOST_AUTO_TEST_CASE(test_lock_ten_in_range_inherited_mutex) |
380 | { |
381 | unsigned const num_mutexes=10; |
382 | dummy_mutex2 mutexes[num_mutexes]; |
383 | |
384 | boost::lock(m1&: mutexes,m2: mutexes+num_mutexes); |
385 | |
386 | for(unsigned i=0;i<num_mutexes;++i) |
387 | { |
388 | BOOST_CHECK(mutexes[i].is_locked); |
389 | } |
390 | } |
391 | |
392 | BOOST_AUTO_TEST_CASE(test_try_lock_two_uncontended) |
393 | { |
394 | dummy_mutex m1,m2; |
395 | |
396 | int const res=boost::try_lock(m1,m2); |
397 | |
398 | BOOST_CHECK(res==-1); |
399 | BOOST_CHECK(m1.is_locked); |
400 | BOOST_CHECK(m2.is_locked); |
401 | } |
402 | BOOST_AUTO_TEST_CASE(test_try_lock_two_first_locked) |
403 | { |
404 | dummy_mutex m1,m2; |
405 | m1.lock(); |
406 | |
407 | boost::unique_lock<dummy_mutex> l1(m1,boost::defer_lock), |
408 | l2(m2,boost::defer_lock); |
409 | |
410 | int const res=boost::try_lock(m1&: l1,m2&: l2); |
411 | |
412 | BOOST_CHECK(res==0); |
413 | BOOST_CHECK(m1.is_locked); |
414 | BOOST_CHECK(!m2.is_locked); |
415 | BOOST_CHECK(!l1.owns_lock()); |
416 | BOOST_CHECK(!l2.owns_lock()); |
417 | } |
418 | BOOST_AUTO_TEST_CASE(test_try_lock_two_second_locked) |
419 | { |
420 | dummy_mutex m1,m2; |
421 | m2.lock(); |
422 | |
423 | boost::unique_lock<dummy_mutex> l1(m1,boost::defer_lock), |
424 | l2(m2,boost::defer_lock); |
425 | |
426 | int const res=boost::try_lock(m1&: l1,m2&: l2); |
427 | |
428 | BOOST_CHECK(res==1); |
429 | BOOST_CHECK(!m1.is_locked); |
430 | BOOST_CHECK(m2.is_locked); |
431 | BOOST_CHECK(!l1.owns_lock()); |
432 | BOOST_CHECK(!l2.owns_lock()); |
433 | } |
434 | |
435 | BOOST_AUTO_TEST_CASE(test_try_lock_three) |
436 | { |
437 | int const num_mutexes=3; |
438 | |
439 | for(int i=-1;i<num_mutexes;++i) |
440 | { |
441 | dummy_mutex mutexes[num_mutexes]; |
442 | |
443 | if(i>=0) |
444 | { |
445 | mutexes[i].lock(); |
446 | } |
447 | boost::unique_lock<dummy_mutex> l1(mutexes[0],boost::defer_lock), |
448 | l2(mutexes[1],boost::defer_lock), |
449 | l3(mutexes[2],boost::defer_lock); |
450 | |
451 | int const res=boost::try_lock(m1&: l1,m2&: l2,m3&: l3); |
452 | |
453 | BOOST_CHECK(res==i); |
454 | for(int j=0;j<num_mutexes;++j) |
455 | { |
456 | if((i==j) || (i==-1)) |
457 | { |
458 | BOOST_CHECK(mutexes[j].is_locked); |
459 | } |
460 | else |
461 | { |
462 | BOOST_CHECK(!mutexes[j].is_locked); |
463 | } |
464 | } |
465 | if(i==-1) |
466 | { |
467 | BOOST_CHECK(l1.owns_lock()); |
468 | BOOST_CHECK(l2.owns_lock()); |
469 | BOOST_CHECK(l3.owns_lock()); |
470 | } |
471 | else |
472 | { |
473 | BOOST_CHECK(!l1.owns_lock()); |
474 | BOOST_CHECK(!l2.owns_lock()); |
475 | BOOST_CHECK(!l3.owns_lock()); |
476 | } |
477 | } |
478 | } |
479 | |
480 | BOOST_AUTO_TEST_CASE(test_try_lock_four) |
481 | { |
482 | int const num_mutexes=4; |
483 | |
484 | for(int i=-1;i<num_mutexes;++i) |
485 | { |
486 | dummy_mutex mutexes[num_mutexes]; |
487 | |
488 | if(i>=0) |
489 | { |
490 | mutexes[i].lock(); |
491 | } |
492 | boost::unique_lock<dummy_mutex> l1(mutexes[0],boost::defer_lock), |
493 | l2(mutexes[1],boost::defer_lock), |
494 | l3(mutexes[2],boost::defer_lock), |
495 | l4(mutexes[3],boost::defer_lock); |
496 | |
497 | int const res=boost::try_lock(m1&: l1,m2&: l2,m3&: l3,m4&: l4); |
498 | |
499 | BOOST_CHECK(res==i); |
500 | for(int j=0;j<num_mutexes;++j) |
501 | { |
502 | if((i==j) || (i==-1)) |
503 | { |
504 | BOOST_CHECK(mutexes[j].is_locked); |
505 | } |
506 | else |
507 | { |
508 | BOOST_CHECK(!mutexes[j].is_locked); |
509 | } |
510 | } |
511 | if(i==-1) |
512 | { |
513 | BOOST_CHECK(l1.owns_lock()); |
514 | BOOST_CHECK(l2.owns_lock()); |
515 | BOOST_CHECK(l3.owns_lock()); |
516 | BOOST_CHECK(l4.owns_lock()); |
517 | } |
518 | else |
519 | { |
520 | BOOST_CHECK(!l1.owns_lock()); |
521 | BOOST_CHECK(!l2.owns_lock()); |
522 | BOOST_CHECK(!l3.owns_lock()); |
523 | BOOST_CHECK(!l4.owns_lock()); |
524 | } |
525 | } |
526 | } |
527 | |
528 | BOOST_AUTO_TEST_CASE(test_try_lock_five) |
529 | { |
530 | int const num_mutexes=5; |
531 | |
532 | for(int i=-1;i<num_mutexes;++i) |
533 | { |
534 | dummy_mutex mutexes[num_mutexes]; |
535 | |
536 | if(i>=0) |
537 | { |
538 | mutexes[i].lock(); |
539 | } |
540 | boost::unique_lock<dummy_mutex> l1(mutexes[0],boost::defer_lock), |
541 | l2(mutexes[1],boost::defer_lock), |
542 | l3(mutexes[2],boost::defer_lock), |
543 | l4(mutexes[3],boost::defer_lock), |
544 | l5(mutexes[4],boost::defer_lock); |
545 | |
546 | int const res=boost::try_lock(m1&: l1,m2&: l2,m3&: l3,m4&: l4,m5&: l5); |
547 | |
548 | BOOST_CHECK(res==i); |
549 | for(int j=0;j<num_mutexes;++j) |
550 | { |
551 | if((i==j) || (i==-1)) |
552 | { |
553 | BOOST_CHECK(mutexes[j].is_locked); |
554 | } |
555 | else |
556 | { |
557 | BOOST_CHECK(!mutexes[j].is_locked); |
558 | } |
559 | } |
560 | if(i==-1) |
561 | { |
562 | BOOST_CHECK(l1.owns_lock()); |
563 | BOOST_CHECK(l2.owns_lock()); |
564 | BOOST_CHECK(l3.owns_lock()); |
565 | BOOST_CHECK(l4.owns_lock()); |
566 | BOOST_CHECK(l5.owns_lock()); |
567 | } |
568 | else |
569 | { |
570 | BOOST_CHECK(!l1.owns_lock()); |
571 | BOOST_CHECK(!l2.owns_lock()); |
572 | BOOST_CHECK(!l3.owns_lock()); |
573 | BOOST_CHECK(!l4.owns_lock()); |
574 | BOOST_CHECK(!l5.owns_lock()); |
575 | } |
576 | } |
577 | } |
578 | |
579 | |