|
Boost : |
From: Jeff Mirwaisi (jeff_mirwaisi_at_[hidden])
Date: 2003-10-23 07:28:52
Below is a simple guard type that i have found useful,
there are still a couple of issues, but perhaps its
interesting enough to pursue as a possible addition to
the boost thread library
template <typename T, typename LOCK_TYPE =
empty_mutex, template <typename> class LOCK_ADAPTER =
empty_mutex_adapter > struct locked_ref : private
LOCK_ADAPTER<LOCK_TYPE>
{
struct trule : private LOCK_ADAPTER<LOCK_TYPE>
{
trule(const locked_ref& l) :
LOCK_ADAPTER<LOCK_TYPE>(l),t_(l.t_),release_(true)
{const_cast<locked_ref&>(l).release_=false;}
trule(const trule& rhs) :
LOCK_ADAPTER<LOCK_TYPE>(rhs),t_(rhs.t_),release_(true)
{const_cast<trule&>(rhs).release_=false;}
~trule() {if (release_) this->release();}
T* operator->() const {return &t_;}
T& operator*() const {return t_;}
private:
friend struct locked_ref;
trule(trule&); //discourage use as lvalue, causes
warning in vc 7.1
trule& operator=(const trule&);
T& t_;
bool release_;
};
locked_ref(T& t,LOCK_TYPE& l) :
LOCK_ADAPTER<LOCK_TYPE>(l),t_(t),release_(true)
{this->acquire();}
locked_ref(const trule& tr) :
LOCK_ADAPTER<LOCK_TYPE>(tr),t_(tr.t_),release_(true)
{const_cast<trule&>(tr).release_=false;}
~locked_ref() {if(release_) this->release();}
T* operator->() const {return &t_;}
T& operator*() const {return t_;}
private:
friend struct trule;
//even with trules this should probable be public,
//would allow us to return a temporary locked_ref as
a trule, and to use locked_ref t = locked_ref()
locked_ref(const locked_ref& rhs); //:
LOCK_ADAPTER<LOCK_TYPE>(rhs),t_(rhs.t_),release_(true)
{this->acquire();}
locked_ref & operator=(const locked_ref&);
T& t_;
bool release_;
};
Attached is a simple test application and a
demonstration of a couple of the shortcomings
__________________________________
Do you Yahoo!?
The New Yahoo! Shopping - with improved product search
http://shopping.yahoo.com
#include <iostream>
//fake mutex for testing
struct empty_mutex
{
unsigned int i_;
empty_mutex() : i_(0) {}
void acquire() {std::cout << ++i_ << std::endl;}
void release() {std::cout << --i_ << std::endl;}
};
//pass through adapter
template <typename LOCK_TYPE> struct empty_mutex_adapter
{
empty_mutex_adapter(LOCK_TYPE& m) : m_(m) {}
void acquire() {m_.acquire();}
void release() {m_.release();}
private:
LOCK_TYPE& m_;
};
template <typename T, typename LOCK_TYPE = empty_mutex, template <typename> class LOCK_ADAPTER = empty_mutex_adapter > struct locked_ref : private LOCK_ADAPTER<LOCK_TYPE>
{
struct trule : private LOCK_ADAPTER<LOCK_TYPE>
{
trule(const locked_ref& l) : LOCK_ADAPTER<LOCK_TYPE>(l),t_(l.t_),release_(true) {const_cast<locked_ref&>(l).release_=false;}
trule(const trule& rhs) : LOCK_ADAPTER<LOCK_TYPE>(rhs),t_(rhs.t_),release_(true) {const_cast<trule&>(rhs).release_=false;}
~trule() {if (release_) this->release();}
T* operator->() const {return &t_;}
T& operator*() const {return t_;}
private:
friend struct locked_ref;
trule(trule&); //discourage use as lvalue, causes warning in vc 7.1
trule& operator=(const trule&);
T& t_;
bool release_;
};
locked_ref(T& t,LOCK_TYPE& l) : LOCK_ADAPTER<LOCK_TYPE>(l),t_(t),release_(true) {this->acquire();}
locked_ref(const trule& tr) : LOCK_ADAPTER<LOCK_TYPE>(tr),t_(tr.t_),release_(true) {const_cast<trule&>(tr).release_=false;}
~locked_ref() {if(release_) this->release();}
T* operator->() const {return &t_;}
T& operator*() const {return t_;}
private:
friend struct trule;
//even with trules this should probable be public,
//would allow us to return a temporary locked_ref as a trule, and to use locked_ref t = locked_ref()
locked_ref(const locked_ref& rhs); //: LOCK_ADAPTER<LOCK_TYPE>(rhs),t_(rhs.t_),release_(true) {this->acquire();}
locked_ref & operator=(const locked_ref&);
T& t_;
bool release_;
};
//simple test client
class test
{
empty_mutex mtx_;
int value_;
public:
//hidden copy constructor, so we cant return temporary directly
locked_ref<int>::trule get_value() {locked_ref<int> t(value_,mtx_); return t;}
locked_ref<test>::trule get_test() {locked_ref<test> t(*this,mtx_); return t;}
};
//template parameter deduction fails
template <typename T,typename L> void f0(typename locked_ref<T,L>::trule const& p) {}
template <typename T,typename L> void f1(locked_ref<T,L> const& p) {}
//template function cant deduce T and L above when used with trule
void f2(locked_ref<test>::trule const&) {}
void f3(locked_ref<int>::trule const&) {}
int main(int argc, char* argv[])
{
test t;
std::cout << "*t.get_value()" << std::endl;
*t.get_value()=0;
//self referenceing via get_test, no way to avoid double lock
std::cout << "*t.get_test()->get_value()" << std::endl;
*t.get_test()->get_value()=1;
{
std::cout << "locked_ref<int> li = t.get_value()" << std::endl;
//hidden copy constructor wont allow us to use
//locked_ref<int> li = t.get_value(); syntax
locked_ref<int> li(t.get_value());
std::cout << "*li" << std::endl;
*li=2;
}
{
std::cout << "locked_ref<test> lt = t.get_test()" << std::endl;
locked_ref<test> lt(t.get_test());
std::cout << "*lt->get_value()" << std::endl;
//self reference due to above (li) egt_value() results in double lock
*lt->get_value()=3;
}
std::cout << "f3(t.get_value())" << std::endl;
f3(t.get_value());
std::cout << "f2(t.get_test())" << std::endl;
f2(t.get_test());
//bug: template param deduction fails
//std::cout << "f0(t.get_value())" << std::endl;
//f0(t.get_value());
{
std::cout << "locked_ref<int> li = t.get_value()" << std::endl;
locked_ref<int> li(t.get_value());
std::cout << "f1(li)" << std::endl;
f1(li);
std::cout << "f3(li)" << std::endl;
f3(li);
//bug: above gets converted to a trule, trule destructor releases lock
std::cout << "f3(li)" << std::endl;
f3(li);
}
return 0;
}
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk