You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
741 lines
25 KiB
741 lines
25 KiB
/*==LICENSE==* |
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools |
|
Copyright (C) 2011 Cyan Worlds, Inc. |
|
|
|
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 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 General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
Additional permissions under GNU GPL version 3 section 7 |
|
|
|
If you modify this Program, or any covered work, by linking or |
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, |
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent |
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK |
|
(or a modified version of those libraries), |
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, |
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG |
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the |
|
licensors of this Program grant you additional |
|
permission to convey the resulting work. Corresponding Source for a |
|
non-source form of such a combination shall include the source code for |
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered |
|
work. |
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
|
or by snail mail at: |
|
Cyan Worlds, Inc. |
|
14617 N Newport Hwy |
|
Mead, WA 99021 |
|
|
|
*==LICENSE==*/ |
|
////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// plResManagerHelper - The wonderful helper class that can receive messages |
|
// for the resManager. |
|
// |
|
//// History ///////////////////////////////////////////////////////////////// |
|
// |
|
// 6.7.2002 mcn - Created |
|
// |
|
////////////////////////////////////////////////////////////////////////////// |
|
|
|
#include "HeadSpin.h" |
|
#include "plResManagerHelper.h" |
|
#include "plResManager.h" |
|
#include "plRegistryNode.h" |
|
#include "plRegistryHelpers.h" |
|
//#include "plRegistry.h" |
|
#include "plResMgrSettings.h" |
|
|
|
#include "pnKeyedObject/plFixedKey.h" |
|
#include "plMessage/plResMgrHelperMsg.h" |
|
#include "plStatusLog/plStatusLog.h" |
|
#include "hsTimer.h" |
|
|
|
#ifdef MCN_RESMGR_DEBUGGING |
|
|
|
static const int kLogSize = 40; |
|
static const float kUpdateDelay = 0.5f; |
|
|
|
#include "plInputCore/plInputInterface.h" |
|
#include "plInputCore/plInputDevice.h" |
|
#include "plInputCore/plInputInterfaceMgr.h" |
|
#include "pnInputCore/plInputMap.h" |
|
#include "plMessage/plInputEventMsg.h" |
|
#include "plMessage/plInputIfaceMgrMsg.h" |
|
#include "pnKeyedObject/plKeyImp.h" |
|
|
|
#endif |
|
|
|
/// Logging #define for easier use |
|
#define kResMgrLog( level ) if( plResMgrSettings::Get().GetLoggingLevel() >= level ) plStatusLog::AddLineS( "resources.log", |
|
|
|
|
|
|
|
//// Constructor/Destructor ////////////////////////////////////////////////// |
|
|
|
plResManagerHelper *plResManagerHelper::fInstance = nil; |
|
|
|
plResManagerHelper::plResManagerHelper( plResManager *resMgr ) |
|
{ |
|
fResManager = resMgr; |
|
fInstance = this; |
|
|
|
fInShutdown = false; |
|
#ifdef MCN_RESMGR_DEBUGGING |
|
fDebugScreen = nil; |
|
fCurrAge = -1; |
|
fCurrAgeExpanded = false; |
|
fRefreshing = false; |
|
fDebugDisplayType = 0; |
|
#endif |
|
} |
|
plResManagerHelper::~plResManagerHelper() |
|
{ |
|
fInstance = nil; |
|
} |
|
|
|
//// Shutdown //////////////////////////////////////////////////////////////// |
|
|
|
void plResManagerHelper::Shutdown( void ) |
|
{ |
|
EnableDebugScreen( false ); |
|
UnRegisterAs( kResManagerHelper_KEY ); |
|
} |
|
|
|
//// Init //////////////////////////////////////////////////////////////////// |
|
|
|
void plResManagerHelper::Init( void ) |
|
{ |
|
RegisterAs( kResManagerHelper_KEY ); |
|
} |
|
|
|
//// MsgReceive ////////////////////////////////////////////////////////////// |
|
|
|
bool plResManagerHelper::MsgReceive( plMessage *msg ) |
|
{ |
|
plResMgrHelperMsg *refferMsg = plResMgrHelperMsg::ConvertNoRef( msg ); |
|
if( refferMsg != nil ) |
|
{ |
|
if( refferMsg->GetCommand() == plResMgrHelperMsg::kKeyRefList ) |
|
{ |
|
// Message to let go of these keys. So unref the key list, destroy it and we're done! |
|
kResMgrLog( 2 ) 0xff80ff80, "Dropping page keys after timed delay" ); |
|
hsStatusMessage( "*** Dropping page keys after timed delay ***" ); |
|
|
|
delete refferMsg->fKeyList; |
|
refferMsg->fKeyList = nil; |
|
} |
|
else if( refferMsg->GetCommand() == plResMgrHelperMsg::kUpdateDebugScreen ) |
|
{ |
|
IUpdateDebugScreen(); |
|
} |
|
else if( refferMsg->GetCommand() == plResMgrHelperMsg::kEnableDebugScreen ) |
|
EnableDebugScreen( true ); |
|
else if( refferMsg->GetCommand() == plResMgrHelperMsg::kDisableDebugScreen ) |
|
EnableDebugScreen( false ); |
|
|
|
return true; |
|
} |
|
|
|
return hsKeyedObject::MsgReceive( msg ); |
|
} |
|
|
|
//// Read/Write ////////////////////////////////////////////////////////////// |
|
|
|
void plResManagerHelper::Read( hsStream *s, hsResMgr *mgr ) |
|
{ |
|
hsAssert( false, "You should never read me in!" ); |
|
} |
|
|
|
void plResManagerHelper::Write( hsStream *s, hsResMgr *mgr ) |
|
{ |
|
hsAssert( false, "You should never write me out!" ); |
|
} |
|
|
|
//// LoadAndHoldPageKeys ///////////////////////////////////////////////////// |
|
// Loads and refs the keys for the given page, then sends the ref list as |
|
// a list to ourself, time delayed 1 second, so that we can unref them one |
|
// second later. |
|
|
|
void plResManagerHelper::LoadAndHoldPageKeys( plRegistryPageNode *page ) |
|
{ |
|
hsAssert( GetKey() != nil, "Can't load and hold keys when we don't have a key for the helper" ); |
|
|
|
// Create our msg |
|
plResMgrHelperMsg *refferMsg = new plResMgrHelperMsg( plResMgrHelperMsg::kKeyRefList ); |
|
refferMsg->fKeyList = new plResPageKeyRefList; |
|
|
|
fResManager->LoadPageKeys(page); |
|
page->IterateKeys( refferMsg->fKeyList ); |
|
|
|
// Load and ref the keys |
|
#ifdef HS_DEBUGGING |
|
char msg[ 256 ]; |
|
sprintf( msg, "*** Temporarily loading keys for room %s>%s based on FindKey() query, will drop in 1 sec ***", page->GetPageInfo().GetAge(), page->GetPageInfo().GetPage() ); |
|
hsStatusMessage( msg ); |
|
#endif |
|
kResMgrLog( 2 ) 0xff80ff80, "Temporarily loading keys for room %s>%s, will drop in 1 sec", page->GetPageInfo().GetAge(), page->GetPageInfo().GetPage() ); |
|
|
|
// Deliver the message to ourselves! |
|
refferMsg->SetTimeStamp( hsTimer::GetSysSeconds() + 1.f ); |
|
refferMsg->Send( GetKey() ); |
|
} |
|
|
|
#ifdef MCN_RESMGR_DEBUGGING |
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
//// plResMgrDebugInterface Definition /////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////// |
|
|
|
class plResMgrDebugInterface : public plInputInterface |
|
{ |
|
protected: |
|
plResManagerHelper * const fParent; |
|
|
|
virtual ControlEventCode *IGetOwnedCodeList( void ) const |
|
{ |
|
static ControlEventCode codes[] = { END_CONTROLS }; |
|
return codes; |
|
} |
|
|
|
public: |
|
|
|
plResMgrDebugInterface( plResManagerHelper * const mgr ) : fParent( mgr ) { SetEnabled( true ); } |
|
|
|
virtual uint32_t GetPriorityLevel( void ) const { return kGUISystemPriority + 10; } |
|
virtual bool InterpretInputEvent( plInputEventMsg *pMsg ) |
|
{ |
|
plKeyEventMsg *pKeyMsg = plKeyEventMsg::ConvertNoRef( pMsg ); |
|
if( pKeyMsg != nil && pKeyMsg->GetKeyDown() ) |
|
{ |
|
if( pKeyMsg->GetKeyCode() == KEY_UP && fParent->fCurrAge >= 0 ) |
|
{ |
|
fParent->fCurrAge--; |
|
fParent->IUpdateDebugScreen( true ); |
|
return true; |
|
} |
|
else if( pKeyMsg->GetKeyCode() == KEY_DOWN ) |
|
{ |
|
fParent->fCurrAge++; |
|
fParent->IUpdateDebugScreen( true ); |
|
return true; |
|
} |
|
else if( pKeyMsg->GetKeyCode() == KEY_ENTER ) |
|
{ |
|
fParent->fCurrAgeExpanded = !fParent->fCurrAgeExpanded; |
|
fParent->IUpdateDebugScreen( true ); |
|
return true; |
|
} |
|
else if( pKeyMsg->GetKeyCode() == KEY_ESCAPE ) |
|
{ |
|
plResMgrHelperMsg *msg = new plResMgrHelperMsg( plResMgrHelperMsg::kDisableDebugScreen ); |
|
msg->Send( fParent->GetKey() ); |
|
return true; |
|
} |
|
else if( pKeyMsg->GetKeyCode() == KEY_RIGHT ) |
|
{ |
|
if( !fParent->fCurrAgeExpanded ) |
|
fParent->fCurrAgeExpanded = true; |
|
else |
|
{ |
|
fParent->fDebugDisplayType++; |
|
if( fParent->fDebugDisplayType == plResManagerHelper::kMaxDisplayType ) |
|
fParent->fDebugDisplayType = 0; |
|
} |
|
fParent->IUpdateDebugScreen( true ); |
|
return true; |
|
} |
|
else if( pKeyMsg->GetKeyCode() == KEY_LEFT ) |
|
{ |
|
fParent->fCurrAgeExpanded = false; |
|
fParent->IUpdateDebugScreen( true ); |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
virtual uint32_t GetCurrentCursorID( void ) const { return 0; } |
|
virtual bool HasInterestingCursorID( void ) const { return false; } |
|
}; |
|
|
|
#endif |
|
|
|
//// EnableDebugScreen /////////////////////////////////////////////////////// |
|
|
|
void plResManagerHelper::EnableDebugScreen( bool enable ) |
|
{ |
|
#ifdef MCN_RESMGR_DEBUGGING |
|
if( enable ) |
|
{ |
|
if( fDebugScreen == nil ) |
|
{ |
|
fDebugScreen = plStatusLogMgr::GetInstance().CreateStatusLog( kLogSize, "ResManager Status", plStatusLog::kFilledBackground | plStatusLog::kDontWriteFile ); |
|
fRefreshing = true; |
|
|
|
plResMgrHelperMsg *msg = new plResMgrHelperMsg( plResMgrHelperMsg::kUpdateDebugScreen ); |
|
// msg->SetTimeStamp( hsTimer::GetSysSeconds() + kUpdateDelay ); |
|
msg->Send( GetKey() ); |
|
|
|
fDebugInput = new plResMgrDebugInterface( this ); |
|
plInputIfaceMgrMsg *imsg = new plInputIfaceMgrMsg( plInputIfaceMgrMsg::kAddInterface ); |
|
imsg->SetIFace( fDebugInput ); |
|
imsg->Send(); |
|
} |
|
} |
|
else |
|
{ |
|
fRefreshing = false; |
|
if( fDebugScreen != nil ) |
|
{ |
|
delete fDebugScreen; |
|
fDebugScreen = nil; |
|
|
|
plInputIfaceMgrMsg *imsg = new plInputIfaceMgrMsg( plInputIfaceMgrMsg::kRemoveInterface ); |
|
imsg->SetIFace( fDebugInput ); |
|
imsg->Send(); |
|
|
|
hsRefCnt_SafeUnRef( fDebugInput ); |
|
fDebugInput = nil; |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
//// IUpdateDebugScreen ///////////////////////////////////////////////////// |
|
|
|
#ifdef MCN_RESMGR_DEBUGGING |
|
class plDebugPrintIterator : public plRegistryPageIterator, plRegistryKeyIterator |
|
{ |
|
public: |
|
plStatusLog *fLog; |
|
uint8_t fStep, fLines; |
|
uint32_t &fLoadedCount, &fHoldingCount, fPageCount, fAgeIndex; |
|
char fCurrAge[ 128 ]; |
|
uint32_t fLoadedKeys, fTotalKeys, fTotalSize, fLoadedSize; |
|
|
|
plResManagerHelper *fParent; |
|
|
|
plDebugPrintIterator( plResManagerHelper *parent, plStatusLog *log, uint32_t &loadedCount, uint32_t &holdingCount ) |
|
: fParent( parent ), fLog( log ), fStep( 0 ), fLines( 0 ), fLoadedCount( loadedCount ), fHoldingCount( holdingCount ) |
|
{ |
|
fLoadedCount = fHoldingCount = 0; |
|
fCurrAge[ 0 ] = 0; |
|
fPageCount = 0; |
|
fAgeIndex = 0; |
|
} |
|
|
|
virtual bool EatPage( plRegistryPageNode *page ) |
|
{ |
|
if( fStep == 0 ) |
|
{ |
|
fLog->AddLineF( 0xff80ff80, "Loaded Pages" ); |
|
fStep = 1; |
|
fLines++; |
|
} |
|
else if( fStep == 1 && page != nil && !page->IsLoaded() ) |
|
{ |
|
fStep = 2; |
|
fLog->AddLineF( 0xff80ff80, "Holding Pages" ); |
|
fLines++; |
|
} |
|
|
|
if( page != nil && page->IsLoaded() ) |
|
fLoadedCount++; |
|
else if( page != nil ) |
|
fHoldingCount++; |
|
|
|
// Changed ages? |
|
if( page == nil || strcmp( fCurrAge, page->GetPageInfo().GetAge() ) != 0 ) |
|
{ |
|
// Print some info for the last age we were on |
|
if( fCurrAge[ 0 ] != 0 ) |
|
{ |
|
if( fParent->fCurrAge != fAgeIndex || !fParent->fCurrAgeExpanded ) |
|
{ |
|
if( fLines < kLogSize - 4 ) |
|
{ |
|
uint32_t color = plStatusLog::kWhite; |
|
if( fParent->fCurrAge == fAgeIndex ) |
|
color = plStatusLog::kYellow; |
|
|
|
fLog->AddLineF( color, " %s (%d pages)", fCurrAge, fPageCount ); |
|
fLines++; |
|
} |
|
else if( fLines == kLogSize - 4 ) |
|
{ |
|
fLog->AddLineF( plStatusLog::kWhite, " ..." ); |
|
fLines++; |
|
} |
|
} |
|
fAgeIndex++; |
|
} |
|
fPageCount = 0; |
|
if( page != nil ) |
|
strncpy( fCurrAge, page->GetPageInfo().GetAge(), sizeof( fCurrAge ) - 1 ); |
|
else |
|
fCurrAge[ 0 ] = 0; |
|
|
|
if( fParent->fCurrAge == fAgeIndex && fParent->fCurrAgeExpanded ) |
|
{ |
|
// Print header now, since we won't be printing a footer |
|
if( fLines < kLogSize - 4 ) |
|
{ |
|
fLog->AddLineF( plStatusLog::kYellow, " %s>", fCurrAge ); |
|
fLines++; |
|
} |
|
else if( fLines == kLogSize - 4 ) |
|
{ |
|
fLog->AddLineF( plStatusLog::kWhite, " ..." ); |
|
fLines++; |
|
} |
|
} |
|
} |
|
|
|
fPageCount++; |
|
|
|
if( fParent->fCurrAge == fAgeIndex && fParent->fCurrAgeExpanded && page != nil ) |
|
{ |
|
// Count keys for this page |
|
fTotalKeys = fLoadedKeys = fTotalSize = fLoadedSize = 0; |
|
page->IterateKeys( this ); |
|
|
|
// Print page for this expanded age view |
|
if( fLines < kLogSize - 4 ) |
|
{ |
|
if( fParent->fDebugDisplayType == plResManagerHelper::kSizes ) |
|
fLog->AddLineF( plStatusLog::kWhite, " %s (%d keys @ %4.1fk, %d loaded @ %4.1fk)", page->GetPageInfo().GetPage(), fTotalKeys, fTotalSize / 1024.f, fLoadedKeys, fLoadedSize / 1024.f ); |
|
else if( fParent->fDebugDisplayType == plResManagerHelper::kPercents ) |
|
fLog->AddLineF( plStatusLog::kWhite, " %s (%d%% loaded of %d keys @ %4.1fk)", page->GetPageInfo().GetPage(), fLoadedSize * 100 / ( fTotalSize > 0 ? fTotalSize : -1 ), fTotalKeys, fTotalSize / 1024.f ); |
|
else //if( fParent->fDebugDisplayType == plResManagerHelper::kBars ) |
|
{ |
|
const int startPos = 20, length = 32; |
|
|
|
char line[ 128 ]; |
|
memset( line, ' ', sizeof( line ) - 1 ); |
|
line[ 127 ] = 0; |
|
if( strlen( page->GetPageInfo().GetPage() ) < startPos - 2 ) |
|
memcpy( line + 2, page->GetPageInfo().GetPage(), strlen( page->GetPageInfo().GetPage() ) ); |
|
else |
|
memcpy( line + 2, page->GetPageInfo().GetPage(), startPos - 2 ); |
|
|
|
line[ startPos ] = '|'; |
|
if( fTotalSize == 0 ) |
|
{ |
|
line[ startPos + 1 ] = '|'; |
|
line[ startPos + 2 ] = 0; |
|
} |
|
else |
|
{ |
|
char temp[ 12 ]; |
|
sprintf( temp, "%d%%", fLoadedSize * 100 / fTotalSize ); |
|
|
|
line[ startPos + length + 1 ] = '|'; |
|
int i, sum = 0; |
|
for( i = startPos + 1; i < startPos + length + 1 && sum < fLoadedSize; i++ ) |
|
{ |
|
line[ i ] = '='; |
|
sum += fTotalSize / length; |
|
} |
|
line[ startPos + length + 2 ] = 0; |
|
|
|
memcpy( line + startPos + 1, temp, strlen( temp ) ); |
|
} |
|
|
|
fLog->AddLine( line, plStatusLog::kWhite ); |
|
} |
|
fLines++; |
|
} |
|
else if( fLines == kLogSize - 4 ) |
|
{ |
|
fLog->AddLineF( plStatusLog::kWhite, " ..." ); |
|
fLines++; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
virtual bool EatKey( const plKey& key ) |
|
{ |
|
if( key->ObjectIsLoaded() ) |
|
{ |
|
fLoadedKeys++; |
|
fLoadedSize += ((plKeyImp *)key)->GetDataLen(); |
|
} |
|
fTotalKeys++; |
|
fTotalSize += ((plKeyImp *)key)->GetDataLen(); |
|
return true; |
|
} |
|
}; |
|
#endif |
|
|
|
void plResManagerHelper::IUpdateDebugScreen( bool force ) |
|
{ |
|
#ifdef MCN_RESMGR_DEBUGGING |
|
|
|
if( !fRefreshing ) |
|
return; |
|
|
|
plRegistry *reg = fResManager->IGetRegistry(); |
|
uint32_t loadedCnt, holdingCnt; |
|
|
|
fDebugScreen->Clear(); |
|
|
|
plDebugPrintIterator iter( this, fDebugScreen, loadedCnt, holdingCnt ); |
|
reg->IterateAllPages( &iter ); |
|
iter.EatPage( nil ); // Force a final update |
|
|
|
fDebugScreen->AddLineF( plStatusLog::kGreen, "%d pages loaded, %d holding", loadedCnt, holdingCnt ); |
|
|
|
if( fCurrAge >= iter.fAgeIndex ) |
|
fCurrAge = -1; |
|
|
|
// Repump our update |
|
if( !force ) |
|
{ |
|
plResMgrHelperMsg *msg = new plResMgrHelperMsg( plResMgrHelperMsg::kUpdateDebugScreen ); |
|
msg->SetTimeStamp( hsTimer::GetSysSeconds() + kUpdateDelay ); |
|
msg->Send( GetKey() ); |
|
} |
|
|
|
#endif |
|
} |
|
|
|
#if 0 |
|
// FIXME |
|
bool VerifyKeyUnloaded(const char* logFile, const plKey& key); |
|
// Verifies that a key which shouldn't be loaded isn't, and if it is tries to figure out why. |
|
void VerifyAgeUnloaded(const char* logFile, const char* age); |
|
|
|
// Helper for VerifyKeyUnloaded |
|
bool IVerifyKeyUnloadedRecur(const char* logFile, const plKey& baseKey, const plKey& upKey, const char* baseAge); |
|
bool ILookForCyclesRecur(const char* logFile, const plKey& key, hsTArray<plKey>& tree, int& cycleStart); |
|
|
|
bool plResManager::ILookForCyclesRecur(const char* logFile, const plKey& key, hsTArray<plKey>& tree, int& cycleStart) |
|
{ |
|
int idx = tree.Find(key); |
|
tree.Append(key); |
|
if (tree.kMissingIndex != idx) |
|
{ |
|
cycleStart = idx; |
|
// Found a cycle. |
|
return true; |
|
} |
|
|
|
// Now recurse up the active reference tree. |
|
for (int i = 0; i < key->GetNumNotifyCreated(); i++) |
|
{ |
|
if (key->GetActiveBits().IsBitSet(i)) |
|
{ |
|
for (int j = 0; j < key->GetNotifyCreated(i)->GetNumReceivers(); j++) |
|
{ |
|
plKey reffer = key->GetNotifyCreated(i)->GetReceiver(j); |
|
|
|
if (ILookForCyclesRecur(logFile, reffer, tree, cycleStart)) |
|
return true; |
|
} |
|
} |
|
} |
|
tree.Pop(); |
|
return false; |
|
} |
|
|
|
bool plResManager::IVerifyKeyUnloadedRecur(const char* logFile, const plKey& baseKey, const plKey& upKey, const char* baseAge) |
|
{ |
|
const plPageInfo& pageInfo = FindPage(upKey->GetUoid().GetLocation())->GetPageInfo(); |
|
const char* upAge = pageInfo.GetAge(); |
|
const char* upPage = pageInfo.GetPage(); |
|
|
|
if( !upKey->GetActiveRefs() ) |
|
{ |
|
// We've hit a key active reffing us that should be inactive. |
|
// If it's object is loaded, then it somehow missed getting unloaded. |
|
// Else it must have missed letting go of us when it got unloaded. |
|
if( upKey->ObjectIsLoaded() ) |
|
{ |
|
plStatusLog::AddLineS(logFile, "\tHeld by %s [%s] page %s which is loaded but nothing is reffing", |
|
upKey->GetName(), |
|
plFactory::GetNameOfClass(upKey->GetUoid().GetClassType()), |
|
upPage); |
|
|
|
return true; |
|
} |
|
else |
|
{ |
|
plStatusLog::AddLineS(logFile, "\tHeld by %s [%s] page %s which isn't even loaded", |
|
upKey->GetName(), |
|
plFactory::GetNameOfClass(upKey->GetUoid().GetClassType()), |
|
upPage); |
|
|
|
return true; |
|
} |
|
} |
|
|
|
// if the age of this key is different from the age on the baseKey, |
|
// we've got a cross age active ref, which is illegal. |
|
if( stricmp(upAge, baseAge) ) |
|
{ |
|
plStatusLog::AddLineS(logFile, "\tHeld by %s [%s] which is in a different age %s-%s", |
|
upKey->GetName(), |
|
plFactory::GetNameOfClass(upKey->GetUoid().GetClassType()), |
|
upAge, |
|
upPage); |
|
|
|
return true; |
|
} |
|
|
|
int numActive = 0; |
|
int i; |
|
for( i = 0; i < upKey->GetNumNotifyCreated(); i++ ) |
|
{ |
|
if( upKey->GetActiveBits().IsBitSet(i) ) |
|
{ |
|
numActive++; |
|
} |
|
} |
|
if( numActive < upKey->GetActiveRefs() ) |
|
{ |
|
// Someone has AddRef'd us |
|
plStatusLog::AddLineS(logFile, "\tHeld by %s [%s] page %s which is loaded due to %d AddRef(s)", |
|
upKey->GetName(), |
|
plFactory::GetNameOfClass(upKey->GetUoid().GetClassType()), |
|
upPage, |
|
upKey->GetActiveRefs()-numActive); |
|
|
|
return true; |
|
} |
|
|
|
// Now recurse up the active reference tree. |
|
for( i = 0; i < upKey->GetNumNotifyCreated(); i++ ) |
|
{ |
|
if( upKey->GetActiveBits().IsBitSet(i) ) |
|
{ |
|
int j; |
|
for( j = 0; j < upKey->GetNotifyCreated(i)->GetNumReceivers(); j++ ) |
|
{ |
|
plKey reffer = upKey->GetNotifyCreated(i)->GetReceiver(j); |
|
|
|
|
|
if( IVerifyKeyUnloadedRecur(logFile, baseKey, reffer, baseAge) ) |
|
{ |
|
return true; |
|
} |
|
} |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
bool plResManager::VerifyKeyUnloaded(const char* logFile, const plKey& key) |
|
{ |
|
if( key->ObjectIsLoaded() ) |
|
{ |
|
const plPageInfo& pageInfo = FindPage(key->GetUoid().GetLocation())->GetPageInfo(); |
|
const char* age = pageInfo.GetAge(); |
|
const char* page = pageInfo.GetPage(); |
|
|
|
plStatusLog::AddLineS(logFile, "=================================="); |
|
plStatusLog::AddLineS(logFile, "Object %s [%s] page %s is loaded", key->GetName(), plFactory::GetNameOfClass(key->GetUoid().GetClassType()), page); |
|
|
|
hsTArray<plKey> tree; |
|
int cycleStart; |
|
bool hasCycle = ILookForCyclesRecur(logFile, key, tree, cycleStart); |
|
if( hasCycle ) |
|
{ |
|
plStatusLog::AddLineS(logFile, "\t%s [%s] held by dependency cycle", key->GetName(), plFactory::GetNameOfClass(key->GetUoid().GetClassType())); |
|
int i; |
|
for( i = cycleStart; i < tree.GetCount(); i++ ) |
|
{ |
|
plStatusLog::AddLineS(logFile, "\t%s [%s]", tree[i]->GetName(), plFactory::GetNameOfClass(tree[i]->GetUoid().GetClassType())); |
|
} |
|
plStatusLog::AddLineS(logFile, "\tEnd Cycle"); |
|
return true; |
|
} |
|
else |
|
{ |
|
return IVerifyKeyUnloadedRecur(logFile, key, key, age); |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
class plValidateKeyIterator : public plRegistryKeyIterator |
|
{ |
|
protected: |
|
plRegistry* fRegistry; |
|
const char* fLogFile; |
|
|
|
public: |
|
plValidateKeyIterator(const char* logFile, plRegistry* reg) |
|
{ |
|
fRegistry = reg; |
|
fLogFile = logFile; |
|
} |
|
virtual bool EatKey(const plKey& key) |
|
{ |
|
fRegistry->VerifyKeyUnloaded(fLogFile, key); |
|
return true; |
|
} |
|
}; |
|
|
|
class plValidatePageIterator : public plRegistryPageIterator |
|
{ |
|
protected: |
|
const char* fAge; |
|
plRegistryKeyIterator* fIter; |
|
|
|
public: |
|
plValidatePageIterator(const char* age, plRegistryKeyIterator* iter) : fAge(age), fIter(iter) {} |
|
|
|
|
|
virtual bool EatPage( plRegistryPageNode *keyNode ) |
|
{ |
|
if( !stricmp(fAge, keyNode->GetPageInfo().GetAge()) ) |
|
return keyNode->IterateKeys( fIter ); |
|
return true; |
|
} |
|
}; |
|
|
|
void plResManager::VerifyAgeUnloaded(const char* logFile, const char* age) |
|
{ |
|
bool autoLog = false; |
|
char buff[256]; |
|
if( !logFile || !*logFile ) |
|
{ |
|
sprintf(buff, "%s.log", age); |
|
logFile = buff; |
|
autoLog = true; |
|
} |
|
|
|
if( !autoLog ) |
|
{ |
|
plStatusLog::AddLineS(logFile, "///////////////////////////////////"); |
|
plStatusLog::AddLineS(logFile, "Begin Verification of age %s", age); |
|
} |
|
|
|
plValidateKeyIterator keyIter(logFile, this); |
|
plValidatePageIterator pageIter(age, &keyIter); |
|
|
|
IterateAllPages(&pageIter); |
|
|
|
if( !autoLog ) |
|
{ |
|
plStatusLog::AddLineS(logFile, "End Verification of age %s", age); |
|
plStatusLog::AddLineS(logFile, "///////////////////////////////////"); |
|
} |
|
} |
|
#endif
|
|
|