
On Mar 22, 2007, at 1:14 PM, Yuval Ronen wrote:
The usual idiom is to have the mutex at namespace scope, or as a class data member, and have the lock referring to that mutex "on the stack". So for the usual idioms, non-copyable, non-movable mutexes do not seem overly problematic.
I'm not sure it's not problematic. If the mutex is class data member (a very common scenario - perhaps the most common one), then it makes that class non-movable. Non-copyable - I understand, but non-movable - that's a major limitation, IMO.
I think we're ok here, both for copying and moving. Here's a prototype class A which owns an int on the heap. And because I think this is a cool use of the read/write locks from N2094 I'm going to use them to implement the copy assignment. :-) I'm typing this code in untested, so please forgive me any careless mistakes. class A { int* the_data_; std::sharable_mutex mut_; typedef std::sharable_lock<std::sharable_mutex> read_lock; typedef std::exclusive_lock<std::sharable_mutex> write_lock; public: explicit A(int data) : the_data_(new int(data)) {} ~A() {delete the_data_;} // copy semantics A(const A& a) : the_data_(0) { read_lock lk(a.mut_); if (a.the_data_ != 0) the_data_ = new int(*a.the_data_); } A& operator=(const A& a) { if (this != &a) { read_lock lock_a(a.mut_, std::defer_lock); write_lock lock_this(mut_, std::defer_lock); std::lock(lock_this, lock_a); // prevents deadlock int* new_data = a.the_data_ ? new int(*a.the_data_) : (int*)0; delete the_data_; the_data_ = new_data; } return *this; } // move semantics A(A&& a) : the_data_(a.the_data_) { a.the_data_ = 0; // no locking required, a is an rvalue! :-) } A& operator=(A&& a) { write_lock lock_this(mut_); delete the_data_; the_data_ = a.the_data_; a.the_data_ = 0; return *this; } }; I.e. you just move or copy the protected data and not the mutex. The cool part here (imho) is the template <class Lock1, class Lock2> void std::lock(Lock1&,Lock2&) function. You don't want to get into a situation like this without deadlock protection: A a1, a2; Thread 1 Thread 2 a1 = a2; a2 = a1; std::lock will do whatever it needs to do to lock both locks without deadlock (personally I'm a fan of the "try and back off" algorithm, but locking in a predefined order is also popular). And std::lock can work with a heterogenous list of locks. :-) -Howard