/*****************************************************
 * File: Lock.hh
 * Author: Keith Schwarz (htiek@cs.stanford.edu)
 *
 * An interface representing an object that can be locked
 * and unlocked.
 *
 * Any lockable object can be locked in a scope using the
 * synchronized macro.  The syntax is
 *
 * synchronized (lock) {
 *    ...
 * }
 *
 * or
 *
 * synchronized (lock)
 *    .. single statement ...
 *
 */
#ifndef Lock_Included
#define Lock_Included

namespace synch {
  class Lock {
  public:
    /* Polymorphic classes need virtual destructors. */
    virtual ~Lock() {}
    
    /**
     * void lock();
     * ------------------------------------------------
     * Locks this object, blocking until the lock can
     * be acquired.  The specifics of how this operation
     * is implemented depend on the type of lock.
     */
    virtual void lock() = 0;
    
    /**
     * void unlock();
     * ------------------------------------------------
     * Unlocks this object.  The specifics of how this
     * operation is implementation depend on the type
     * of lock.
     */
    virtual void unlock() = 0;
    
  protected:
    /* Constructor available only to subclasses. */
    Lock() {}
    
  private:
    /* No copying or assignment. */
    Lock(const Lock&);
    Lock& operator= (const Lock&);
  };
  
  /* * * * * Implementation Below This Point * * * * */
  
  /* Pack everything in a namespace which is not intended to be used by
   * clients.
   */
  namespace synchronized_impl {
    /* A utility class which uses RAII to acquire and release a lock
     * automatically.  Moreover, it overloads operator bool() so that
     * in a condition statement, it evaluates to false.
     *
     * You should not use this directly in your code, as it's fairly
     * unsafe (i.e. no defined copy behavior).
     */
    class AutoLock {
    public:
      /* Ctor stores the mutex, then acquires a lock on it. */
      AutoLock(Lock& lock) : mLock(lock) {
        mLock.lock();
      }
      
      /* Dtor releases the lock. */
      ~AutoLock() {
        mLock.unlock();
      }
      
      /* Operator bool evaluates to false so we can put this in a conditional
       * statement.
       */
      operator bool() {
        return false;
      }
      
    private:
      Lock& mLock; // The stored mutex
    };
  }
  
  /* The actual definition of the macro. */
#define synchronized(lock)                                         \
  if (::synch::synchronized_impl::AutoLock _tmpImpl = (lock)) {     \
  } else 

}
#endif
