/*!
 * \file    OMS_RWLock.cpp
 * \author  IvanS
 * \brief   Reader-writer lock.
 */
/*

    ========== licence begin  GPL
    Copyright (c) 2002-2005 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end


*/

#include "Oms/OMS_RWLock.hpp"
#include "Oms/OMS_Globals.hpp"
#include "Oms/OMS_DbpError.hpp"
#include "liveCache/LVC_KernelInterface.hpp"
#include "liveCache/LVC_LockRequest.hpp"
#include "Oms/OMS_Defines.h"




/*-----------------------------------------------------*/

OmsRWLock::OmsRWLock(OmsHandle &h, int id)
  : handle(h), 
    // lock-Ids [1,OMS_VDIR_RW_START_CNT+OMS_VDIR_SIZE] are used by the version directory
    lockId(id + OMS_VDIR_RW_START_CNT + OMS_VDIR_SIZE), 
    count(0), 
    isLocked(false), 
    isExclusive(false)
{
  LVC_LockRequest lock(LVC_LockRequest::RWLOCK_CREATE, lockId);
  short err = OMS_Globals::GetKernelInterface()->LockRequest(lock);
  if (err != 0) {
    throw DbpError(err, "Cannot create R/W lock");
  }
}

/*-----------------------------------------------------*/

void OmsRWLock::enter(bool exclusive)
{
  if (isLocked) {
    if (isExclusive == exclusive) {
      ++count;
      return;
    }
    throw DbpError(e_already_in_critical_section, "Cannot change shared/exclusive state");
  }
  LVC_LockRequest lock(exclusive ? LVC_LockRequest::RWLOCK_LOCK_EXCLUSIVE :
    LVC_LockRequest::RWLOCK_LOCK_SHARED, lockId);
  short err = OMS_Globals::GetKernelInterface()->LockRequest(lock);
  if (err != 0) {
    throw DbpError(err, "Cannot lock R/W lock");
  }
  isLocked = true;
  isExclusive = exclusive;
}

/*-----------------------------------------------------*/

bool OmsRWLock::tryEnter(bool exclusive)
{
  if (isLocked) {
    if (isExclusive == exclusive) {
      ++count;
      return true;
    }
    throw DbpError(e_already_in_critical_section, "Cannot change shared/exclusive state");
  }
  LVC_LockRequest lock(exclusive ? LVC_LockRequest::RWLOCK_TRYLOCK_EXCLUSIVE :
    LVC_LockRequest::RWLOCK_TRYLOCK_SHARED, lockId);
  short err = OMS_Globals::GetKernelInterface()->LockRequest(lock);
  if (err == e_request_timeout) {
    return false;
  }
  if (err != 0) {
    throw DbpError(err, "Cannot lock R/W lock");
  }
  isLocked = true;
  isExclusive = exclusive;
  return true;
}

/*-----------------------------------------------------*/

void OmsRWLock::leave()
{
  if (!isLocked) {
    throw DbpError(e_not_in_critical_section, "R/W lock not locked");
  }
  if (count > 0) {
    --count;
    return;
  }
  LVC_LockRequest lock(isExclusive ? LVC_LockRequest::RWLOCK_UNLOCK_EXCLUSIVE :
    LVC_LockRequest::RWLOCK_UNLOCK_SHARED, lockId);
  short err = OMS_Globals::GetKernelInterface()->LockRequest(lock);
  if (err != 0) {
    throw DbpError(err, "Cannot unlock R/W lock");
  }
  isLocked = false;
}

/*-----------------------------------------------------*/
