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 
BOOST_AUTO_TEST_CASE(test_lock_two_uncontended)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(l1,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 
wait_datawait_data40     wait_data():
41         flag(false)
42     {}
43 
waitwait_data44     void wait()
45     {
46         boost::unique_lock<boost::mutex> l(m);
47         while(!flag)
48         {
49             cond.wait(l);
50         }
51     }
52 
53     template<typename Duration>
timed_waitwait_data54     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(l,target))
62             {
63                 return flag;
64             }
65         }
66         return true;
67     }
68 
signalwait_data69     void signal()
70     {
71         boost::unique_lock<boost::mutex> l(m);
72         flag=true;
73         cond.notify_all();
74     }
75 };
76 
77 
lock_mutexes_slowly(boost::mutex * m1,boost::mutex * m2,wait_data * locked,wait_data * quit)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(boost::posix_time::milliseconds(500));
82     boost::lock_guard<boost::mutex> l2(*m2);
83     locked->signal();
84     quit->wait();
85 }
86 
lock_pair(boost::mutex * m1,boost::mutex * m2)87 void lock_pair(boost::mutex* m1,boost::mutex* m2)
88 {
89     boost::lock(*m1,*m2);
90     boost::unique_lock<boost::mutex> l1(*m1,boost::adopt_lock),
91         l2(*m2,boost::adopt_lock);
92 }
93 
BOOST_AUTO_TEST_CASE(test_lock_two_other_thread_locks_in_order)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(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 
BOOST_AUTO_TEST_CASE(test_lock_two_other_thread_locks_in_opposite_order)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(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 
BOOST_AUTO_TEST_CASE(test_lock_five_uncontended)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(l1,l2,l3,l4,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 
lock_five_mutexes_slowly(boost::mutex * m1,boost::mutex * m2,boost::mutex * m3,boost::mutex * m4,boost::mutex * m5,wait_data * locked,wait_data * quit)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(boost::posix_time::milliseconds(500));
162     boost::lock_guard<boost::mutex> l2(*m2);
163     boost::this_thread::sleep(boost::posix_time::milliseconds(500));
164     boost::lock_guard<boost::mutex> l3(*m3);
165     boost::this_thread::sleep(boost::posix_time::milliseconds(500));
166     boost::lock_guard<boost::mutex> l4(*m4);
167     boost::this_thread::sleep(boost::posix_time::milliseconds(500));
168     boost::lock_guard<boost::mutex> l5(*m5);
169     locked->signal();
170     quit->wait();
171 }
172 
lock_five(boost::mutex * m1,boost::mutex * m2,boost::mutex * m3,boost::mutex * m4,boost::mutex * m5)173 void lock_five(boost::mutex* m1,boost::mutex* m2,boost::mutex* m3,boost::mutex* m4,boost::mutex* m5)
174 {
175     boost::lock(*m1,*m2,*m3,*m4,*m5);
176     m1->unlock();
177     m2->unlock();
178     m3->unlock();
179     m4->unlock();
180     m5->unlock();
181 }
182 
BOOST_AUTO_TEST_CASE(test_lock_five_other_thread_locks_in_order)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(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 
BOOST_AUTO_TEST_CASE(test_lock_five_other_thread_locks_in_different_order)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(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 
lock_n(boost::mutex * mutexes,unsigned count)221 void lock_n(boost::mutex* mutexes,unsigned count)
222 {
223     boost::lock(mutexes,mutexes+count);
224     for(unsigned i=0;i<count;++i)
225     {
226         mutexes[i].unlock();
227     }
228 }
229 
230 
BOOST_AUTO_TEST_CASE(test_lock_ten_other_thread_locks_in_different_order)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(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 
dummy_mutexdummy_mutex256     dummy_mutex():
257         is_locked(false)
258     {}
259 
lockdummy_mutex260     void lock()
261     {
262         is_locked=true;
263     }
264 
try_lockdummy_mutex265     bool try_lock()
266     {
267         if(is_locked)
268         {
269             return false;
270         }
271         is_locked=true;
272         return true;
273     }
274 
unlockdummy_mutex275     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 
BOOST_AUTO_TEST_CASE(test_lock_five_in_range)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(mutexes,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 
dummy_iterator(dummy_mutex * p_)316     explicit dummy_iterator(dummy_mutex* p_):
317         p(p_)
318     {}
319 
operator ==(dummy_iterator const & other) const320     bool operator==(dummy_iterator const& other) const
321     {
322         return p==other.p;
323     }
324 
operator !=(dummy_iterator const & other) const325     bool operator!=(dummy_iterator const& other) const
326     {
327         return p!=other.p;
328     }
329 
operator <(dummy_iterator const & other) const330     bool operator<(dummy_iterator const& other) const
331     {
332         return p<other.p;
333     }
334 
operator *() const335     dummy_mutex& operator*() const
336     {
337         return *p;
338     }
339 
operator ->() const340     dummy_mutex* operator->() const
341     {
342         return p;
343     }
344 
operator ++(int)345     dummy_iterator operator++(int)
346     {
347         dummy_iterator temp(*this);
348         ++p;
349         return temp;
350     }
351 
operator ++()352     dummy_iterator& operator++()
353     {
354         ++p;
355         return *this;
356     }
357 
358 };
359 
360 
BOOST_AUTO_TEST_CASE(test_lock_five_in_range_custom_iterator)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(dummy_iterator(mutexes),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 
BOOST_AUTO_TEST_CASE(test_lock_ten_in_range_inherited_mutex)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(mutexes,mutexes+num_mutexes);
385 
386     for(unsigned i=0;i<num_mutexes;++i)
387     {
388         BOOST_CHECK(mutexes[i].is_locked);
389     }
390 }
391 
BOOST_AUTO_TEST_CASE(test_try_lock_two_uncontended)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 }
BOOST_AUTO_TEST_CASE(test_try_lock_two_first_locked)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(l1,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 }
BOOST_AUTO_TEST_CASE(test_try_lock_two_second_locked)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(l1,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 
BOOST_AUTO_TEST_CASE(test_try_lock_three)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(l1,l2,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 
BOOST_AUTO_TEST_CASE(test_try_lock_four)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(l1,l2,l3,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 
BOOST_AUTO_TEST_CASE(test_try_lock_five)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(l1,l2,l3,l4,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