"Darryl Green" <green@tabq.com.au> wrote on gmane.comp.lib.boost.user news:696BA06B3A0FD211BAAD0060B0C4DD8301FD3F14@WNTX...
From: Raoul Gough [mailto:yg-boost-users@m.gmane.org] Sent: Friday, 27 September 2002 9:44 PM [snip] The proxy copy constructor would unlock() the original lock and construct the copy using the original's mutex (which is the part that can't currently work, because there is no public access to a lock's mutex). Assuming that the compiler implements the NRVO, the unlock/lock sequence wouldn't actually happen, and the client code just gets its proxy object constructed for it. If the compiler doesn't implement the NRVO, the code still works, but is less efficient.
This issue has come up before on the development list (search for Boost.Threads Locking Delima). I'm not sure that it was resolved satisfactorily - move semantics for scoped_locks seemed to be the answer, but I think there were problems actually implementing that. The intent was that if move couldn't be implemented, then the mutex lock methods would have to be exposed.
Hi, Darryl. Thanks for the pointer - move semantics is what I was looking for. How are things going at tabq? If anyone is interested, I've come up with an improved proxy implementation that provides move semantics using the existing boost::scoped_lock. It stores its own reference to the original mutex, which is only slightly less efficient than if scoped_lock had move semantics itself. This allows code like the following: void foo (mt::holder<std::deque<int> > &queue) { queue()->push_back (5); // thread-safe } Regards, Raoul Gough. // Proxy template #include <boost/thread/mutex.hpp> namespace mt { template<typename T> class proxy { boost::mutex &mMutex; boost::mutex::scoped_lock mLock; T *mPtr; boost::mutex &release () { mLock.unlock(); return mMutex; } public: proxy (proxy &other) : mMutex (other.release()) , mLock (mMutex) , mPtr (other.mPtr) { } template<typename TOwner> proxy (TOwner &owner) : mMutex (owner.mutex()) , mLock (mMutex) , mPtr (&owner.data()) { } T *get() const { return mPtr; } T &operator* () const { return *mPtr; } T *operator-> () const { return mPtr; } }; } // Holder with thread-safe proxy access: #include "proxy.hh" #include <boost/thread/mutex.hpp> namespace mt { template<typename T> class holder { template<typename X> friend class proxy; public: typedef proxy<T> proxy_type; typedef proxy<T> returnable_proxy_type; holder () : mMutex (), mT () { } explicit holder (T const &t) : mMutex (), mT (t) { } returnable_proxy_type operator() (); // Convenience function. It is more efficient for the caller // to construct a proxy<T> directly from the holder object. private: // Proxy support methods. These allow a proxy<T> to construct // itself from us (accessible via friendship). boost::mutex &mutex () { return mMutex; } T &data () { return mT; } private: boost::mutex mMutex; T mT; }; template<typename T> typename holder<T>::returnable_proxy_type holder<T>::operator() () { returnable_proxy_type temp (*this); return temp; } }