| // (C) Copyright 2008 Anthony Williams |
| // Distributed under the Boost Software License, Version 1.0. (See |
| // accompanying file LICENSE_1_0.txt or copy at |
| // http://www.boost.org/LICENSE_1_0.txt) |
| |
| #include <boost/test/unit_test.hpp> |
| #include <boost/thread/mutex.hpp> |
| #include <boost/thread/thread.hpp> |
| #include <boost/thread/locks.hpp> |
| #include <boost/thread/condition_variable.hpp> |
| |
| void test_lock_two_uncontended() |
| { |
| boost::mutex m1,m2; |
| |
| boost::mutex::scoped_lock l1(m1,boost::defer_lock), |
| l2(m2,boost::defer_lock); |
| |
| BOOST_CHECK(!l1.owns_lock()); |
| BOOST_CHECK(!l2.owns_lock()); |
| |
| boost::lock(l1,l2); |
| |
| BOOST_CHECK(l1.owns_lock()); |
| BOOST_CHECK(l2.owns_lock()); |
| } |
| |
| struct wait_data |
| { |
| boost::mutex m; |
| bool flag; |
| boost::condition_variable cond; |
| |
| wait_data(): |
| flag(false) |
| {} |
| |
| void wait() |
| { |
| boost::mutex::scoped_lock l(m); |
| while(!flag) |
| { |
| cond.wait(l); |
| } |
| } |
| |
| template<typename Duration> |
| bool timed_wait(Duration d) |
| { |
| boost::system_time const target=boost::get_system_time()+d; |
| |
| boost::mutex::scoped_lock l(m); |
| while(!flag) |
| { |
| if(!cond.timed_wait(l,target)) |
| { |
| return flag; |
| } |
| } |
| return true; |
| } |
| |
| void signal() |
| { |
| boost::mutex::scoped_lock l(m); |
| flag=true; |
| cond.notify_all(); |
| } |
| }; |
| |
| |
| void lock_mutexes_slowly(boost::mutex* m1,boost::mutex* m2,wait_data* locked,wait_data* quit) |
| { |
| boost::lock_guard<boost::mutex> l1(*m1); |
| boost::this_thread::sleep(boost::posix_time::milliseconds(500)); |
| boost::lock_guard<boost::mutex> l2(*m2); |
| locked->signal(); |
| quit->wait(); |
| } |
| |
| void lock_pair(boost::mutex* m1,boost::mutex* m2) |
| { |
| boost::lock(*m1,*m2); |
| boost::mutex::scoped_lock l1(*m1,boost::adopt_lock), |
| l2(*m2,boost::adopt_lock); |
| } |
| |
| void test_lock_two_other_thread_locks_in_order() |
| { |
| boost::mutex m1,m2; |
| wait_data locked; |
| wait_data release; |
| |
| boost::thread t(lock_mutexes_slowly,&m1,&m2,&locked,&release); |
| boost::this_thread::sleep(boost::posix_time::milliseconds(10)); |
| |
| boost::thread t2(lock_pair,&m1,&m2); |
| BOOST_CHECK(locked.timed_wait(boost::posix_time::seconds(1))); |
| |
| release.signal(); |
| |
| BOOST_CHECK(t2.timed_join(boost::posix_time::seconds(1))); |
| |
| t.join(); |
| } |
| |
| void test_lock_two_other_thread_locks_in_opposite_order() |
| { |
| boost::mutex m1,m2; |
| wait_data locked; |
| wait_data release; |
| |
| boost::thread t(lock_mutexes_slowly,&m1,&m2,&locked,&release); |
| boost::this_thread::sleep(boost::posix_time::milliseconds(10)); |
| |
| boost::thread t2(lock_pair,&m2,&m1); |
| BOOST_CHECK(locked.timed_wait(boost::posix_time::seconds(1))); |
| |
| release.signal(); |
| |
| BOOST_CHECK(t2.timed_join(boost::posix_time::seconds(1))); |
| |
| t.join(); |
| } |
| |
| void test_lock_five_uncontended() |
| { |
| boost::mutex m1,m2,m3,m4,m5; |
| |
| boost::mutex::scoped_lock l1(m1,boost::defer_lock), |
| l2(m2,boost::defer_lock), |
| l3(m3,boost::defer_lock), |
| l4(m4,boost::defer_lock), |
| l5(m5,boost::defer_lock); |
| |
| BOOST_CHECK(!l1.owns_lock()); |
| BOOST_CHECK(!l2.owns_lock()); |
| BOOST_CHECK(!l3.owns_lock()); |
| BOOST_CHECK(!l4.owns_lock()); |
| BOOST_CHECK(!l5.owns_lock()); |
| |
| boost::lock(l1,l2,l3,l4,l5); |
| |
| BOOST_CHECK(l1.owns_lock()); |
| BOOST_CHECK(l2.owns_lock()); |
| BOOST_CHECK(l3.owns_lock()); |
| BOOST_CHECK(l4.owns_lock()); |
| BOOST_CHECK(l5.owns_lock()); |
| } |
| |
| void 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) |
| { |
| boost::lock_guard<boost::mutex> l1(*m1); |
| boost::this_thread::sleep(boost::posix_time::milliseconds(500)); |
| boost::lock_guard<boost::mutex> l2(*m2); |
| boost::this_thread::sleep(boost::posix_time::milliseconds(500)); |
| boost::lock_guard<boost::mutex> l3(*m3); |
| boost::this_thread::sleep(boost::posix_time::milliseconds(500)); |
| boost::lock_guard<boost::mutex> l4(*m4); |
| boost::this_thread::sleep(boost::posix_time::milliseconds(500)); |
| boost::lock_guard<boost::mutex> l5(*m5); |
| locked->signal(); |
| quit->wait(); |
| } |
| |
| void lock_five(boost::mutex* m1,boost::mutex* m2,boost::mutex* m3,boost::mutex* m4,boost::mutex* m5) |
| { |
| boost::lock(*m1,*m2,*m3,*m4,*m5); |
| m1->unlock(); |
| m2->unlock(); |
| m3->unlock(); |
| m4->unlock(); |
| m5->unlock(); |
| } |
| |
| void test_lock_five_other_thread_locks_in_order() |
| { |
| boost::mutex m1,m2,m3,m4,m5; |
| wait_data locked; |
| wait_data release; |
| |
| boost::thread t(lock_five_mutexes_slowly,&m1,&m2,&m3,&m4,&m5,&locked,&release); |
| boost::this_thread::sleep(boost::posix_time::milliseconds(10)); |
| |
| boost::thread t2(lock_five,&m1,&m2,&m3,&m4,&m5); |
| BOOST_CHECK(locked.timed_wait(boost::posix_time::seconds(3))); |
| |
| release.signal(); |
| |
| BOOST_CHECK(t2.timed_join(boost::posix_time::seconds(3))); |
| |
| t.join(); |
| } |
| |
| void test_lock_five_other_thread_locks_in_different_order() |
| { |
| boost::mutex m1,m2,m3,m4,m5; |
| wait_data locked; |
| wait_data release; |
| |
| boost::thread t(lock_five_mutexes_slowly,&m1,&m2,&m3,&m4,&m5,&locked,&release); |
| boost::this_thread::sleep(boost::posix_time::milliseconds(10)); |
| |
| boost::thread t2(lock_five,&m5,&m1,&m4,&m2,&m3); |
| BOOST_CHECK(locked.timed_wait(boost::posix_time::seconds(3))); |
| |
| release.signal(); |
| |
| BOOST_CHECK(t2.timed_join(boost::posix_time::seconds(3))); |
| |
| t.join(); |
| } |
| |
| void lock_n(boost::mutex* mutexes,unsigned count) |
| { |
| boost::lock(mutexes,mutexes+count); |
| for(unsigned i=0;i<count;++i) |
| { |
| mutexes[i].unlock(); |
| } |
| } |
| |
| |
| void test_lock_ten_other_thread_locks_in_different_order() |
| { |
| unsigned const num_mutexes=10; |
| |
| boost::mutex mutexes[num_mutexes]; |
| wait_data locked; |
| wait_data release; |
| |
| boost::thread t(lock_five_mutexes_slowly,&mutexes[6],&mutexes[3],&mutexes[8],&mutexes[0],&mutexes[2],&locked,&release); |
| boost::this_thread::sleep(boost::posix_time::milliseconds(10)); |
| |
| boost::thread t2(lock_n,mutexes,num_mutexes); |
| BOOST_CHECK(locked.timed_wait(boost::posix_time::seconds(3))); |
| |
| release.signal(); |
| |
| BOOST_CHECK(t2.timed_join(boost::posix_time::seconds(3))); |
| |
| t.join(); |
| } |
| |
| struct dummy_mutex |
| { |
| bool is_locked; |
| |
| dummy_mutex(): |
| is_locked(false) |
| {} |
| |
| void lock() |
| { |
| is_locked=true; |
| } |
| |
| bool try_lock() |
| { |
| if(is_locked) |
| { |
| return false; |
| } |
| is_locked=true; |
| return true; |
| } |
| |
| void unlock() |
| { |
| is_locked=false; |
| } |
| }; |
| |
| namespace boost |
| { |
| template<> |
| struct is_mutex_type<dummy_mutex> |
| { |
| BOOST_STATIC_CONSTANT(bool, value = true); |
| }; |
| } |
| |
| |
| |
| void test_lock_five_in_range() |
| { |
| unsigned const num_mutexes=5; |
| dummy_mutex mutexes[num_mutexes]; |
| |
| boost::lock(mutexes,mutexes+num_mutexes); |
| |
| for(unsigned i=0;i<num_mutexes;++i) |
| { |
| BOOST_CHECK(mutexes[i].is_locked); |
| } |
| } |
| |
| class dummy_iterator: |
| public std::iterator<std::forward_iterator_tag, |
| dummy_mutex> |
| { |
| private: |
| dummy_mutex* p; |
| public: |
| explicit dummy_iterator(dummy_mutex* p_): |
| p(p_) |
| {} |
| |
| bool operator==(dummy_iterator const& other) const |
| { |
| return p==other.p; |
| } |
| |
| bool operator!=(dummy_iterator const& other) const |
| { |
| return p!=other.p; |
| } |
| |
| bool operator<(dummy_iterator const& other) const |
| { |
| return p<other.p; |
| } |
| |
| dummy_mutex& operator*() const |
| { |
| return *p; |
| } |
| |
| dummy_mutex* operator->() const |
| { |
| return p; |
| } |
| |
| dummy_iterator operator++(int) |
| { |
| dummy_iterator temp(*this); |
| ++p; |
| return temp; |
| } |
| |
| dummy_iterator& operator++() |
| { |
| ++p; |
| return *this; |
| } |
| |
| }; |
| |
| |
| void test_lock_five_in_range_custom_iterator() |
| { |
| unsigned const num_mutexes=5; |
| dummy_mutex mutexes[num_mutexes]; |
| |
| boost::lock(dummy_iterator(mutexes),dummy_iterator(mutexes+num_mutexes)); |
| |
| for(unsigned i=0;i<num_mutexes;++i) |
| { |
| BOOST_CHECK(mutexes[i].is_locked); |
| } |
| } |
| |
| class dummy_mutex2: |
| public dummy_mutex |
| {}; |
| |
| |
| void test_lock_ten_in_range_inherited_mutex() |
| { |
| unsigned const num_mutexes=10; |
| dummy_mutex2 mutexes[num_mutexes]; |
| |
| boost::lock(mutexes,mutexes+num_mutexes); |
| |
| for(unsigned i=0;i<num_mutexes;++i) |
| { |
| BOOST_CHECK(mutexes[i].is_locked); |
| } |
| } |
| |
| void test_try_lock_two_uncontended() |
| { |
| dummy_mutex m1,m2; |
| |
| int const res=boost::try_lock(m1,m2); |
| |
| BOOST_CHECK(res==-1); |
| BOOST_CHECK(m1.is_locked); |
| BOOST_CHECK(m2.is_locked); |
| } |
| void test_try_lock_two_first_locked() |
| { |
| dummy_mutex m1,m2; |
| m1.lock(); |
| |
| boost::unique_lock<dummy_mutex> l1(m1,boost::defer_lock), |
| l2(m2,boost::defer_lock); |
| |
| int const res=boost::try_lock(l1,l2); |
| |
| BOOST_CHECK(res==0); |
| BOOST_CHECK(m1.is_locked); |
| BOOST_CHECK(!m2.is_locked); |
| BOOST_CHECK(!l1.owns_lock()); |
| BOOST_CHECK(!l2.owns_lock()); |
| } |
| void test_try_lock_two_second_locked() |
| { |
| dummy_mutex m1,m2; |
| m2.lock(); |
| |
| boost::unique_lock<dummy_mutex> l1(m1,boost::defer_lock), |
| l2(m2,boost::defer_lock); |
| |
| int const res=boost::try_lock(l1,l2); |
| |
| BOOST_CHECK(res==1); |
| BOOST_CHECK(!m1.is_locked); |
| BOOST_CHECK(m2.is_locked); |
| BOOST_CHECK(!l1.owns_lock()); |
| BOOST_CHECK(!l2.owns_lock()); |
| } |
| |
| void test_try_lock_three() |
| { |
| int const num_mutexes=3; |
| |
| for(int i=-1;i<num_mutexes;++i) |
| { |
| dummy_mutex mutexes[num_mutexes]; |
| |
| if(i>=0) |
| { |
| mutexes[i].lock(); |
| } |
| boost::unique_lock<dummy_mutex> l1(mutexes[0],boost::defer_lock), |
| l2(mutexes[1],boost::defer_lock), |
| l3(mutexes[2],boost::defer_lock); |
| |
| int const res=boost::try_lock(l1,l2,l3); |
| |
| BOOST_CHECK(res==i); |
| for(int j=0;j<num_mutexes;++j) |
| { |
| if((i==j) || (i==-1)) |
| { |
| BOOST_CHECK(mutexes[j].is_locked); |
| } |
| else |
| { |
| BOOST_CHECK(!mutexes[j].is_locked); |
| } |
| } |
| if(i==-1) |
| { |
| BOOST_CHECK(l1.owns_lock()); |
| BOOST_CHECK(l2.owns_lock()); |
| BOOST_CHECK(l3.owns_lock()); |
| } |
| else |
| { |
| BOOST_CHECK(!l1.owns_lock()); |
| BOOST_CHECK(!l2.owns_lock()); |
| BOOST_CHECK(!l3.owns_lock()); |
| } |
| } |
| } |
| |
| void test_try_lock_four() |
| { |
| int const num_mutexes=4; |
| |
| for(int i=-1;i<num_mutexes;++i) |
| { |
| dummy_mutex mutexes[num_mutexes]; |
| |
| if(i>=0) |
| { |
| mutexes[i].lock(); |
| } |
| boost::unique_lock<dummy_mutex> l1(mutexes[0],boost::defer_lock), |
| l2(mutexes[1],boost::defer_lock), |
| l3(mutexes[2],boost::defer_lock), |
| l4(mutexes[3],boost::defer_lock); |
| |
| int const res=boost::try_lock(l1,l2,l3,l4); |
| |
| BOOST_CHECK(res==i); |
| for(int j=0;j<num_mutexes;++j) |
| { |
| if((i==j) || (i==-1)) |
| { |
| BOOST_CHECK(mutexes[j].is_locked); |
| } |
| else |
| { |
| BOOST_CHECK(!mutexes[j].is_locked); |
| } |
| } |
| if(i==-1) |
| { |
| BOOST_CHECK(l1.owns_lock()); |
| BOOST_CHECK(l2.owns_lock()); |
| BOOST_CHECK(l3.owns_lock()); |
| BOOST_CHECK(l4.owns_lock()); |
| } |
| else |
| { |
| BOOST_CHECK(!l1.owns_lock()); |
| BOOST_CHECK(!l2.owns_lock()); |
| BOOST_CHECK(!l3.owns_lock()); |
| BOOST_CHECK(!l4.owns_lock()); |
| } |
| } |
| } |
| |
| void test_try_lock_five() |
| { |
| int const num_mutexes=5; |
| |
| for(int i=-1;i<num_mutexes;++i) |
| { |
| dummy_mutex mutexes[num_mutexes]; |
| |
| if(i>=0) |
| { |
| mutexes[i].lock(); |
| } |
| boost::unique_lock<dummy_mutex> l1(mutexes[0],boost::defer_lock), |
| l2(mutexes[1],boost::defer_lock), |
| l3(mutexes[2],boost::defer_lock), |
| l4(mutexes[3],boost::defer_lock), |
| l5(mutexes[4],boost::defer_lock); |
| |
| int const res=boost::try_lock(l1,l2,l3,l4,l5); |
| |
| BOOST_CHECK(res==i); |
| for(int j=0;j<num_mutexes;++j) |
| { |
| if((i==j) || (i==-1)) |
| { |
| BOOST_CHECK(mutexes[j].is_locked); |
| } |
| else |
| { |
| BOOST_CHECK(!mutexes[j].is_locked); |
| } |
| } |
| if(i==-1) |
| { |
| BOOST_CHECK(l1.owns_lock()); |
| BOOST_CHECK(l2.owns_lock()); |
| BOOST_CHECK(l3.owns_lock()); |
| BOOST_CHECK(l4.owns_lock()); |
| BOOST_CHECK(l5.owns_lock()); |
| } |
| else |
| { |
| BOOST_CHECK(!l1.owns_lock()); |
| BOOST_CHECK(!l2.owns_lock()); |
| BOOST_CHECK(!l3.owns_lock()); |
| BOOST_CHECK(!l4.owns_lock()); |
| BOOST_CHECK(!l5.owns_lock()); |
| } |
| } |
| } |
| |
| |
| |
| boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) |
| { |
| boost::unit_test_framework::test_suite* test = |
| BOOST_TEST_SUITE("Boost.Threads: generic locks test suite"); |
| |
| test->add(BOOST_TEST_CASE(&test_lock_two_uncontended)); |
| test->add(BOOST_TEST_CASE(&test_lock_two_other_thread_locks_in_order)); |
| test->add(BOOST_TEST_CASE(&test_lock_two_other_thread_locks_in_opposite_order)); |
| test->add(BOOST_TEST_CASE(&test_lock_five_uncontended)); |
| test->add(BOOST_TEST_CASE(&test_lock_five_other_thread_locks_in_order)); |
| test->add(BOOST_TEST_CASE(&test_lock_five_other_thread_locks_in_different_order)); |
| test->add(BOOST_TEST_CASE(&test_lock_five_in_range)); |
| test->add(BOOST_TEST_CASE(&test_lock_five_in_range_custom_iterator)); |
| test->add(BOOST_TEST_CASE(&test_lock_ten_in_range_inherited_mutex)); |
| test->add(BOOST_TEST_CASE(&test_lock_ten_other_thread_locks_in_different_order)); |
| test->add(BOOST_TEST_CASE(&test_try_lock_two_uncontended)); |
| test->add(BOOST_TEST_CASE(&test_try_lock_two_first_locked)); |
| test->add(BOOST_TEST_CASE(&test_try_lock_two_second_locked)); |
| test->add(BOOST_TEST_CASE(&test_try_lock_three)); |
| test->add(BOOST_TEST_CASE(&test_try_lock_four)); |
| test->add(BOOST_TEST_CASE(&test_try_lock_five)); |
| |
| return test; |
| } |