Boost logo

Boost :

From: Raoul Gough (RaoulGough_at_[hidden])
Date: 2002-09-30 05:21:39


"Darryl Green" <green_at_[hidden]> wrote on gmane.comp.lib.boost.user
news:696BA06B3A0FD211BAAD0060B0C4DD8301FD3F14_at_WNTX...
> > From: Raoul Gough [mailto:yg-boost-users_at_[hidden]]
> > 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;
  }
}


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk