/*
 *  ZThreads, a platform-independant, multithreading and 
 *  synchroniation library
 *
 *  Copyright (C) 2000-2002, Eric Crahen, See LGPL.TXT for details
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 *  SUNY @ Buffalo, hereby disclaims all copyright interest in the
 *  ZThreads library written by Eric Crahen
 */

#ifndef __ZTRWLOCK_H__
#define __ZTRWLOCK_H__

#include "zthread/ZThread.h"
#include "zthread/Condition.h"
#include "zthread/FastMutex.h"

namespace ZThread {
  
/**
 * @class RWLock
 * 
 * @author Eric Crahen <crahen@cse.buffalo.edu>
 * @date <2002-02-24T09:28:38-0500>
 * @version 1.3.0
 *
 * Read/Write lock provide a set of coordinated Lockable object that
 * allow many Readers while there is no update in progress, but only 1 
 * Writer & 0 Readers during an update
 *
 * Because the general case where an RWLock would be used is one where 
 * you have more readers than writers, this lock will have a bias toward
 * readers
 */
  
class ZTHREAD_API RWLock {

  //! friend
  friend class RLock;
  //! friend
  friend class WLock;

  protected: 

  //! Serialize access to this object
  FastMutex _mtxLock;   
  //! Notified on read ready
  Condition _condRead;  
  //! Notified on write read
  Condition _condWrite; 
  //! RLock
  Lockable *_readLock; 
  //! WLock
  Lockable *_writeLock; 
  //! track fairness
  int _fairnessCounter; 
  //! track the current readers
  int _activeReaders;   
  //! track the current writers
  int _activeWriters;   
  //! track the waiting readers
  int _waitingReaders;  
  //! track the waiting writers
  int _waitingWriters;  
  
  // Logic

  /**
   * Checked before a lock is acquired. Changing this function in 
   * a subclass can create RWLocks with different biases.
   *
   * @return bool - true, allow lock to be acquired, false block
   */
  virtual bool allowReader();

  /**
   * Checked before a lock is acquired. Changing this function in 
   * a subclass can create RWLocks with different biases.
   *
   * @return bool - true, allow lock to be acquired, false block
   */
  virtual bool allowWriter();

  
  /**
   * Called just prior to a read lock acquire()
   */
  virtual void beforeRead() 
    throw(Synchronization_Exception);

  /**
   * Called just prior to a write lock acquire()
   */
  virtual void beforeWrite() 
    throw(Synchronization_Exception);

  /**
   * Called just before a read lock tryAcquire()
   *
   * @param long - timeout the read will be called with
   */
  virtual bool beforeRead(unsigned long) 
    throw(Synchronization_Exception);

  /**
   * Called just before a write lock tryAcquire()
   *
   * @param long - timeout the write will be called with
   */
  virtual bool beforeWrite(unsigned long) 
    throw(Synchronization_Exception);

  
  /**
   * Called after a read lock is released
   */
  virtual void postRead() 
    throw(Synchronization_Exception);

  /**
   * Called after a write lock is released
   */
  virtual void postWrite()
    throw(Synchronization_Exception);
  
  public:

  /**
   * Create a new RWLock
   *
   * @exception Initialization_Exception - thrown if resources could 
   * not be allocated
   */
  RWLock() 
    throw(Synchronization_Exception);
  
  /**
   * Destroy this RWLock
   */
  virtual ~RWLock() throw();
  
  /**
   * Get a reference to the read lock
   *
   * @return Lockable read lock
   */
  inline Lockable* getReadLock() { return _readLock; }

  /**
   * Get a reference to the write lock
   *
   * @return Lockable write lock
   */
  inline Lockable* getWriteLock() { return _writeLock; }

      
}; /* RWLock */

  /**
   * @class WLock
   *
   * Delegate the lockable interface to the associated RWLock. This allows
   * the RWLock to be used with the Guard object
   */
  class WLock : public Lockable {
    
    //! friend
    friend class RWLock;

    //! Lock delegate
    RWLock* _lock;
    
    protected:

    //! Create write lock associated with a certain RWLock
    inline WLock(RWLock* l) 
      throw(Synchronization_Exception) : _lock(l) {};
    
    //! Destroy the write lock
    inline virtual ~WLock() {}
    
    //! Release the write lock
    inline virtual void release()
      throw(Synchronization_Exception) { _lock->postWrite();  }
    
    //! Acquire the write lock
    inline virtual void acquire()
      throw(Synchronization_Exception) { _lock->beforeWrite(); }

    //! Acquire the write lock with a timeout
    inline virtual bool tryAcquire(unsigned long milliseconds) 
      throw(Synchronization_Exception) { 

      return _lock->beforeWrite(milliseconds); 

    }
    
  }; /* WLock */
  
  /**
   * @class RLock
   *
   * Delegate the lockable interface to the associated RWLock. This allows
   * the RWLock to be used with the Guard object
   */
  class RLock : public Lockable {
    
    //! friend
    friend class RWLock;

    //! lock delegate
    RWLock* _lock;
    
    protected:

    //! Create read lock associated with a certain RWLock
    inline RLock(RWLock *l) 
      throw(Synchronization_Exception) : _lock(l) {}

    //! Destroy the read lock
    inline virtual ~RLock() {}

    //! Release the read lock
    inline virtual void release() 
      throw(Synchronization_Exception) { _lock->postRead(); }
    
    //! Acquire the read lock
    inline virtual void acquire() 
      throw(Synchronization_Exception) { _lock->beforeRead(); }
    
    //! Acquire the read lock with a timeout
    inline virtual bool tryAcquire(unsigned long milliseconds) 
      throw(Synchronization_Exception) { 
      
      return _lock->beforeRead(milliseconds); 

    }
    
  }; /* RLock */
  


};

#endif







