c++ - assignment operator for classes having a non copyable boost::mutex -
i reading here old boost thread faq there guideline implement copy-construction , assignment operator classes having boost::mutex
non-copyable object member.
i fine copy constructor have doubt on assignment operator. still valid instruction below?
// old boost thread const counter & operator=( const counter& other ){ if (this == &other) return *this; boost::mutex::scoped_lock lock1(&m_mutex < &other.m_mutex ? m_mutex : other.m_mutex); boost::mutex::scoped_lock lock2(&m_mutex > &other.m_mutex ? m_mutex : other.m_mutex); m_value = other.m_value; return *this; }
should not updated to:
// new boost thread const counter& operator=(const counter& other){ if (this == &other) return *this; boost::unique_lock<boost::mutex> l1(m_mutex, boost::defer_lock); boost::unique_lock<boost::mutex> l2(other.m_mutex, boost::defer_lock); boost::lock(l1,l2); m_value = other.m_value; return *this; }
first of all, assume question avoiding deadlock when locking multiple arbitrary mutexes. important thing use same ordering convention throughout code using set of mutexes. if can guarantee mutex locked before b, b locked before c, , before c, avoid deadlock.
in first code example, convention lock mutex lower memory address first. work fine, address ordering invariant. second version official boost method of avoiding deadlock. documentation not specify ordering performed internally. don't recommend looking in source , using information elsewhere in code - might subtly break things if library changes.
if you're starting scratch (you've not held more 1 mutex @ time in code before), using boost's method preferable - don't have worry exact ordering long leave boost every time. if rest of code uses memory ordering, must use that. if code uses other convention entirely, need apply here, too. under no circumstances should mix conventions within set of locks may held @ same time, that's asking trouble.
to answer question in comment:
a custom lock ordering scheme can useful in circumstances, if need hold locks (a) long time, (b) briefly, while holding long one. example if need run long jobs on objects of type a, briefly affect lots of instances of b. convention acquire lock first, then lock on b object:
void dostuff(a& a, std::list<b*> bs) { boost::unique_lock<boost::mutex> la(a.mutex); // lock throughout (std::list<b*>::iterator ib = bs.begin(); ib != bs.end(); ++ib) { // lock each b 1 loop iteration boost::unique_lock<boost::mutex> lb(ib->mutex); // work on , *ib // ... } }
you may able relinquish lock on between each loop iteration , use boost's/c++0x's lock ordering, depending on dostuff() does, might make algorithm more complicated or confusing.
another example: in runtime environments objects don't stay in same memory location (e.g. due copying garbage collection), relying on memory address ordering won't reliable. might therefore give each object unique id, , base lock ordering on id order.
Comments
Post a Comment