Boost logo

Boost :

From: Christopher Currie (codemonkey_at_[hidden])
Date: 2004-07-16 12:41:59


Michael Glassford wrote:
> I believe there was (nearly?) consensus that we want to make locks
> movable at some point. My conclusion was that at that point this syntax
> becomes possible, too. I think Peter said or implied much the same
> thing, maybe before I did.

I'm just going to be the voice of dissent here, again, and argue that
the scoped_locks, at least, should not be moveable, otherwise they're
not very scoped. The scoped lock serves a specific purpose in ensuring
that the mutex doesn't remain locked outside of block scope, just like
scoped_ptr ensures that a heap object has a specific lifetime.

If there's enough hew and cry for a movable lock, then we should create
a new class for it. That said, most of the arguments I could find on the
list archive for it are to support syntax changes in initializing a
scoped lock. I also saw an argument for storing locks in containers,
which I can't quite fathom the need for, but I welcome the use cases.

As a compromise, we could create lock_transfer<> objects that wouldn't
lock a mutex, but would allow initialization syntax in places where the
language makes it difficult (this is just a quick sketch, there may be
errors):

template <typename Mutex> class lock_transfer;
template <typename Mutex> class try_lock_transfer;

template <typename Mutex>
    lock_transfer<Mutex> lock( Mutex & m );
template <typename Mutex>
    try_lock_transfer<Mutex> try_lock( Mutex & m );

template <typename Mutex>
class lock_transfer
{
private:
   friend class scoped_lock<Mutex>;
   friend class scoped_try_lock<Mutex>;
   friend lock_transfer<Mutex> lock<>( Mutex & m );

   Mutex & m_;

   explicit lock_tranfer( Mutex & m ) : m_( m ) { }
};

template <typename Mutex>
lock_transfer<Mutex> lock( Mutex & m )
{
   return lock_transfer<Mutex>( m );
}

template <typename Mutex>
class try_lock_transfer
{
private:
   friend class scoped_try_lock<Mutex>;
   friend try_lock_transfer<Mutex> try_lock<>( Mutex & m );

   Mutex & m_;

   explicit try_lock_tranfer( Mutex & m ) : m_( m ) { }
};

template <typename Mutex>
try_lock_transfer<Mutex> try_lock( Mutex & m )
{
   return try_lock_transfer<Mutex>( m );
}

template <typename Mutex>
class scoped_lock
{
   Mutex & m_;
public:
   explicit scoped_lock( Mutex & m )
    : m_( m ) { lock(); }

   // blocking lock transfer
   explicit scoped_lock( lock_transfer<Mutex> const & t )
    : m_( t.m_ ) { lock(); }

   scoped_lock & operator=( lock_tranfer<Mutex> const & t )
    : m_( t.m_ ) { lock(); return *this; }

   operator /* convertible to bool */() const
   { return locked(); }
};

template <typename Mutex>
class scoped_try_lock
{
   Mutex & m_;
public:
   explicit scoped_try_lock( Mutex & m )
    : m_( m ) { try_lock(); }

   // blocking lock transfer
   explicit scoped_lock( lock_transfer<Mutex> const & t )
    : m_( t.m_ ) { lock(); }

   scoped_lock & operator=( lock_tranfer<Mutex> const & t )
    : m_( t.m_ ) { lock(); return *this; }

   // try lock transfer
   explicit scoped_lock( try_lock_transfer<Mutex> const & t )
    : m_( t.m_ ) { try_lock(); }

   scoped_lock & operator=( try_lock_tranfer<Mutex> const & t )
    : m_( t.m_ ) { try_lock(); return *this; }

   operator /* convertible to bool */() const
   { return locked(); }
};

void foo()
{
   mutex m;
   try_mutex try_m;

   if ( mutex::scoped_lock l = lock( m ) ) // always true
   {
     // do stuff
   }

   if ( mutex::scoped_lock l = try_lock( m ) ) // compile error!
   {
     // never reached
   }

   if ( try_mutex::scoped_try_lock l = lock( m ) ) // always true
   {
     // do stuff
   }

   if ( try_mutex::scoped_try_lock l = try_lock( m ) ) // may be false
   {
     // do stuff
   }
}

Comments and critique welcome,
Christopher

-- 
Christopher Currie <codemonkey_at_[hidden]>

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