// File: lock.hpp


//


// Copyright (c) 2002,2003 


// Kevin Atkinson


//


// Permission to use, copy, modify, distribute and sell this software


// and its documentation for any purpose is hereby granted without


// fee, provided that the above copyright notice appear in all copies


// and that both that copyright notice and this permission notice


// appear in supporting documentation.  Kevin Atkinson makes no


// representations about the suitability of this software for any


// purpose.  It is provided "as is" without express or implied


// warranty.





#ifndef DISTRIBNET_LOCK__HPP


#define DISTRIBNET_LOCK__HPP





namespace distribnet {





  // These lock classes serve three functions:


  //


  // 1) The ability to aquire a lock and release it when the object


  //    goes out of scope effectvly implemented the "Monitor" concept.


  //


  // 2) Avoid the need for recursive locks by careful and efficient


  //    book keeping with the out the use of global thread specific


  //    data.


  //


  // 3) Document and enforce guaranteed lock behavior for functions


  //    which take a lock class as a parameter.


  //





  // The global lock used to initialize the lock classes.


  class Mutex;





  // The scoped lock classes:





  class LockOnly;   // If the lock is not already aquire it will do so


                    // and then release it at the end of the scope.


                    // It will not release it under any other


                    // circumstances.





  class Lock;       // Will aquire the lock if necessary but may also


                    // release it at any time.  Do not call a function


                    // with this type of lock while in an inconsistent


                    // state.





  class UnlockOnly; // If the lock is already aquired it will be


                    // released and then reaquire at the end of the


                    // scope.  If will not aquire a lock under any


                    // other circumstances.





  class Unlock;     // If locked is it will be released it, however


                    // it may reaquire at any time.  Do not call a


                    // function with this type of lock if it is


                    // unexceptable to wait for the lock to be


                    // reaquired.





  // These locks should never be passed by value to functions.


  // Instead use the following typedef to pass by reference:


  typedef const LockOnly   & WillOnlyLock;


  typedef const Lock       & WillLock;


  typedef const UnlockOnly & WillOnlyUnlock;


  typedef const Unlock     & WillUnlock;





  // The following lock "states" are also provided.  When a functions


  // takes one of these lock states as a parameter it may change the


  // lock state at any time but then again it might not.





  class MightLockUnlock; // Might aquire or release the lock or both.


  typedef MightLockUnlock LockState; // alternate name





  class MightLock;       // Might aquire a lock but will not release it


                         // if the lock is already aquired.





  class MightUnlock;     // Might release the lock but will not aquire one


                         // if the lock is already released.





  // Lock states may be either passed by const refrence or by value.





  // The various lock and lock states may be converted between each


  // other as follows.  (All relationships are tranasitive).





  //             <== Lock <=> MightLockUnlock <=> Unlock ==>


  // MightLock <=> LockOnly                     UnlockOnly <=> MightLock





  // Never pass any of the locks or the lock states by non const


  // refrence as that will mess up the automatic conversion between


  // the lock types.





  // All Lock classes and states have a public constructor with the


  // following parms, (Mutex &, bool locked = false).  The first


  // paramter is the global mutex which must be provided.  The second 


  // paramter is the state of the mutex which defaults to unlocked.





  // To lock a region of code use these macros like so:


  //   {LOCK(l); /*...*/}


  // Or without using these macros


  //   {Lock l(l); /*...*/}


  // However do NOT do this


  //   {Lock l0(l); /*...*/}


  // as this is can easily lead to mistakes such as


  //   void f(Unlock l);


  //   {Lock l0(l); ...; f(l /* should of been l0 */);}


  // which is wrong and will cause a variaty of problems from not


  // locking/unlocking a region of code that should be to locking a


  // already locked mutex or unlocking a mutex which the current


  // thread does not own.  The latter two cases will likely cause


  // deadlock unless an error checking mutex is used.


  


#ifndef __GNUC__


#define __attribute__(x)


#endif





#define LOCK(l) const Lock l(l) __attribute__((__unused__))


#define UNLOCK(l) const Unlock l(l) __attribute__((__unused__))


#define LOCK_ONLY(l) const LockOnly l(l) __attribute__((__unused__))


#define UNLOCK_ONLY(l) const UnlockOnly l(l) __attribute__((__unused__))


#define LOCKO LOCK_ONLY


#define UNLOCKO UNLOCK_ONLY





  //////////////////////////////////////////////////////////////////////


  //


  // The implementation


  //





  class Mutex {


    // implementation unimportant


  private:


    Mutex(const Mutex &);


    void operator=(const Mutex &);


  public:


    Mutex() {}


    void lock() {}


    void unlock() {}


  };





  class LockBase;


  class UnlockBase;





  class MightBase {


    friend class LockBase;


    friend class UnlockBase;


  protected:


    Mutex & lock_;


    inline MightBase(const LockBase &);


    inline MightBase(const UnlockBase &);


    MightBase(Mutex & l, bool lkd) : lock_(l), locked(lkd) {}


  public:


    const bool locked;


  };





  class MightLockUnlock : public MightBase {


  public:


    MightLockUnlock(Mutex & l, bool lkd = false) : MightBase(l, lkd) {}


    inline MightLockUnlock(const Lock & l);


    inline MightLockUnlock(const Unlock & l);


  };





  class MightLock : public MightBase {


  public:


    MightLock(Mutex & l, bool lkd = false) : MightBase(l, lkd) {}


    MightLock(const MightLockUnlock & l) : MightBase(l) {}


    inline MightLock(const LockOnly & l);


    inline MightLock(const Unlock & l);


  };





  class MightUnlock : public MightBase {


  public:


    MightUnlock(Mutex & l, bool lkd = false) : MightBase(l, lkd) {}


    MightUnlock(const MightLockUnlock & l) : MightBase(l) {}


    inline MightUnlock(const UnlockOnly &);


    inline MightUnlock(const Lock &);


  };





  class LockBase {


    friend class MightBase;


  private:


    LockBase(const LockBase &);


    void operator= (const LockBase &);


  protected:


    Mutex & lock_;


    const bool this_scope;


    LockBase(Mutex & l, bool lkd = false) 


      : lock_(l), this_scope(!lkd)


      {if (this_scope) lock_.lock();}


    LockBase(const MightBase & l) 


      : lock_(l.lock_), this_scope(!l.locked)


      {if (this_scope) lock_.lock();}


    ~LockBase() {if (this_scope) lock_.unlock();}


  };





  class LockOnly : public LockBase {


  public:


    LockOnly(Mutex & l, bool lkd = false) : LockBase(l, lkd) {}


    LockOnly(const MightLock & l) : LockBase(l) {}


    LockOnly(const MightLockUnlock & l) : LockBase (l) {}


    inline LockOnly(const Unlock &);


  };





  class Lock : public LockOnly


  {


    friend class UnlockOnly;


  public:


    Lock(Mutex & l, bool lkd = false) : LockOnly(l, lkd) {}


    Lock(const MightLockUnlock & l) : LockOnly(l) {}


    Lock(const Unlock & l) : LockOnly(l) {}


  };





  class UnlockBase {


    friend class MightBase;


  private:


    UnlockBase(const UnlockBase &);


    void operator= (const UnlockBase &);


  protected:


    Mutex & lock_;


    const bool this_scope;


    UnlockBase(Mutex & l, bool lkd = false) 


      : lock_(l), this_scope(lkd)


      {if (this_scope) lock_.unlock();}


    UnlockBase(const MightBase & l) 


      : lock_(l.lock_), this_scope(l.locked)


      {if (this_scope) lock_.unlock();}


    ~UnlockBase() {if (this_scope) lock_.lock();}


  };





  class UnlockOnly : public UnlockBase {


  public:


    UnlockOnly(Mutex & l, bool lkd = false) : UnlockBase(l, lkd) {}


    UnlockOnly(const MightUnlock & l) : UnlockBase(l) {}


    UnlockOnly(const MightLockUnlock & l) : UnlockBase (l) {}


    inline UnlockOnly(const Lock &); 


  };





  class Unlock : public UnlockOnly


  {


    friend class LockOnly;


  public:


    Unlock(Mutex & l, bool lkd = false) : UnlockOnly(l, lkd) {}


    Unlock(const MightLockUnlock & l) : UnlockOnly(l) {}


    Unlock(const Lock & l) : UnlockOnly(l) {}


  };





  inline MightBase::MightBase(const LockBase & l)


    : lock_(l.lock_), locked(true) {}


  inline MightBase::MightBase(const UnlockBase & l)


    : lock_(l.lock_), locked(false) {}





  inline MightLockUnlock::MightLockUnlock(const Lock & l) 


    : MightBase(l) {}


  inline MightLockUnlock::MightLockUnlock(const Unlock & l) 


    : MightBase(l) {}





  inline MightLock::MightLock(const LockOnly & l) 


    : MightBase(l) {}


  inline MightLock::MightLock(const Unlock & l) 


    : MightBase(l) {}





  inline MightUnlock::MightUnlock(const UnlockOnly & l) 


    : MightBase(l) {}


  inline MightUnlock::MightUnlock(const Lock & l) 


    : MightBase(l) {}





  inline LockOnly::LockOnly(const Unlock & l)


    : LockBase(l.lock_, false) {}





  inline UnlockOnly::UnlockOnly(const Lock & l)


    : UnlockBase(l.lock_, true) {}





}





#endif



