mirror of
https://github.com/boostorg/thread.git
synced 2026-01-31 20:52:10 +00:00
349 lines
8.1 KiB
C++
349 lines
8.1 KiB
C++
// rw_mutex_test.cpp
|
|
|
|
#include <iostream>
|
|
|
|
#include <boost/thread/thread.hpp>
|
|
#include <boost/thread/xtime.hpp>
|
|
#define BOOST_INCLUDE_MAIN
|
|
#include <boost/test/test_tools.hpp>
|
|
|
|
#include <boost/thread/rw_mutex.hpp>
|
|
|
|
static int shared_val=0;
|
|
|
|
|
|
boost::xtime xsecs(int secs)
|
|
{
|
|
boost::xtime ret;
|
|
boost::xtime_get(&ret, boost::TIME_UTC);
|
|
ret.sec += secs;
|
|
return ret;
|
|
}
|
|
|
|
template<typename RW>
|
|
class thread_adapter
|
|
{
|
|
public:
|
|
thread_adapter(void (*func)(void*,RW &), void* param1,RW ¶m2)
|
|
: _func(func), _param1(param1) ,_param2(param2){ }
|
|
void operator()() const { _func(_param1,_param2); }
|
|
private:
|
|
void (*_func)(void*,RW &);
|
|
void* _param1;
|
|
RW &_param2;
|
|
};
|
|
|
|
|
|
template<typename RW>
|
|
struct data
|
|
{
|
|
data(int id,RW &m) :m_id(id), m_value(0), m_rw_mutex(m){}
|
|
int m_value;
|
|
int m_id;
|
|
|
|
RW& m_rw_mutex; // Reader/Writer mutex
|
|
};
|
|
|
|
|
|
// plain_writer excercises the "infinite" lock for each
|
|
// RW_mutex type.
|
|
|
|
template<typename RW>
|
|
void plain_writer(void *arg,RW &rw)
|
|
{
|
|
data<RW> *pdata = (data<RW> *) arg;
|
|
// std::cout << "-->W" << pdata->m_id << "\n";
|
|
|
|
RW::scoped_rw_lock l(rw,boost::EXCL_LOCK);
|
|
|
|
boost::thread::sleep(xsecs(3));
|
|
shared_val += 10;
|
|
|
|
pdata->m_value = shared_val;
|
|
}
|
|
|
|
|
|
template<typename RW>
|
|
void plain_reader(void *arg,RW &rw)
|
|
{
|
|
data<RW> *pdata = (data<RW> *) arg;
|
|
RW::scoped_rw_lock l(rw,boost::SHARED_LOCK);
|
|
|
|
pdata->m_value = shared_val;
|
|
}
|
|
|
|
|
|
|
|
|
|
template<typename RW>
|
|
void try_writer(void *arg,RW &rw)
|
|
{
|
|
data<RW> *pdata = (data<RW> *) arg;
|
|
// std::cout << "-->W" << pdata->m_id << "\n";
|
|
|
|
RW::scoped_try_rw_lock l(rw,boost::NO_LOCK);
|
|
|
|
if(l.try_wrlock())
|
|
{
|
|
|
|
boost::thread::sleep(xsecs(3));
|
|
shared_val += 10;
|
|
|
|
pdata->m_value = shared_val;
|
|
}
|
|
|
|
}
|
|
|
|
template<typename RW>
|
|
void try_reader(void *arg,RW &rw)
|
|
{
|
|
data<RW> *pdata = (data<RW> *) arg;
|
|
RW::scoped_try_rw_lock l(rw);
|
|
|
|
if(l.try_rdlock())
|
|
{
|
|
pdata->m_value = shared_val;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
template<typename RW>
|
|
void timed_writer(void *arg,RW &rw)
|
|
{
|
|
data<RW> *pdata = (data<RW> *) arg;
|
|
|
|
boost::xtime xt;
|
|
xt = xsecs(2);
|
|
RW::scoped_timed_rw_lock l(rw,boost::NO_LOCK);
|
|
|
|
if(l.timed_wrlock(xt))
|
|
{
|
|
boost::thread::sleep(xsecs(3));
|
|
shared_val += 10;
|
|
|
|
pdata->m_value = shared_val;
|
|
}
|
|
}
|
|
|
|
template<typename RW>
|
|
void timed_reader(void *arg,RW &rw)
|
|
{
|
|
data<RW> *pdata = (data<RW> *) arg;
|
|
RW::scoped_try_rw_lock l(rw);
|
|
|
|
if(l.try_rdlock())
|
|
{
|
|
pdata->m_value = shared_val;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
template<typename RW>
|
|
void dump_times(const char *prefix,data<RW> *pdata)
|
|
{
|
|
std::cout << " " << prefix << pdata->m_id <<
|
|
" In:" << pdata->m_start.LowPart <<
|
|
" Holding:" << pdata->m_holding.LowPart <<
|
|
" Out: " << pdata->m_end.LowPart << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
template<typename RW>
|
|
void
|
|
test_plain_rw_mutex(RW &rw_mutex)
|
|
{
|
|
shared_val = 0;
|
|
data<RW> r1(1,rw_mutex);
|
|
data<RW> r2(2,rw_mutex);
|
|
data<RW> w1(1,rw_mutex);
|
|
data<RW> w2(2,rw_mutex);
|
|
|
|
// Writer one launches, holds the lock for 3 seconds.
|
|
boost::thread tw1(thread_adapter<RW>(plain_writer,&w1,rw_mutex));
|
|
|
|
// Writer two launches, tries to grab the lock, "clearly"
|
|
// after Writer one will already be holding it.
|
|
boost::thread::sleep(xsecs(1));
|
|
boost::thread tw2(thread_adapter<RW>(plain_writer,&w2,rw_mutex));
|
|
|
|
// Reader one launches, "clearly" after writer two, and "clearly"
|
|
// while writer 1 still holds the lock
|
|
boost::thread::sleep(xsecs(1));
|
|
boost::thread tr1(thread_adapter<RW>(plain_reader,&r1,rw_mutex));
|
|
boost::thread tr2(thread_adapter<RW>(plain_reader,&r2,rw_mutex));
|
|
|
|
tr2.join();
|
|
tr1.join();
|
|
tw2.join();
|
|
tw1.join();
|
|
|
|
if(rw_mutex.policy() == boost::sp_writer_priority)
|
|
{
|
|
BOOST_TEST(w1.m_value == 10);
|
|
BOOST_TEST(w2.m_value == 20);
|
|
BOOST_TEST(r1.m_value == 20); // Readers get in after 2nd writer
|
|
BOOST_TEST(r2.m_value == 20);
|
|
}
|
|
else if(rw_mutex.policy() == boost::sp_reader_priority)
|
|
{
|
|
BOOST_TEST(w1.m_value == 10);
|
|
BOOST_TEST(w2.m_value == 20);
|
|
BOOST_TEST(r1.m_value == 10); // Readers get in before 2nd writer
|
|
BOOST_TEST(r2.m_value == 10);
|
|
}
|
|
else if(rw_mutex.policy() == boost::sp_alternating_many_reads)
|
|
{
|
|
BOOST_TEST(w1.m_value == 10);
|
|
BOOST_TEST(w2.m_value == 20);
|
|
BOOST_TEST(r1.m_value == 10); // Readers get in before 2nd writer
|
|
BOOST_TEST(r2.m_value == 10);
|
|
}
|
|
else if(rw_mutex.policy() == boost::sp_alternating_single_reads)
|
|
{
|
|
BOOST_TEST(w1.m_value == 10);
|
|
BOOST_TEST(w2.m_value == 20);
|
|
|
|
// One Reader gets in before 2nd writer, but we can't tell
|
|
// which reader will "win", so just check their sum.
|
|
BOOST_TEST((r1.m_value + r2.m_value == 30));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
template<typename RW>
|
|
void
|
|
test_try_rw_mutex(RW &rw_mutex)
|
|
{
|
|
data<RW> r1(1,rw_mutex);
|
|
data<RW> w1(2,rw_mutex);
|
|
data<RW> w2(3,rw_mutex);
|
|
|
|
// We start with some specialized tests for "try" behavior
|
|
|
|
shared_val = 0;
|
|
|
|
// Writer one launches, holds the lock for 3 seconds.
|
|
|
|
boost::thread tw1(thread_adapter<RW>(try_writer,&w1,rw_mutex));
|
|
|
|
// Reader one launches, "clearly" after writer #1 holds the lock
|
|
// and before it releases the lock.
|
|
boost::thread::sleep(xsecs(1));
|
|
boost::thread tr1(thread_adapter<RW>(try_reader,&r1,rw_mutex));
|
|
|
|
// Writer two launches in the same timeframe.
|
|
boost::thread tw2(thread_adapter<RW>(try_writer,&w2,rw_mutex));
|
|
|
|
tw2.join();
|
|
tr1.join();
|
|
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
|
|
|
|
// We finish by repeating the plain tests with the try lock
|
|
// This is important to verify that try locks are proper rw_mutexes as
|
|
// well.
|
|
test_plain_rw_mutex(rw_mutex);
|
|
|
|
}
|
|
|
|
|
|
template<typename RW>
|
|
void
|
|
test_timed_rw_mutex(RW &rw_mutex)
|
|
{
|
|
data<RW> r1(1,rw_mutex);
|
|
data<RW> r2(2,rw_mutex);
|
|
data<RW> w1(2,rw_mutex);
|
|
data<RW> w2(3,rw_mutex);
|
|
|
|
// We begin with some specialized tests for "timed" behavior
|
|
|
|
shared_val = 0;
|
|
|
|
// Writer one will hold the lock for 3 seconds.
|
|
boost::thread tw1(thread_adapter<RW>(timed_writer,&w1,rw_mutex));
|
|
|
|
boost::thread::sleep(xsecs(1));
|
|
|
|
// Readers one and two will "clearly" try for the lock after writer
|
|
// one already holds it. Readers will wait up to 5 seconds for
|
|
// the lock.
|
|
boost::thread tr1(thread_adapter<RW>(timed_reader,&r1,rw_mutex));
|
|
boost::thread tr2(thread_adapter<RW>(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<RW>(timed_writer,&w2,rw_mutex));
|
|
|
|
|
|
|
|
tw1.join();
|
|
tr1.join();
|
|
tr2.join();
|
|
tw2.join();
|
|
|
|
|
|
BOOST_TEST(w1.m_value == 10);
|
|
BOOST_TEST(r1.m_value == 0);
|
|
BOOST_TEST(w2.m_value == 0);
|
|
|
|
// 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;
|
|
for(i = (int) boost::sp_writer_priority;
|
|
i <= (int) boost::sp_alternating_single_reads;
|
|
i++)
|
|
{
|
|
boost::rw_mutex plain_rw((boost::rw_scheduling_policy) i);
|
|
boost::try_rw_mutex try_rw((boost::rw_scheduling_policy) i);
|
|
boost::timed_rw_mutex timed_rw((boost::rw_scheduling_policy) i);
|
|
|
|
std::cout << "plain test, sp=" << i << "\n";
|
|
test_plain_rw_mutex(plain_rw);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
int test_main(int argc,char *argv[])
|
|
{
|
|
test_rw_mutex();
|
|
|
|
return 0;
|
|
};
|
|
|