Boost logo

Boost :

From: Bronek Kozicki (brok_at_[hidden])
Date: 2004-07-16 07:31:37


Peter Dimov wrote:
> Eric Niebler wrote:
>>
>> I continue to believe that a better interface for a unified lock
>> involves descriptively named helper functions:
>>
>> scoped_lock l1( m ); // lock unconditionally
>> scoped_lock l1 = defer_lock( m ); // don't lock
>> scoped_lock l2 = try_lock( m ); // try lock
>> scoped_lock l3 = timed_lock( m, t ); // timed lock
>
> Maybe. Do you have a sketch of the specification? (Not the
> implementation.)

I know it's not particulary strong point, but I used approach propsed by
Eric in the past, and it worked pretty well. I used something similar
to:

template <typename T>
inline typename lock::scoped_lock_impl<T> acquire(lock::lock_impl<T>&
lock)
{
  return lock::scoped_lock_impl<T>(lock);
}

template <typename T>
inline typename lock::scoped_lock_impl<T>
try_acquire(lock::lock_impl<T>& lock, unsigned long timeout)
{
  return lock::scoped_lock_impl<T>(lock, timeout);
}

template <typename T>
inline typename lock::scoped_lock_impl<T>
try_acquire(lock::lock_impl<T>& lock)
{
  return lock::scoped_lock_impl<T>(lock,
lock::scoped_lock_impl<T>::dont_wait_t());
}

Where scoped_lock_impl was similar to:

struct scoped_lock_t
{
protected:
  scoped_lock_t();
  ~scoped_lock_t();

  // safe_bool is typedef to "pointer to member of private struct"
  // ...

public:
  virtual bool active() const = 0;

  operator safe_bool() const
  {
    return active();
  }
};

typedef scoped_lock_t& scoped_lock;

template <typename T>
class scoped_lock_impl : public scoped_lock_t
{
  lock_impl<T>* lock_;

   // non-assignable
  scoped_lock_impl<T>& operator= (scoped_lock_impl<T>& other);

public:
  struct dont_wait_t {};

  virtual bool active() const
  {
    return lock_ != NULL;
  }

  explicit scoped_lock_impl(lock_impl<T>& lock) : lock_(&lock)
  {
    lock.acquire();
  }

  scoped_lock_impl(lock_impl<T>& lock, unsigned long timeout) :
lock_(lock.try_acquire(timeout))
  {}

  scoped_lock_impl(lock_impl<T>& lock, const dont_wait_t&) :
lock_(lock.try_acquire())
  {}

  lock_impl<T>* release()
  {
    lock_impl<T>* tmp = lock_;
    lock_ = NULL;
    return tmp;
  }

  // cctor is passing ownership
  scoped_lock_impl(scoped_lock_impl& other) : lock_(other.release())
  {}

  ~scoped_lock_impl()
  {
    if (lock_)
      lock_->release();
  }
};

and lock_impl is:

template <typename T>
class lock_impl
{
protected:
  lock_impl() {}
  ~lock_impl() {}

  void acquire()
  {
     (static_cast<T*> (this))->acquire();
  }

  T* try_acquire()
  {
    return (static_cast<T*> (this))->try_acquire();
  }

  T* try_acquire(unsigned long timeout)
  {
    return (static_cast<T*> (this))->try_acquire(timeout);
  }

  void release()
  {
    (static_cast<T*> (this))->release();
  }
};

and actual syncronization primitives are:

class critical_section : public lock::lock_impl<critical_section>
{
 // ...
};

class mutex : public lock::lock_impl<mutex>
{
 // ...
};

Here is simple usage:

{
  scoped_lock l1 = acquire(mymutex1_);
  // ok, we own mutex now
}
// now we do not own it

{
  scoped_lock l2 = try_acquire(mymutex2_);
  if (l.active())
  {
    // we own mutex
  }
  // we still own mutex here
}

or

if (scoped_lock l3 = try_acquire(mymutex2_, 1000L))
{
  // we own mutex
}

If type of mymutex_ changes from mutex to critical_section, creation of
l3 will no longer compile (crit. section does not support timed lock),
but no other code changes are required. And compiler error will clearly
point to the problem. Creation of l2 will still compile, as try_acquire
with no timeout is using different constructor of scoped_lock_impl.

B.


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