"Darryl Green" 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 &queue)
{
queue()->push_back (5); // thread-safe
}
Regards,
Raoul Gough.
// Proxy template
#include
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
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;
}
}