// Ryzom - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "stdpch.h"
#include "nel/misc/shared_memory.h"
#include "change_tracker_base.h"
/*
* Create the mutex (either create or use existing)
*/
bool CChangeTrackerBase::createMutex( sint32 mutid, bool createNew )
{
#ifdef USE_FAST_MUTEX
bool res = true;
if ( createNew )
trackerMutex().init();
#else
#ifdef NL_OS_WINDOWS
bool res = _TrackerMutex.createByName( NLMISC::toString( "%dM", mutid ).c_str() );
#else
bool res = _TrackerMutex.createByKey( mutid, createNew );
#endif
#endif
MIRROR_DEBUG( "MIRROR: TRACKER: Mutex %s (id %d) ", createNew?"created":"accessed", mutid );
_MutId = mutid;
return res;
}
/*
* Record a change
*/
void CChangeTrackerBase::recordChange( TDataSetIndex entityIndex )
{
// Note: could be shorter to link backwards but we need to link forward
// to read the changes in order (for partial sending)
#ifdef NL_DEBUG
nlassertex( (entityIndex != INVALID_DATASET_INDEX) && (entityIndex != LAST_CHANGED), ("E%d", entityIndex) );
#endif
// Protect consistency of two parallel calls to recordChange() for the same tracker
// and between recordChange() and popFirstChanged()
trackerMutex().enter();
// Test if the entity is already or not flagged as changed
if ( _Array[entityIndex].NextChanged==INVALID_DATASET_INDEX )
{
//nldebug( "Tracker (smid %u): adding change E%d", smid(), entityIndex );
// Set the entityIndex as changed, and last one
_Array[entityIndex].NextChanged = LAST_CHANGED;
if ( _Header->Last!=INVALID_DATASET_INDEX )
{
// Link the previous last one to the new last one
//nlassertex( _Header->Last != entityIndex, ( "Looping at %u", entityIndex ) );
_Array[_Header->Last].NextChanged = entityIndex;
//nldebug( "Tracker %p: E%d -> %d", _Array, _Header->Last, entityIndex );
}
else
{
// Set the first one if not set
_Header->First = entityIndex;
//nldebug( "Tracker %p: FIRST = %d", _Array, entityIndex );
//nldebug( "_Array = %p, _Array[%d].NextChanged = %d", _Array, entityIndex, _Array[entityIndex].NextChanged );
}
// Set the new last one
_Header->Last = entityIndex;
#ifdef COUNT_MIRROR_PROP_CHANGES
++_Header->NbDistinctChanges;
#endif
}
//else
// nldebug( "Tracker %p: E%d already in list (%d)", _Array, entityIndex, _Array[entityIndex].NextChanged );
#ifdef COUNT_MIRROR_PROP_CHANGES
//++_Header->NbValuesSet; // should be atomic!
#endif
//nldebug( "Tracker (smid %u): E%d already in list (pointing to %d)", smid(), entityIndex, _Array[entityIndex].NextChanged );
trackerMutex().leave();
}
/*
* Remove a change (slow) (assumes isAllocated())
*/
void CChangeTrackerBase::cancelChange( TDataSetIndex entityIndex )
{
#ifdef NL_DEBUG
nlassertex( (entityIndex != INVALID_DATASET_INDEX) && (entityIndex != LAST_CHANGED), ("E%d", entityIndex) );
#endif
trackerMutex().enter();
// Find the change before the specified one, to make the link skip it
TDataSetIndex row = _Header->First;
if ( row != LAST_CHANGED )
{
if ( row == entityIndex )
{
// It was the first one
_Header->First = _Array[entityIndex].NextChanged;
_Array[entityIndex].NextChanged = INVALID_DATASET_INDEX;
if ( _Header->First == LAST_CHANGED ) // and it was the only one
_Header->Last = INVALID_DATASET_INDEX;
nldebug( "Cancelling change of E%u", entityIndex );
}
else
{
// It wasn't the first one
while ( (_Array[row].NextChanged != entityIndex) && (_Array[row].NextChanged != LAST_CHANGED) )
{
row = _Array[row].NextChanged;
}
if ( _Array[row].NextChanged == entityIndex )
{
_Array[row].NextChanged = _Array[entityIndex].NextChanged;
_Array[entityIndex].NextChanged = INVALID_DATASET_INDEX;
if ( _Header->Last == entityIndex ) // it was the last one
_Header->Last = row;
nldebug( "Cancelling change of E%u", entityIndex );
}
}
}
trackerMutex().leave();
}
/*
* Return the number of changes (slow)
*/
sint32 CChangeTrackerBase::nbChanges() const
{
sint32 nb = 0;
TDataSetRow entityIndex (getFirstChanged());
while ( entityIndex.getIndex() != LAST_CHANGED )
{
++nb;
entityIndex = getNextChanged( entityIndex );
}
return nb;
}
/*
* Display debug info (1 line)
*/
void CChangeTrackerBase::displayTrackerInfo( const char *headerStr, const char *footerStrNotAllocd, NLMISC::CLog *log ) const
{
if ( isAllocated() )
log->displayNL( "%sAllocated, %d changes, smid %d mutid %d", headerStr, nbChanges(), smid(), mutid() );
else
log->displayNL( "%sNot allocated%s", headerStr, footerStrNotAllocd );
}