///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoLockHandler.cc
// ------------------
// Cego database lock handler implementation
//                                                         
// Design and Implementation by Bjoern Lemke               
//     
// (C)opyright 2000-2025 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: CegoLockHandler
// 
// Description: All semaphore lock operations on database files, pool buffers, pages and records level
//              are managed using the LockHandler class.
//              A number of available semaphores is distributed among all required lock operations.
//              
//             
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

// LFC INCLUDES
#include <lfcbase/Exception.h>
#include <lfcbase/ThreadLock.h>
#include <lfcbase/Tokenizer.h>

// CEGO INCLUDES
#include "CegoLockHandler.h"
#include "CegoDatabaseManager.h"

// POSIX INCLUDES
#include <string.h>
#include <stdlib.h>
		 
static ThreadLock** _lockArray;

bool __lockStatOn = false;

CegoLockHandler::CegoLockHandler(CegoDatabaseManager* pDBMng)
{
    _numRecSema = pDBMng->getNumRecordSema();
    _numRBRecSema = pDBMng->getNumRBRecordSema();
    _numSysRecSema = pDBMng->getNumSysRecordSema();
    _numSysPageSema = pDBMng->getNumSysPageSema();
    _numDataPageSema = pDBMng->getNumDataPageSema();
    _numIdxPageSema = pDBMng->getNumIndexPageSema();
    _numRBPageSema = pDBMng->getNumRBPageSema();
    _numDataFileSema = pDBMng->getNumDataFileSema();
    _numBufferPoolSema = pDBMng->getNumBufferPoolSema();
    _pageLockTimeout = pDBMng->getPageLockTimeout();
    _recLockTimeout = pDBMng->getRecLockTimeout();
    _fileLockTimeout = pDBMng->getFileLockTimeout();
    _poolLockTimeout = pDBMng->getPoolLockTimeout();
    _numLockTries = pDBMng->getNumLockTries();

    _pDBMng = pDBMng;
    _modId = pDBMng->getModId("CegoLockHandler");

    _numSema = _numRecSema
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema
	+ _numDataPageSema
	+ _numIdxPageSema
	+ _numRBPageSema
	+ _numDataFileSema
	+ _numBufferPoolSema;
    
    _lockCount = new unsigned[_numSema];

    _recordLock = (RecordLock*)malloc( LCKMNG_NUMLOCKS * sizeof( struct RecordLock));
    _rbRecordLock = (RecordLock*)malloc( LCKMNG_NUMLOCKS * sizeof( struct RecordLock));
    _sysRecordLock = (RecordLock*)malloc( LCKMNG_NUMLOCKS * sizeof( struct RecordLock));
    _sysPageLock = (PageLock*)malloc( LCKMNG_NUMLOCKS * sizeof( struct PageLock));
    _dataPageLock = (PageLock*)malloc( LCKMNG_NUMLOCKS * sizeof( struct PageLock));
    _idxPageLock = (PageLock*)malloc( LCKMNG_NUMLOCKS * sizeof( struct PageLock));
    _rbPageLock = (PageLock*)malloc( LCKMNG_NUMLOCKS * sizeof( struct PageLock));
    
    unsigned i;
    for (i = 0; i<_numSema; i++)
    {
	_lockCount[i]=0;
    }
    for (i = 0; i<LCKMNG_NUMLOCKS; i++)
    {
	_recordLock[i].semId=0;
	_recordLock[i].lockId=0;
	_rbRecordLock[i].semId=0;
	_rbRecordLock[i].lockId=0;
	_sysRecordLock[i].semId=0;
	_sysRecordLock[i].lockId=0;
	_sysPageLock[i].lockId=0;
	_sysPageLock[i].pageId=0;
	_sysPageLock[i].semId=0;
	_dataPageLock[i].lockId=0;
	_dataPageLock[i].pageId=0;
	_dataPageLock[i].semId=0;
	_idxPageLock[i].lockId=0;
	_idxPageLock[i].pageId=0;
	_idxPageLock[i].semId=0;
	_rbPageLock[i].lockId=0;
	_rbPageLock[i].pageId=0;
	_rbPageLock[i].semId=0;		
    }
    _nextLockId = 1;
}

CegoLockHandler::~CegoLockHandler()
{
    delete _lockCount;
    delete _recordLock;
    delete _rbRecordLock;
    delete _sysRecordLock;
    delete _sysPageLock;
    delete _dataPageLock;
    delete _idxPageLock;
    delete _rbPageLock;
}

void CegoLockHandler::initLocks()
{
    _lockArray = new ThreadLock*[_numSema];

    for (unsigned i = 0; i<_numSema; i++)
    {
	_lockArray[i] = new ThreadLock();
	_lockArray[i]->init(LCKMNG_LOCKWAITDELAY, __lockStatOn);
    }

    unsigned low = 0;
    unsigned high = _numRecSema;
    unsigned semId;

    for (  semId = low ; semId < high ; semId++ )
    {
	_lockArray[semId]->setId( Chain(RECLOCKGROUP) +  Chain("-") + Chain(semId-low));
    }

    low = _numRecSema;
    high = _numRecSema 
	+ _numRBRecSema;

    for (  semId = low ; semId < high ; semId++ )
    {
	_lockArray[semId]->setId( Chain(RBRECLOCKGROUP) + Chain("-") + Chain(semId-low));
    }

    low = _numRecSema
	+ _numRBRecSema;
    high = _numRecSema 
	+ _numRBRecSema
	+ _numSysRecSema;

    for (  semId = low ; semId < high ; semId++ )
    {
	_lockArray[semId]->setId( Chain(SYSRECLOCKGROUP) + Chain("-") + Chain(semId-low));
    }

    low = _numRecSema
	+ _numRBRecSema
	+ _numSysRecSema;

    high = _numRecSema 
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema;

    for (  semId = low ; semId < high ; semId++ )
    {
	_lockArray[semId]->setId( Chain(SYSPAGELOCKGROUP) + Chain("-") + Chain(semId-low));
    }
	
    low = _numRecSema
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema;
    high = _numRecSema
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema;

    for (  semId = low ; semId < high ; semId++ )
    {
	_lockArray[semId]->setId( Chain(DATAPAGELOCKGROUP) + Chain("-") + Chain(semId-low));
    }

    low = _numRecSema 
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema;
    high = _numRecSema
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema 
	+ _numIdxPageSema ;

    for (  semId = low ; semId < high ; semId++ )
    {
	_lockArray[semId]->setId( Chain(IDXPAGELOCKGROUP) + Chain("-") + Chain(semId-low));
    }

    low = _numRecSema 
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema
	+ _numIdxPageSema;
    high = _numRecSema 
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema 
	+ _numIdxPageSema
	+ _numRBPageSema;
    
    for (  semId = low ; semId < high ; semId++ )
    {
	_lockArray[semId]->setId( Chain(RBPAGELOCKGROUP) + Chain("-") + Chain(semId-low));
    }

    low = _numRecSema 
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema 
	+ _numIdxPageSema
	+ _numRBPageSema;
    high = _numRecSema 
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema 
	+ _numIdxPageSema
	+ _numRBPageSema 
	+ _numDataFileSema;

    for (  semId = low ; semId < high ; semId++ )
    {
	_lockArray[semId]->setId( Chain(DATAFILELOCKGROUP) + Chain("-") + Chain(semId-low));
    }
  
    low = _numRecSema 
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema 
	+ _numIdxPageSema
	+ _numRBPageSema 
	+ _numDataFileSema;

    high = _numRecSema 
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema
	+ _numIdxPageSema
	+ _numRBPageSema  
	+ _numDataFileSema 
	+ _numBufferPoolSema;

    for (  semId = low ; semId < high ; semId++ )
    {
	_lockArray[semId]->setId( Chain(POOLLOCKGROUP) + Chain("-") + Chain(semId-low));
    }    
}

void CegoLockHandler::deleteLocks()
{
    if ( _lockArray != 0 )
    {
	for (unsigned i = 0; i<_numSema; i++)
	    delete _lockArray[i];
	delete _lockArray;
	_lockArray = 0;
    }
}

unsigned long long CegoLockHandler::lockRecord(const CegoDataPointer& dp, enum LockMode lockMode)
{
    unsigned timeout = _recLockTimeout;
    unsigned numTries = _numLockTries;

    unsigned semId = ( dp.getPageId() + dp.getOffset()) % _numRecSema;

    unsigned i=0;
    while ( _recordLock[i].lockId != 0 && i < LCKMNG_NUMLOCKS )
    {
	i++;
    }
    if (i == LCKMNG_NUMLOCKS)
    {
	throw Exception(EXLOC, "Number of record locks exceeded");    
    }
    _recordLock[i].lockId=_nextLockId;
    _nextLockId++;

    _recordLock[i].semId=semId;
    if ( _lockCount[semId] == 0 )
    {
	while ( numTries > 0 )
	{
	    try
	    {
		if ( lockMode == READ )
		    _lockArray[semId]->readLock(timeout);
		else
		    _lockArray[semId]->writeLock(timeout);
		numTries=0;
	    }
	    catch ( Exception e )
	    {		
		numTries--;									
		if ( numTries == 0 )
		{
		    Chain lockError;
		    e.pop(lockError);

		    Chain lockMode;
		    if ( lockMode == READ )
			lockMode = Chain("Read lock");
		    else
			lockMode = Chain("Write lock");


		    Chain msg = lockMode + Chain(" for record failed, pageid ") + Chain(dp.getPageId()) + Chain(", offset ") + Chain(dp.getOffset()) + Chain(", reason ") + lockError; 

		    throw Exception(EXLOC, msg, e);
		}
		else
		{
		    _pDBMng->log(_modId, Logger::NOTICE, Chain("Lock record failed, still ") + Chain(numTries) + Chain(" tries"));
		}
	    }
	}
	_lockCount[semId]++;
    }
    else
    {
	_lockCount[semId]++;
    }
        
    return _recordLock[i].lockId;    
}

void CegoLockHandler::unlockRecord(unsigned long long lockId)
{
    if ( lockId == 0 )
	return;
    
    unsigned i=0;
    while (i < LCKMNG_NUMLOCKS && _recordLock[i].lockId != lockId )
    {
	i++;
    }
    if ( _recordLock[i].lockId == lockId )
    {	
	unsigned semId = _recordLock[i].semId;
	
	if ( _lockCount[semId] > 1 )
	{
	    _lockCount[semId]--;
	}
	else
	{
	    _lockArray[semId]->unlock();
	    _lockCount[semId]=0;
	}
	_recordLock[i].lockId = 0;
    }
    else
    {
	Chain msg = Chain("Invalid record lockid ") + Chain(lockId);
	throw Exception(EXLOC, msg);
    }
}

unsigned long long CegoLockHandler::lockRBRecord(const CegoDataPointer& dp, enum LockMode lockMode)
{
    unsigned timeout = _recLockTimeout;
    unsigned numTries = _numLockTries;

    unsigned semId = _numRecSema + ( dp.getPageId() + dp.getOffset()) % _numRBRecSema;

    unsigned i=0;
    while ( _rbRecordLock[i].lockId != 0 && i < LCKMNG_NUMLOCKS)
    {
	i++;
    }
    if (i == LCKMNG_NUMLOCKS)
    {
	throw Exception(EXLOC, "Number of rb record locks exceeded");    
    }
    _rbRecordLock[i].lockId=_nextLockId;
    _nextLockId++;

    _rbRecordLock[i].semId=semId;
    if ( _lockCount[semId] == 0 )
    {
	while ( numTries > 0 )
	{
	    try
	    {		
		if ( lockMode == READ )
		    _lockArray[semId]->readLock(timeout);
		else
		    _lockArray[semId]->writeLock(timeout);
		numTries=0;
	    }
	    catch ( Exception e )
	    {		
		numTries--;									
		if ( numTries == 0 )
		{
		    Chain lockError;
		    e.pop(lockError);

		    Chain lockMode;
		    if ( lockMode == READ )
			lockMode = Chain("Read lock");
		    else
			lockMode = Chain("Write lock");


		    Chain msg = lockMode + Chain(" for rb record failed, pageid ") + Chain(dp.getPageId()) + Chain(", offset ") + Chain(dp.getOffset()) + Chain(", reason ") + lockError; 

		    throw Exception(EXLOC, msg, e);
		}
		else
		{
		    _pDBMng->log(_modId, Logger::NOTICE, Chain("RBlock record failed, still ") + Chain(numTries) + Chain(" tries"));
		}
	    }
	}
	_lockCount[semId]++;
    }
    else
    {
	_lockCount[semId]++;
    }
    return _rbRecordLock[i].lockId;
}

void CegoLockHandler::unlockRBRecord(unsigned long long lockId)
{
    if ( lockId == 0 )
	return;
    
    unsigned i=0;
    while (i < LCKMNG_NUMLOCKS && _rbRecordLock[i].lockId != lockId )
    {
	i++;
    }
    if (_rbRecordLock[i].lockId == lockId)
    {	
	unsigned semId = _rbRecordLock[i].semId;
	
	if ( _lockCount[semId] > 1 )
	{
	    _lockCount[semId]--;
	}
	else
	{
	    _lockArray[semId]->unlock();
	    _lockCount[semId]=0;
	}
	_rbRecordLock[i].lockId = 0;
    }
    else
    {
	Chain msg = Chain("Invalid RB record lockid ") + Chain(lockId);
	throw Exception(EXLOC, msg);
    }
}

unsigned long long CegoLockHandler::lockSysRecord(const CegoDataPointer& dp, enum LockMode lockMode)
{
    unsigned timeout = _recLockTimeout;
    unsigned numTries = _numLockTries;

    unsigned semId = _numRecSema
	+ _numRBRecSema
	+ ( dp.getPageId() + dp.getOffset()) % _numSysRecSema;

    unsigned i=0;
    while ( _sysRecordLock[i].lockId != 0 && i < LCKMNG_NUMLOCKS)
    {
	i++;
    }
    if (i == LCKMNG_NUMLOCKS)
    {
	throw Exception(EXLOC, "Number of sys record locks exceeded");    
    }
    _sysRecordLock[i].lockId=_nextLockId;
    _nextLockId++;

    _sysRecordLock[i].semId=semId;
    if ( _lockCount[semId] == 0 )
    {
	while ( numTries > 0 )
	{
	    try
	    {		
		if ( lockMode == READ )
		    _lockArray[semId]->readLock(timeout);
		else
		    _lockArray[semId]->writeLock(timeout);
		numTries=0;
	    }
	    catch ( Exception e )
	    {		
		numTries--;									
		if ( numTries == 0 )
		{
		    Chain lockError;
		    e.pop(lockError);

		    Chain lockMode;
		    if ( lockMode == READ )
			lockMode = Chain("Read lock");
		    else
			lockMode = Chain("Write lock");

		    Chain msg = lockMode + Chain(" for sys record failed, pageid ") + Chain(dp.getPageId()) + Chain(", offset ") + Chain(dp.getOffset()) + Chain(", reason ") + lockError; 

		    throw Exception(EXLOC, msg, e);
		}
		else
		{
		    _pDBMng->log(_modId, Logger::NOTICE, Chain("Syslock record failed, still ") + Chain(numTries) + Chain(" tries"));
		}
	    }
	}
	_lockCount[semId]++;
    }
    else
    {
	_lockCount[semId]++;
    }
    
    return _sysRecordLock[i].lockId;    
}

void CegoLockHandler::unlockSysRecord(unsigned long long lockId)
{
    if ( lockId == 0 )
	return;
    
    unsigned i=0;
    while (i < LCKMNG_NUMLOCKS && _sysRecordLock[i].lockId != lockId )
    {
	i++;
    }
    if (_sysRecordLock[i].lockId == lockId)
    {	
	unsigned semId = _sysRecordLock[i].semId;
	
	if ( _lockCount[semId] > 1 )
	{
	    _lockCount[semId]--;
	}
	else
	{
	    _lockArray[semId]->unlock();
	    _lockCount[semId]=0;
	}
	_sysRecordLock[i].lockId = 0;
    }
    else
    {
	Chain msg = Chain("Invalid sys record lockid ") + Chain(lockId);
	throw Exception(EXLOC, msg);
    }
}

unsigned long long CegoLockHandler::lockSysPage(PageIdType pageId, enum LockMode lockMode)
{
    unsigned semId = _numRecSema 
	+ _numRBRecSema 
	+ _numSysRecSema 
	+ pageId % _numSysPageSema;

    unsigned timeout = _pageLockTimeout;
    unsigned numTries = _numLockTries;

    unsigned i=0;
    while ( _sysPageLock[i].lockId != 0 && i < LCKMNG_NUMLOCKS)
    {
	i++;
    }
    if (i == LCKMNG_NUMLOCKS)
    {
	throw Exception(EXLOC, "Number of sys page locks exceeded");    
    }
    _sysPageLock[i].lockId = _nextLockId;    
    _nextLockId++;

    if ( _lockCount[semId] == 0 )
    {
	while ( numTries > 0 )
	{
	    try
	    {
		if ( lockMode == READ )
		    _lockArray[semId]->readLock(timeout);
		else 
		    _lockArray[semId]->writeLock(timeout);
		numTries=0;
	    }
	    catch ( Exception e )
	    {		
		numTries--;									
		if ( numTries == 0 )
		{
		    Chain lockError;
		    e.pop(lockError);

		    Chain lockMode;
		    if ( lockMode == READ )
			lockMode = Chain("Read lock");
		    else
			lockMode = Chain("Write lock");

		    Chain msg = lockMode + Chain(" for sys page failed, pageId ") + Chain(pageId) + Chain(", reason ") + lockError;
		    throw Exception(EXLOC, msg, e);
		}
		else
		{
		    _pDBMng->log(_modId, Logger::NOTICE, Chain("Syslock page failed, still ") + Chain(numTries) + Chain(" tries"));
		}
	    }
	}
	
	_lockCount[semId]++;
    }
    else
    {
	_lockCount[semId]++;
    }
    
    _sysPageLock[i].semId=semId;    
    _sysPageLock[i].pageId = pageId;
    
    return _sysPageLock[i].lockId;    
}

void CegoLockHandler::unlockSysPage(unsigned long long lockId)
{
    if ( lockId == 0 )
	return;
    
    unsigned i=0;
    while (i < LCKMNG_NUMLOCKS && _sysPageLock[i].lockId != lockId )
    {
	i++;
    }
    if (_sysPageLock[i].lockId == lockId)
    {
	unsigned semId = _sysPageLock[i].semId;
	
	if ( _lockCount[semId] > 1 )
	{
	    _lockCount[semId]--;
	}
	else
	{
	    _lockArray[semId]->unlock();
	    _lockCount[semId]=0;
	}
	_sysPageLock[i].lockId = 0;
    }
    else
    {
	Chain msg = Chain("Invalid sys page lockid ") + Chain(lockId);
	throw Exception(EXLOC, msg);
    }
}

unsigned long long CegoLockHandler::lockData(CegoObject::ObjectType type, PageIdType pageId, enum LockMode lockMode)
{
    if ( type == CegoObject::AVLTREE || type == CegoObject::UAVLTREE || type == CegoObject::PAVLTREE || type == CegoObject::BTREE || type == CegoObject::UBTREE || type == CegoObject::PBTREE )
    {
	return lockIndexPage(pageId, lockMode);
    }
    else if ( type == CegoObject::RBSEG )
    {
	return lockRBPage(pageId, lockMode);
    }
    return lockDataPage(pageId, lockMode);
}

void CegoLockHandler::unlockData(CegoObject::ObjectType type, unsigned long long lockId)
{
    if ( lockId == 0 )
	return;
    
    if ( type == CegoObject::AVLTREE || type == CegoObject::UAVLTREE || type == CegoObject::PAVLTREE || type == CegoObject::BTREE || type == CegoObject::UBTREE || type == CegoObject::PBTREE )
    {
	return unlockIndexPage(lockId);
    }
    else if ( type == CegoObject::RBSEG )
    {
	return unlockRBPage(lockId);
    }
    return unlockDataPage(lockId);
}

unsigned long long CegoLockHandler::lockDataPage(PageIdType pageId, enum LockMode lockMode)
{
    unsigned semId = _numRecSema 
	+ _numRBRecSema
	+ _numSysRecSema 
	+ _numSysPageSema 
	+ ( pageId % _numDataPageSema );

    unsigned timeout = _pageLockTimeout;
    unsigned numTries = _numLockTries;
    
    unsigned i=0;
    while ( _dataPageLock[i].lockId != 0 && i < LCKMNG_NUMLOCKS)
    {
	i++;
    }    
    if (i == LCKMNG_NUMLOCKS)
    {
	throw Exception(EXLOC, Chain("Number of data page locks exceeded"));    
    }
    _dataPageLock[i].lockId = _nextLockId;    
    _nextLockId++;

    if ( _lockCount[semId] == 0 )
    {
	while ( numTries > 0 )
	{
	    try
	    {
		if ( lockMode == READ )
		    _lockArray[semId]->readLock(timeout);
		else 
		    _lockArray[semId]->writeLock(timeout);				
		numTries=0;
	    }
	    catch ( Exception e )
	    {		
		numTries--;						
		if ( numTries == 0 )
		{
		    Chain lockError;
		    e.pop(lockError);

		    Chain lockMode;
		    if ( lockMode == READ )
			lockMode = Chain("Read lock");
		    else
			lockMode = Chain("Write lock");

		    Chain msg = lockMode + Chain(" for data page failed, pageid ") + Chain(pageId) + Chain(", reason ") + lockError;

		    throw Exception(EXLOC, msg, e);
		}
		else
		{
		    _pDBMng->log(_modId, Logger::NOTICE, Chain("Lock data page failed, still ") + Chain(numTries) + Chain(" tries"));
		}
	    }
	}
	
	_lockCount[semId]++;
    }
    else
    {
	_lockCount[semId]++;
    }

    _dataPageLock[i].semId=semId;
    _dataPageLock[i].pageId = pageId;

    return _dataPageLock[i].lockId;    
}

void CegoLockHandler::unlockDataPage(unsigned long long lockId)
{
    if ( lockId == 0 )
	return;
    
    unsigned i=0;
    while (i < LCKMNG_NUMLOCKS && _dataPageLock[i].lockId != lockId )
    {
	i++;
    }
    if (_dataPageLock[i].lockId == lockId)
    {
	unsigned semId = _dataPageLock[i].semId;
	
	if ( _lockCount[semId] > 1 )
	{
	    _lockCount[semId]--;
	}
	else
	{
	    _lockArray[semId]->unlock();
	    _lockCount[semId]=0;
	}
	_dataPageLock[i].lockId = 0;
    }
    else
    {
	Chain msg = Chain("Invalid data page lockid ") + Chain(lockId);
	throw Exception(EXLOC, msg);
    }
}

unsigned long long CegoLockHandler::lockIndexPage(PageIdType pageId, enum LockMode lockMode)
{
    unsigned semId = _numRecSema
	+ _numRBRecSema 
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema 
	+ ( pageId  % _numIdxPageSema );

    unsigned timeout = _pageLockTimeout;
    unsigned numTries = _numLockTries;

    unsigned i=0;
    while ( _idxPageLock[i].lockId != 0 && i < LCKMNG_NUMLOCKS)
    {
	i++;
    }
    if (i == LCKMNG_NUMLOCKS)
    {
	throw Exception(EXLOC, "Number of index page locks exceeded");    
    }
    _idxPageLock[i].lockId = _nextLockId;    
    _nextLockId++;

    if ( _lockCount[semId] == 0 )
    {
	while ( numTries > 0 )
	{
	    try
	    {
		if ( lockMode == READ )
		    _lockArray[semId]->readLock(timeout);
		else 
		    _lockArray[semId]->writeLock(timeout);
		numTries=0;
	    }
	    catch ( Exception e )
	    {		
		numTries--;									
		if ( numTries == 0 )
		{
		    Chain lockError;
		    e.pop(lockError);

		    Chain lockMode;
		    if ( lockMode == READ )
			lockMode = Chain("Read lock");
		    else
			lockMode = Chain("Write lock");
		    
		    Chain msg = lockMode + Chain(" for index page failed, pageid ") + Chain(pageId) + Chain(", reason ") + lockError;

		    throw Exception(EXLOC, msg, e);
		}
		else
		{
		    _pDBMng->log(_modId, Logger::NOTICE, Chain("Lock index page failed, still ") + Chain(numTries) + Chain(" tries"));
		}
	    }
	}

	_lockCount[semId]++;
    }
    else
    {
	_lockCount[semId]++;
    }

    _idxPageLock[i].semId=semId;    
    _idxPageLock[i].pageId = pageId;

    return _idxPageLock[i].lockId;   
}

void CegoLockHandler::unlockIndexPage(unsigned long long lockId)
{
    if ( lockId == 0 )
	return;
    
    unsigned i=0;
    while (i < LCKMNG_NUMLOCKS && _idxPageLock[i].lockId != lockId )
    {
	i++;
    }
    if (_idxPageLock[i].lockId == lockId)
    {
	unsigned semId = _idxPageLock[i].semId;
	
	if ( _lockCount[semId] > 1 )
	{
	    _lockCount[semId]--;
	}
	else
	{
	    _lockArray[semId]->unlock();
	    _lockCount[semId]=0;
	}
	_idxPageLock[i].lockId = 0;
    }
    else
    {
	Chain msg = Chain("Invalid index page lockid ") + Chain(lockId);
	throw Exception(EXLOC, msg);
    }
}

unsigned long long CegoLockHandler::lockRBPage(PageIdType pageId, enum LockMode lockMode)
{
    unsigned semId = _numRecSema 
	+ _numRBRecSema 
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema 
	+ _numIdxPageSema 
	+ ( pageId % _numRBPageSema );

    unsigned timeout = _pageLockTimeout;
    unsigned numTries = _numLockTries;

    unsigned i=0;
    while ( _rbPageLock[i].lockId != 0 && i < LCKMNG_NUMLOCKS)
    {
	i++;
    }
    if (i == LCKMNG_NUMLOCKS)
    {
	throw Exception(EXLOC, "Number of rb page locks exceeded");    
    }
    _rbPageLock[i].lockId = _nextLockId;    
    _nextLockId++;

    if ( _lockCount[semId] == 0 )
    {
	while ( numTries > 0 )
	{
	    try
	    {
		if ( lockMode == READ )
		    _lockArray[semId]->readLock(timeout);
		else 
		    _lockArray[semId]->writeLock(timeout);
		numTries=0;
	    }
	    catch ( Exception e )
	    {		
		numTries--;
		if ( numTries == 0 )
		{

		    Chain lockError;
		    e.pop(lockError);

		    Chain lockMode;
		    if ( lockMode == READ )
			lockMode = Chain("Read lock");
		    else
			lockMode = Chain("Write lock");
		    
		    Chain msg = lockMode + Chain(" for rb page failed, pageid ") + Chain(pageId) + Chain(", reason ") + lockError;

		    throw Exception(EXLOC, msg, e);
		}
		else
		{
		    _pDBMng->log(_modId, Logger::NOTICE, Chain("Lock rb page failed (SemId=") + Chain(semId) + Chain("), still ") + Chain(numTries) + Chain(" tries"));
		}
	    }
	}

	_lockCount[semId]++;
    }
    else
    {
	_lockCount[semId]++;
    }

    _rbPageLock[i].semId=semId;    
    _rbPageLock[i].pageId = pageId;

    return _rbPageLock[i].lockId;
}

void CegoLockHandler::unlockRBPage(unsigned long long lockId)
{
    if ( lockId == 0 )
	return;
    
    unsigned i=0;
    while (i < LCKMNG_NUMLOCKS && _rbPageLock[i].lockId != lockId )
    {
	i++;
    }
    if (_rbPageLock[i].lockId == lockId)
    {
	unsigned semId = _rbPageLock[i].semId;
	
	if ( _lockCount[semId] > 1 )
	{
	    _lockCount[semId]--;
	}
	else
	{
	    _lockArray[semId]->unlock();
	    _lockCount[semId]=0;
	}
	_rbPageLock[i].lockId = 0;
    }
    else
    {
	Chain msg = Chain("Invalid rb page lockid ") + Chain(lockId);
	throw Exception(EXLOC, msg);
    }
}

void CegoLockHandler::lockDataFile(unsigned fileId, enum LockMode lockMode)
{
    unsigned semId = _numRecSema 
	+ _numRBRecSema 
	+ _numSysRecSema 
	+ _numSysPageSema 
	+ _numDataPageSema 
	+ _numIdxPageSema
	+ _numRBPageSema 
	+ fileId % _numDataFileSema;

    unsigned timeout = _fileLockTimeout;
    unsigned numTries = _numLockTries;

    if ( _lockCount[semId] == 0 )
    {
	while ( numTries > 0 )
	{
	    try
	    {
		if ( lockMode == READ )
		    _lockArray[semId]->readLock(timeout);
		else
		{
		    _lockArray[semId]->writeLock(timeout);
		}
		numTries=0;
	    }
	    catch ( Exception e )
	    {
		numTries--;
		if ( numTries == 0 )
		{
		    Chain lockError;
		    e.pop(lockError);

		    Chain lockMode;
		    if ( lockMode == READ )
			lockMode = Chain("Read lock");
		    else
			lockMode = Chain("Write lock");
		    
		    Chain msg = lockMode + Chain(" for data file failed, fileid ") + Chain(fileId) + Chain(", reason ") + lockError;

		    throw Exception(EXLOC, msg, e);
		}
		else
		{
		    _pDBMng->log(_modId, Logger::NOTICE, Chain("Lock data file failed, still ") + Chain(numTries) + Chain(" tries"));
		}
	    }
	}

	_lockCount[semId]++;
    }
    else
    {
	_lockCount[semId]++;
    }    
}

void CegoLockHandler::unlockDataFile(unsigned fileId)
{
    unsigned semId = _numRecSema
	+ _numRBRecSema 
	+ _numSysRecSema 
	+ _numSysPageSema 
	+ _numDataPageSema
	+ _numIdxPageSema  
	+ _numRBPageSema
	+ fileId % _numDataFileSema;

    if ( _lockCount[semId] == 1 )
    {
	_lockArray[semId]->unlock();
	_lockCount[semId]--;
    }
    else
    {
	_lockCount[semId]--;
    }
}

void CegoLockHandler::lockBufferPool(unsigned hashId, enum LockMode lockMode)
{    
    
    unsigned semId = _numRecSema
	+ _numRBRecSema 
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema 
	+ _numIdxPageSema	
	+ _numRBPageSema  
	+ _numDataFileSema 
	+ hashId % _numBufferPoolSema;

    unsigned timeout = _poolLockTimeout;
    unsigned numTries = _numLockTries;
    
    if ( _lockCount[semId] == 0 )
    {
	while ( numTries > 0 )
	{
	    try
	    {
		if ( lockMode == READ )
		{
		    _lockArray[semId]->readLock(timeout);
		}
		else
		{
		    _lockArray[semId]->writeLock(timeout);
		}
				
		numTries=0;
	    }
	    catch ( Exception e )
	    {		
		numTries--;					
		if ( numTries == 0 )
		{
		    Chain lockError;
		    e.pop(lockError);

		    Chain lockMode;
		    if ( lockMode == READ )
			lockMode = Chain("Read lock");
		    else
			lockMode = Chain("Write lock");

		    throw Exception(EXLOC, lockMode + Chain(" for buffer pool failed, reason ") + lockError);
		}
		else
		{
		    _pDBMng->log(_modId, Logger::NOTICE, Chain("Lock buffer pool failed, still ") + Chain(numTries) + Chain(" tries"));
		}
	    }
	}

	_lockCount[semId]++;
    }
    else
    {
	// _pDBMng->log(_modId, Logger::NOTICE, Chain("Increasing bufferpool lockcount for ") + Chain(semId) + Chain(" from  ") + Chain(_lockCount[semId]));
	_lockCount[semId]++;
    }    
}

void CegoLockHandler::unlockBufferPool(unsigned hashId)
{
    unsigned semId = _numRecSema 
	+ _numRBRecSema 
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema
	+ _numIdxPageSema
	+ _numRBPageSema   
	+ _numDataFileSema 
	+ hashId % _numBufferPoolSema;

    if ( _lockCount[semId] > 1 )
    {
	// _pDBMng->log(_modId, Logger::NOTICE, Chain("Decreasing bufferpool lockcount for ") + Chain(semId) + Chain(" from  ") + Chain(_lockCount[semId]));
	_lockCount[semId]--;
    }
    else 
    {
	_lockArray[semId]->unlock();
	_lockCount[semId] = 0;
    }
}

/*
  the method lockBufferPool tries to lock the whole bufferpool.
  In case of any locking error, we have to save the currect lock position, since later on
  we just have to unlock to this position */
 
void CegoLockHandler::lockBufferPool()
{
    _poolLockRange=0;
    for ( unsigned i=0; i<_numBufferPoolSema; i++ )
    {
	lockBufferPool(_poolLockRange, WRITE);
	_poolLockRange=i+1;
    }
}

void CegoLockHandler::unlockBufferPool()
{
    for ( unsigned i=0; i<_poolLockRange; i++ )
	unlockBufferPool(i);
}

void CegoLockHandler::getLockStat(unsigned semId, Chain& lockName, unsigned& lockCount, unsigned long long &numRdLock, unsigned long long &numWrLock, unsigned long long &sumRdDelay, unsigned long long &sumWrDelay)
{   
    lockName = _lockArray[semId]->getId();
    lockCount = _lockArray[semId]->numLockTry();
    numRdLock = _lockArray[semId]->numReadLock();
    numWrLock = _lockArray[semId]->numWriteLock();
    sumRdDelay = 0;
    sumWrDelay = 0;

    if ( _lockArray[semId]->numReadLock() > 0 )
	sumRdDelay = _lockArray[semId]->sumReadDelay() / LCKMNG_DELRES;   
    if ( _lockArray[semId]->numWriteLock() > 0 )
	sumWrDelay = _lockArray[semId]->sumWriteDelay() / LCKMNG_DELRES;
}

void CegoLockHandler::getAggLockStat(const Chain& lockGroup, unsigned& numLock, unsigned& lockCount, unsigned long long &numRdLock, unsigned long long &numWrLock, unsigned long long &sumRdDelay, unsigned long long &sumWrDelay)
{
    lockCount = 0;
    numRdLock = 0;
    numWrLock = 0;
    sumRdDelay = 0;
    sumWrDelay = 0;
    numLock = 0;

    for ( unsigned semId=0; semId<_numSema; semId++ )
    {	
	Tokenizer tok(_lockArray[semId]->getId(), "-");
	
	Chain checkGroup;
	tok.nextToken(checkGroup);
	
	if ( checkGroup == lockGroup )
	{
	    numLock++;
	    lockCount += _lockArray[semId]->numLockTry();
	    numRdLock += _lockArray[semId]->numReadLock();
	    numWrLock += _lockArray[semId]->numWriteLock();
	    
	    if ( _lockArray[semId]->numReadLock() > 0 )
		sumRdDelay += _lockArray[semId]->sumReadDelay() / LCKMNG_DELRES;   
	    if ( _lockArray[semId]->numWriteLock() > 0 )
		sumWrDelay += _lockArray[semId]->sumWriteDelay() / LCKMNG_DELRES;
	}
    }
}

void CegoLockHandler::verifyLocks(CegoAdminHandler* pAH)
{
    unsigned low = 0;
    unsigned high = _numRecSema;
    verifyLockGroup(pAH, low, high, Chain(RECLOCKGROUP));
    
    low = _numRecSema;
    high = _numRecSema 
	+ _numRBRecSema;

    verifyLockGroup(pAH, low, high, Chain(RBRECLOCKGROUP));


    low = _numRecSema
	+ _numRBRecSema;
    high = _numRecSema 
	+ _numRBRecSema
	+ _numSysRecSema;

    verifyLockGroup(pAH, low, high, Chain(SYSRECLOCKGROUP));


    low = _numRecSema
	+ _numRBRecSema
	+ _numSysRecSema;

    high = _numRecSema 
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema;

    verifyLockGroup(pAH, low, high, Chain(SYSPAGELOCKGROUP));
    
	
    low = _numRecSema
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema;
    high = _numRecSema
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema;

    verifyLockGroup(pAH, low, high, Chain(DATAPAGELOCKGROUP));

    
    low = _numRecSema 
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema;
    high = _numRecSema
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema 
	+ _numIdxPageSema ;

    verifyLockGroup(pAH, low, high, Chain(IDXPAGELOCKGROUP));
    
    low = _numRecSema 
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema
	+ _numIdxPageSema;
    high = _numRecSema 
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema 
	+ _numIdxPageSema
	+ _numRBPageSema;

    verifyLockGroup(pAH, low, high, Chain(RBPAGELOCKGROUP));
    
    low = _numRecSema 
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema 
	+ _numIdxPageSema
	+ _numRBPageSema;
    high = _numRecSema 
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema 
	+ _numIdxPageSema
	+ _numRBPageSema 
	+ _numDataFileSema;

    verifyLockGroup(pAH, low, high, Chain(DATAFILELOCKGROUP));
	
  
    low = _numRecSema 
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema 
	+ _numIdxPageSema
	+ _numRBPageSema 
	+ _numDataFileSema;

    high = _numRecSema 
	+ _numRBRecSema
	+ _numSysRecSema
	+ _numSysPageSema 
	+ _numDataPageSema
	+ _numIdxPageSema
	+ _numRBPageSema  
	+ _numDataFileSema 
	+ _numBufferPoolSema;

    verifyLockGroup(pAH, low, high, Chain(POOLLOCKGROUP));
    
}

void CegoLockHandler::verifyLockGroup(CegoAdminHandler* pAH, unsigned low, unsigned high, const Chain lockGroup)
{
    unsigned timeout = 3000;
    
    for (unsigned i = low; i< high; i++)
    {
	Chain info = lockGroup + Chain(" - Read ") + Chain(i) + Chain(" ...");
	if ( i < high - 1 )
	    info += Chain("\r");
    
	if ( pAH )
	    pAH->sendInfo(info);
	
	_lockArray[i]->readLock(timeout);
	_lockArray[i]->unlock();

    }
	
    if ( pAH )
	pAH->sendInfo(Chain("ok\n"));
    
    for (unsigned i = low; i< high; i++)
    {	

	Chain info = lockGroup + Chain(" - Write ") + Chain(i) + Chain(" ...");
	if ( i < high - 1 )
	    info += Chain("\r");

	if ( pAH )
	    pAH->sendInfo(info);
	
	_lockArray[i]->writeLock(timeout);
	_lockArray[i]->unlock();
    }
    if ( pAH )
	pAH->sendInfo(Chain("ok\n"));
    
}

