/*
 *  ZThreads, a platform-independant, multithreading and 
 *  synchroniation library
 *
 *  Copyright (C) 2001 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
 */


#include "zthread/RWLock.h"
#include "zthread/Guard.h"

#include <assert.h>

using namespace ZThread;

/**
 * Initialize a new RWLock
 */
RWLock::RWLock() 
  throw(Synchronization_Exception) : _fairnessCounter(0) {
  
  _activeReaders = _activeWriters = 0;
  _waitingReaders = _waitingWriters = 0;

  _readLock = new RLock(this);
  _writeLock = new WLock(this);

  assert(_readLock != 0 && _writeLock != 0);

  if(_readLock == 0 || _writeLock == 0)
    throw Initialization_Exception();
}

/**
 * Destroy a read / write lock
 */
RWLock::~RWLock() throw() {
  
  try {

    delete _writeLock;
    delete _readLock;
  
  } catch(...) {

    ZTDEBUG("There was an unexpected error while destroying this RWLock\n");
    assert(false);

  }

}
    


/**
 * Implements the rule to determine if readers should be allowed
 * or made to wait. The default implementation is to prefer writers
 * to readers. This is because there will generally be more readers
 * than writers for most RWLocks
 *
 * @return bool determine if reading is allowed
 */
bool RWLock::allowReader() {

  return _activeWriters == 0;

}
    
/**
 * Implements the rule to determine if a writer should be allowed
 * or made to wait. The default implementation is to prefer writers
 * to readers.
 *
 * @return bool determine if reading is allowed
 */
bool RWLock::allowWriter() {

  return (_activeWriters == 0 && _activeReaders == 0);

}
    

/**
 * Called before a read operation is attempted.
 * Block until the the read operation is allowed. 
 *
 * While this method blocks, the _waitingReaders 
 * variable is incremented to relfect the waiting thread.
 *
 * After the lock is released and the method is no longer
 * blocking, _waitingReaders is decremented and _aciveReaders
 * is incremented
 *
 * @post Reading should now be allowed
 * @exception Interrupted_Exception thrown is the blocking
 * operation is interrupted prematurely
 */
void RWLock::beforeRead() 
  throw(Synchronization_Exception) {
      
  Guard<FastMutex> guard(_mtxLock); // Serialize access to this method
      
  ++_waitingReaders;  

  while(!allowReader()) {
        
    try {
          
      // wait
      _condRead.wait(_mtxLock);
          
    } catch(...) {
          
    
      // catch any synchronization errors
      // roll back on error, throw interrupted exception
      --_waitingReaders;
          
      throw;
    }

  }
      
  --_waitingReaders;
  ++_activeReaders;

}

bool RWLock::beforeRead(unsigned long milliseconds) 
  throw(Synchronization_Exception) {
      
  Guard<FastMutex> guard(_mtxLock); // Serialize access to this method
  bool result = false;

  ++_waitingReaders;  

  while(!allowReader()) {
        
    try {
          
      // wait
      result = _condRead.wait(_mtxLock, milliseconds);
          
    } catch(...) {
          
    
      // catch any synchronization errors
      // roll back on error, throw interrupted exception
      --_waitingReaders;
          
      throw;
    }

  }
      
  --_waitingReaders;
  ++_activeReaders;

  return result;
}   
    
/**
 * Called after the read operation has been performed
 */ 
void RWLock::postRead()
  throw(Synchronization_Exception) {
      
  Guard<FastMutex> guard(_mtxLock); // Serialize access to this method
      
  --_activeReaders;
  _fairnessCounter--;
  // notify all
  _condWrite.broadcast();
  _condRead.signal();
    
}
    
/**
 * Called before a write operation is attempted
 */
void RWLock::beforeWrite() 
  throw(Synchronization_Exception) {
  
  Guard<FastMutex> guard(_mtxLock); // Serialize access to this method
  
  ++_waitingWriters;

  while(!allowWriter()) {
        
    try {

      _condWrite.wait(_mtxLock);
          
    } catch(...) {

      // catch any synchronization errors
      // roll back on error, throw interrupted exception
      --_waitingWriters;
      throw;
    }
        
  }
      
  --_waitingWriters;
  ++_activeWriters;
      
}

bool RWLock::beforeWrite(unsigned long milliseconds) 
  throw(Synchronization_Exception) {
  
  Guard<FastMutex> guard(_mtxLock); // Serialize access to this method
  bool result = false;

  ++_waitingWriters;

  while(!allowWriter()) {
        
    try {

      result = _condWrite.wait(_mtxLock, milliseconds);
          
    } catch(...) {

      // catch any synchronization errors
      // roll back on error, throw interrupted exception
      --_waitingWriters;
      throw;
    }
        
  }
      
  --_waitingWriters;
  ++_activeWriters;
      
  return result;
}
    
/**
 * Called aftre a write operation is attempted
 */

void RWLock::postWrite()
  throw(Synchronization_Exception) {
  
  Guard<FastMutex> guard(_mtxLock); // Serialize access to this method
  
  --_activeWriters;
  _fairnessCounter++;
  
  // notify all
  _condWrite.broadcast();//signal();
  _condRead.signal();
  
}






