Subj : Re: Improving read-write lock To : comp.programming.threads From : Joe Seigh Date : Thu Feb 17 2005 07:13 pm On Thu, 17 Feb 2005 22:03:30 GMT, Howard Hinnant wrote: > In article , > "Joe Seigh" wrote: > >> On Thu, 17 Feb 2005 19:22:18 GMT, Howard Hinnant >> wrote: >> >> > >> > The part of this interface dealing with move semantics may have >> > significant advantages in the not too far future such as the ability to >> > return locks from functions (even locked locks), and the ability to put >> > locks into standard containers without the fear of accidently >> > transferring or leaking mutex ownership. >> >> I'm not even sure what moving a lock means, much less what the advantages >> would be. > > In this interface (as well as the boost::threads interface it is based > on), scoped_lock is a non-copyable object which holds a reference > to a mutex. The scoped_lock is responsible for locking the mutex upon > construction and unlocking it on destruction. > > A movable lock honors the non-copyability of this interface, with a > couple of exceptions. You can "copy" an rvalue scoped_lock to another. > As the rvalue scoped_lock is about to be destructed anyway, mutex > ownership is transferred from the source to the target. As the source > destructs, it no longer is obligated to unlock the mutex. Instead that > responsibility is now the target's. > > For example: > > class A > { > public: > ... > scoped_lock lock() {return scoped_lock(mut_);} > ... > private: > mutex mut_; > }; > > ... > A a; > > void foo() > { > // here "a.mut_" is unlocked > { > scoped_lock a_lock = a.lock(); > // here "a.mut_" is locked > } > // here "a.mut_" is unlocked > } > > In this example a local scoped_lock is created internal to A::lock(). > That local scoped_lock is moved (not copied) to the client's a_lock, > transferring ownership of mut_ as it goes. The scope of a_lock is > effectively the scope of the lock on a.mut_. > > A::lock() instead could have returned a reference to mut_, but that may > expose A's internals more than the author of A is comfortable with. > > A::lock() instead could lock the mut_. But then you need a A::unlock() > which might forget to be called (especially if an exception is thrown > somewhere after the lock). You need to be careful about automatically unlocking locks for uncaught exceptions. The lock should be unlocked if it can be determined that no damages has occurred. Otherwise the lock should be left locked or changed to damaged state. [...] > > There may be some applications that benefit from an array of > scoped_locks, and the array size may not be determined until run time > (perhaps a database with an array of locks where each lock controls only > a limited range in the database). A move-aware std::vector could hold > moveable locks, without risk of accidental transfer of mutex ownership. > Standard algorithms could help manipulate those locks. > > For example: > > std::vector > locks; > > struct is_not_locked > { > bool operator()(const scoped_lock& l) {return !l.locked();} > }; > > foo() > { > locks.erase( > std::remove_if(locks.begin(), > locks.end(), > is_not_locked()), > locks.end()); > } > > Here foo() uses std::remove_if and std::vector to destruct all locks > which are currently not locked in the vector. > > vector will use the scoped_lock's movability to manage its internal > buffer as the vector grows, sometimes needing to move a scoped_lock from > an existing buffer to a larger buffer. > > remove_if will use the scoped_lock's movability to rearrange the vector > such that all currently locked scoped_locks are moved to the front of > the vector (such an operation would naturally need further > synchronization). You'll need some kind of further synchronization. scoped_lock<>::locked() returns bool, not const bool. > > I'm currently exploring applications where moving locks may be > beneficial. The above is some of the capability that moving locks gives > you. I am interested in feedback as to whether any of this capability > might prove useful. > This seems rather analogous to auto_ptr replete with all the problems that auto_ptr suffers from, that whole ownership thing. I just use atomic_ptr which I'm biased towards for some reason. It's atomically thread-safe and you can pretty much do anything you need to do with it, even copy it. Also I should note that locks are held by threads, not objects. You don't move them. You're probably talking about moving references to them or handles, but that's not what you're making it sound like. -- Joe Seigh .