From bb5f17bf04607f1d9770906bf219184cd4780d40 Mon Sep 17 00:00:00 2001 From: "William E. Kempf" Date: Wed, 15 May 2002 17:43:01 +0000 Subject: [PATCH] Bug fixes for rw_mutex and refactoring of tests [SVN r13924] --- src/rw_mutex.cpp | 57 +++++----- test/Jamfile | 15 +-- test/{rw_mutex_test.cpp => test_rw_mutex.cpp} | 100 +++++------------- 3 files changed, 61 insertions(+), 111 deletions(-) rename test/{rw_mutex_test.cpp => test_rw_mutex.cpp} (85%) diff --git a/src/rw_mutex.cpp b/src/rw_mutex.cpp index e957b738..69a27037 100644 --- a/src/rw_mutex.cpp +++ b/src/rw_mutex.cpp @@ -354,42 +354,18 @@ do_rdunlock() { m_waiting_promotion.notify_one(); } - else if(m_state == 0 && m_num_waiting_writers > 0) + else if(m_state == 0) { - // When unlocking a reader and reaching the "idle" state, - // no matter what the scheduling priority is, we'll always - // be waking up a waiting writer. - // - // Q: "But what about sp_reader_priority?" - // A: Any readers would have already obtained the lock, since - // this thread also held a read lock. A reader never has to - // wait when a read lock is held, UNLESS we are giving writers - // priority. - - m_waiting_writers.notify_one(); + do_wakeups(); } - - // Set a flag to help alternating priorities - m_readers_next = 0; } template void rw_mutex_impl:: -do_wrunlock() -{ - // Protect internal state. - Mutex::scoped_lock l(m_prot); - - if(m_state == -1) - m_state = 0; - else - throw lock_error(); - - // After a writer is unlocked, we are always back in the unlocked state. - // - +do_wakeups() +{ if( m_num_waiting_writers > 0 && m_num_waiting_readers > 0) { @@ -435,10 +411,31 @@ do_wrunlock() // Only readers - scheduling doesn't matter m_waiting_readers.notify_all(); } +} + + +template +void +rw_mutex_impl:: +do_wrunlock() +{ + // Protect internal state. + Mutex::scoped_lock l(m_prot); + + if(m_state == -1) + m_state = 0; + else + throw lock_error(); + + // After a writer is unlocked, we are always back in the unlocked state. + // + do_wakeups(); } - } // namespace thread - } // namespace detail + + +} // namespace thread +} // namespace detail diff --git a/test/Jamfile b/test/Jamfile index c23dd024..efd8efd5 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -29,8 +29,12 @@ include threads.jam ; ####################### # Declare the Boost.Threads unit test program test_thread. +# Base names of the source files for test_thread. +CPP_SOURCES = + test_thread test_rw_mutex ; + unit-test test_thread - : test_thread.cpp + : $(CPP_SOURCES).cpp ../build/boost_thread $(threadmon) : $(BOOST_ROOT) @@ -39,12 +43,3 @@ unit-test test_thread : debug release static/dynamic ; -unit-test rw_mutex_test - : rw_mutex_test.cpp - ../build/boost_thread - $(threadmon) - : $(BOOST_ROOT) - $(pthreads-win32) - multi - : debug release static/dynamic -; diff --git a/test/rw_mutex_test.cpp b/test/test_rw_mutex.cpp similarity index 85% rename from test/rw_mutex_test.cpp rename to test/test_rw_mutex.cpp index aceb4fbc..885f2674 100644 --- a/test/rw_mutex_test.cpp +++ b/test/test_rw_mutex.cpp @@ -4,14 +4,11 @@ #include #include -#define BOOST_INCLUDE_MAIN +#include #include -#include - static int shared_val=0; - boost::xtime xsecs(int secs) { boost::xtime ret; @@ -33,18 +30,17 @@ private: RW &_param2; }; - template struct data { - data(int id,RW &m) :m_id(id), m_value(0), m_rw_mutex(m){} + data(int id,RW &m,int secs=0) :m_id(id), m_value(-1), m_secs(secs), m_rw_mutex(m){} int m_value; int m_id; + int m_secs; RW& m_rw_mutex; // Reader/Writer mutex }; - // plain_writer excercises the "infinite" lock for each // RW_mutex type. @@ -62,7 +58,6 @@ void plain_writer(void *arg,RW &rw) pdata->m_value = shared_val; } - template void plain_reader(void *arg,RW &rw) { @@ -72,9 +67,6 @@ void plain_reader(void *arg,RW &rw) pdata->m_value = shared_val; } - - - template void try_writer(void *arg,RW &rw) { @@ -106,15 +98,13 @@ void try_reader(void *arg,RW &rw) } } - - template void timed_writer(void *arg,RW &rw) { data *pdata = (data *) arg; boost::xtime xt; - xt = xsecs(2); + xt = xsecs(pdata->m_secs); RW::scoped_timed_rw_lock l(rw,boost::NO_LOCK); if(l.timed_wrlock(xt)) @@ -130,16 +120,17 @@ template void timed_reader(void *arg,RW &rw) { data *pdata = (data *) arg; - RW::scoped_try_rw_lock l(rw); + boost::xtime xt; + xt = xsecs(pdata->m_secs); - if(l.try_rdlock()) + RW::scoped_timed_rw_lock l(rw,boost::NO_LOCK); + + if(l.timed_rdlock(xt)) { pdata->m_value = shared_val; } } - - template void dump_times(const char *prefix,data *pdata) { @@ -147,14 +138,10 @@ void dump_times(const char *prefix,data *pdata) " In:" << pdata->m_start.LowPart << " Holding:" << pdata->m_holding.LowPart << " Out: " << pdata->m_end.LowPart << std::endl; - - } - template -void -test_plain_rw_mutex(RW &rw_mutex) +void test_plain_rw_mutex(RW &rw_mutex) { shared_val = 0; data r1(1,rw_mutex); @@ -213,11 +200,8 @@ test_plain_rw_mutex(RW &rw_mutex) } } - - template -void -test_try_rw_mutex(RW &rw_mutex) +void test_try_rw_mutex(RW &rw_mutex) { data r1(1,rw_mutex); data w1(2,rw_mutex); @@ -244,8 +228,8 @@ test_try_rw_mutex(RW &rw_mutex) tw1.join(); BOOST_TEST(w1.m_value == 10); - BOOST_TEST(r1.m_value == 0); // Try would return w/o waiting - BOOST_TEST(w2.m_value == 0); // Try would return w/o waiting + BOOST_TEST(r1.m_value == -1); // Try would return w/o waiting + BOOST_TEST(w2.m_value == -1); // Try would return w/o waiting // We finish by repeating the plain tests with the try lock // This is important to verify that try locks are proper rw_mutexes as @@ -254,15 +238,13 @@ test_try_rw_mutex(RW &rw_mutex) } - template -void -test_timed_rw_mutex(RW &rw_mutex) +void test_timed_rw_mutex(RW &rw_mutex) { - data r1(1,rw_mutex); - data r2(2,rw_mutex); - data w1(2,rw_mutex); - data w2(3,rw_mutex); + data r1(1,rw_mutex,1); + data r2(2,rw_mutex,3); + data w1(3,rw_mutex,3); + data w2(4,rw_mutex,1); // We begin with some specialized tests for "timed" behavior @@ -272,19 +254,18 @@ test_timed_rw_mutex(RW &rw_mutex) boost::thread tw1(thread_adapter(timed_writer,&w1,rw_mutex)); boost::thread::sleep(xsecs(1)); + // Writer two will "clearly" try for the lock after the readers + // have tried for it. Writer will wait up 1 second for the lock. This write will fail. + boost::thread tw2(thread_adapter(timed_writer,&w2,rw_mutex)); // Readers one and two will "clearly" try for the lock after writer - // one already holds it. Readers will wait up to 5 seconds for + // one already holds it. 1st reader will wait 1 second, and will fail + // to get the lock. 2nd reader will wait 3 seconds, and will get // the lock. + boost::thread tr1(thread_adapter(timed_reader,&r1,rw_mutex)); boost::thread tr2(thread_adapter(timed_reader,&r2,rw_mutex)); - boost::thread::sleep(xsecs(1)); - // Writer two will "clearly" try for the lock after the readers - // have tries for it. Writer will wait up to 4 seconds for the lock. - boost::thread tw2(thread_adapter(timed_writer,&w2,rw_mutex)); - - tw1.join(); tr1.join(); @@ -293,30 +274,15 @@ test_timed_rw_mutex(RW &rw_mutex) BOOST_TEST(w1.m_value == 10); - BOOST_TEST(r1.m_value == 0); - BOOST_TEST(w2.m_value == 0); + BOOST_TEST(r1.m_value == -1); + BOOST_TEST(r2.m_value == 10); + BOOST_TEST(w2.m_value == -1); // We follow by repeating the try tests with the timed lock. // This is important to verify that timed locks are proper try locks as well test_try_rw_mutex(rw_mutex); } - - - -void test_rw_mutex2() -{ - using namespace boost; - rw_mutex rw(sp_writer_priority); - - { - rw_mutex::scoped_rw_lock l(rw,EXCL_LOCK); - } - { - rw_mutex::scoped_rw_lock l(rw,SHARED_LOCK); - } -} - void test_rw_mutex() { int i; @@ -334,15 +300,7 @@ void test_rw_mutex() std::cout << "try test, sp=" << i << "\n"; test_try_rw_mutex(try_rw); - // std::cout << "timed test, sp=" << i << "\n"; - // test_timed_rw_mutex(timed_rw); + std::cout << "timed test, sp=" << i << "\n"; + test_timed_rw_mutex(timed_rw); } } - -int test_main(int argc,char *argv[]) -{ - test_rw_mutex(); - - return 0; -}; -