/*
	$Id: CDBBaseObject.cpp,v 1.1 2003/04/20 23:32:51 dasenbro Exp $

	File:		CDBObject.cpp

	Contains:	C++ implementation of basic persistent database object

	Version:	Apple Mail Server - Mac OS X :  $Revision: 1.1 $

	Written by:	Nick Brosnahan

	Copyright:	© 1996-2001 by Apple Computer, Inc., all rights reserved.

 	NOT_FOR_OPEN_SOURCE <to be reevaluated at a later time>

	Change History:

		$Log: CDBBaseObject.cpp,v $
		Revision 1.1  2003/04/20 23:32:51  dasenbro
		Initial check-in.
		
		Revision 1.18  2002/03/26 22:58:27  dasenbro
		Added a debug only string.
		
		Revision 1.17  2002/03/21 16:41:17  dasenbro
		Updated file version information.
		
		Revision 1.16  2001/06/21 20:50:53  dasenbro
		Updated file header info.
		
		Revision 1.15  2001/06/21 17:01:16  dasenbro
		Added Change History.
		

	Projector History:

	  <ASX6>	  6/7/99	DOR		Change some Assert/If pairs to BaseDBAssert_IF...
	  <ASX5>	  6/7/99	DOR		Use "this" rather than globals, t'is better programming.
	  <ASX4>	  6/4/99	DOR		Add check compiler struct alignment routine.
	 <ASM10>	 1/29/99	DOR		We look the cache to prevent race conditions in writing objects
									to disk in CDBObject::Done.  When trying to grab a semaphore the
									retry loop should be a while rather than an if...{sigh}
		 <9>	  6/4/98	DOR		Add an Assert.
		 <8>	 5/26/98	DOR		Tighten up some code and do some better boundary checking.
		 <7>	03/23/98	DOR		Add support for Get/Release ObjectLock, and remove support for
									Get/Set/Clear OwnerThreadID...
		 <6>	 3/15/98	MED		Allocate the cache object if it doesn't exist.
		 <5>	 3/14/98	DOR		Add New Classes for list mgmt. there are now multiple cache
									lists to relieve search times...and close up some race
									conditions.
		 <4>	 3/12/98	DOR		Minor changes.
		 <2>	 3/11/98	MED		Changed setDirty to take a force write flag and also changed the
									was Done deals with a 1 ref. count.
		 <1>	  3/9/98	DOR		first checked in
		 <8>	  3/9/98	DOR		Add support for OwnerThreadID.
		 <7>	  3/9/98	DOR		Add support for Get/Set/Clear OwnerThreadID as a feature of
									CDBBaseObject.
		 <6>	 2/27/98	DOR		Lock the list while we are writting things to the database.
		 <5>	 2/26/98	DOR		Make sure to release semaphores that we "lock".
		 <4>	 2/26/98	DOR		Clean up some race conditions in the CDBObjectDone area...

	To Do:
*/

#include <time.h>

#include "CBaseDatabase.h"
#include "CDBBaseObject.h"

// ----------------------------------------------------------------------------------------
#pragma mark * Static Methods *
// ----------------------------------------------------------------------------------------

CSmartTimer::CSmartTimer (uInt32 inTime2Wait)
{
	this->ResetTimer(inTime2Wait);
}

CSmartTimer::~CSmartTimer ()
{
	fStartTime = 0xffffffff;
	fTime2Wait = fStartTime;
	fCount = 0;
}

void CSmartTimer::ResetTimer(uInt32 inTime2Wait)
{
//	fStartTime = ::TickCount();
	fStartTime = ::time( NULL );
	fTime2Wait = inTime2Wait;
	fCount = 0;
}

Boolean CSmartTimer::KeepWaiting (void)
{
	if (fCount == 0)
	{
		this->ResetTimer(this->fTime2Wait);
		fCount++;
		return (true);
	}

	if ((fTime2Wait == 0) && (fCount == 1))
	{
		return false;
	}

	if (fCount == 1)
	{
		fCount++;
		return true;
	}

	return ( (fStartTime + fTime2Wait) > (uTime_t)::time( NULL ) );
//	return ( (fStartTime + fTime2Wait) > ::TickCount() );
}

#pragma mark -

CDBBaseObjectCacheList::CDBBaseObjectCacheList()
{
	fListHead		= NULL;
	fListCount		= 0;
	fListMaxCount	= 0;
}

CDBBaseObjectCacheList::~CDBBaseObjectCacheList()
{
	BaseDBAssert( fListHead == NULL );
	BaseDBAssert( fListCount == 0 );
}

CDBBaseObject* CDBBaseObjectCacheList::AddObject		( CDBBaseObject *inObject )
{
	CDBBaseObject	*tempObj		= NULL;
	CDBBaseObject	*aReallyTempObj	= NULL;

	BaseDBAssert_if (this != NULL)
	{
		BaseDBAssert(inObject != NULL);
		BaseDBAssert(inObject->GetObjectRefCount() == 1);	// we shouldn't be adding objects with more than
														// one reference...

		this->DoListLock();

		tempObj = this->FindObject(inObject->GetObjectID());	// make sure some hasn't already added the "same" object..
																// while we were "waiting"...

		if (tempObj == NULL)
		{	// no duplicate found....
			// let's add this object to the list....

			tempObj = this->GetListHead();
			if (tempObj != NULL)
			{
				tempObj->SetPrevPtr(inObject);
			}

			inObject->SetNextPtr(tempObj);
			inObject->SetPrevPtr(NULL);
			this->SetListHead(inObject);
			this->SetListCount(this->GetListCount() + 1);
			if (this->GetListCount() > this->GetListMax())
			{
				this->SetListMax(this->GetListCount());
			}

			tempObj = inObject;
		}
		else if (tempObj != inObject)
		{
			BaseDBAssert_if (tempObj->GetObjectRefCount() != 0)
			{	// ok this is tricky...but with all the waits & signals in this code, there is a high
				// probability that we've yielded at some point during this code path execution...
				// therefore while we've been working out way upto adding "inObject" another thread
				// managed to slip in and add the same object to the DBCacheList, and we just tried to
				// "add" it again...{sigh} this is bad...but we've detected the case.

				// We'll go ahead and "Use" the object already on the list, this will prevent it from being
				// deleted in case the "Signal/Release" below yields, and the thread that owns it calls "Done"...
				// we are relying on the fact that "Use" doesn't yield in the case where refcount > zero...

				aReallyTempObj = tempObj->Use(tempObj->GetOwnerDBPtr());
				BaseDBAssert(aReallyTempObj == tempObj);	// this had better work!!! or else we're screwed!!
			}
		}
		else
		{
			BaseDBAssert(tempObj != inObject);	// force a user break...this is bad, because we just found
												// our selves on the list, but we haven't added ourselves
												// yet...{sigh}...this is a _VERY_ bad situation, and will
												// need to be carefully debugged...we have a _SERIOUS_
												// race condition problem....
		}

		this->DoListRelease();
	}

	return (tempObj);
}

Boolean CDBBaseObjectCacheList::RemoveObject	( CDBBaseObject *inObject )
{
	Boolean		result = false;
	CDBBaseObject	*tempObj = NULL;

	BaseDBAssert_if (this != NULL)
	{
		BaseDBAssert(inObject != NULL);
		this->DoListLock();

		if (inObject->GetObjectRefCount() == 1)
		{
			if (this->GetListHead() == inObject)
			{
				this->SetListHead(inObject->GetNextPtr());
			}

			tempObj = inObject->GetPrevPtr();
			if (tempObj != NULL)
			{
				tempObj->SetNextPtr(inObject->GetNextPtr());
				tempObj = NULL;
			}

			tempObj = inObject->GetNextPtr();
			if (tempObj != NULL)
			{
				tempObj->SetPrevPtr(inObject->GetPrevPtr());
				tempObj = NULL;
			}

			inObject->SetNextPtr(NULL);
			inObject->SetPrevPtr(NULL);
			this->SetListCount(this->GetListCount() - 1);

			result = true;
		}

		this->DoListRelease();
	}

	return (result);
}

CDBBaseObject* CDBBaseObjectCacheList::GetListHead ( void )
{
	BaseDBAssert_if (this != NULL)
	{
		return ((CDBBaseObject *)this->fListHead);
	}

	return (NULL);
}

void	CDBBaseObjectCacheList::SetListHead ( CDBBaseObject* inListHead )
{
	BaseDBAssert_if (this != NULL)
	{
		this->fListHead = inListHead;
	}
}

uInt32	CDBBaseObjectCacheList::GetListCount ( void )
{
	BaseDBAssert_if (this != NULL)
	{
		return (this->fListCount);
	}

	return (0);
}

void	CDBBaseObjectCacheList::SetListCount ( const uInt32 inListCount )
{
	BaseDBAssert_if (this != NULL)
	{
		this->fListCount = inListCount;
	}
}

uInt32	CDBBaseObjectCacheList::GetListMax ( void )
{
	BaseDBAssert_if (this != NULL)
	{
		return (this->fListMaxCount);
	}

	return (0);
}

void	CDBBaseObjectCacheList::SetListMax ( const uInt32 inListMax )
{
	BaseDBAssert_if (this != NULL)
	{
		BaseDBAssert(inListMax > this->GetListMax());
		this->fListMaxCount = inListMax;
	}
}

CDBBaseObject* CDBBaseObjectCacheList::FindObject ( const ObjID inObjID )
{
	CDBBaseObject *aCDBObject = NULL;
	CDBBaseObject *tempObject = NULL;

	BaseDBAssert_if ((this != NULL) && (inObjID != 0))
	{
		this->DoListLock();

		aCDBObject = this->GetListHead();
		while (aCDBObject != NULL)
		{
			if (aCDBObject->GetObjectID() == inObjID)
			{
				BaseDBAssert(aCDBObject->GetObjectRefCount() != 0);
				tempObject = aCDBObject->Use( aCDBObject->GetOwnerDBPtr() );
				BaseDBAssert(tempObject == aCDBObject);	// this had better work!!!
				break;
			}

			aCDBObject = aCDBObject->GetNextPtr();
		}


		this->DoListRelease();
	}

	return aCDBObject;
}

void CDBBaseObjectCacheList::FlushList ( const Boolean inWakeupThreads )
{
	CDBBaseObject  *aCDBObject	= NULL;
	OSErr			result		= kNoErr;

	BaseDBAssert_if (this != NULL)
	{
		Try_
		{
			this->DoListLock();
//x/4c &this->fListHead.fObjHeader.fSignature
			aCDBObject = this->GetListHead();
			while ( aCDBObject != NULL )
			{
				if ( aCDBObject->GetDirtyFlag() == true )
				{
					aCDBObject->SetDirtyFlag( false );
					result = aCDBObject->WriteObject();
				}

				if ( inWakeupThreads ==  true )
				{
					aCDBObject->WakeUpObjectThreads();
				}
				aCDBObject = aCDBObject->GetNextPtr();
			}
			this->DoListRelease();
		}

		Catch_( err )
		{
			this->DoListRelease();
		}
	}
}


void CDBBaseObjectCacheList::DoListLock ( void )
{
}


void CDBBaseObjectCacheList::DoListRelease ( void )
{
}


#pragma mark -

CDBBaseObjectCache::CDBBaseObjectCache( CBaseDatabase *inBaseDBPtr )
{
	BaseDBAssert_if (this != NULL)
	{
		fCacheList = NULL;
		fCacheListCount = 0;
		for (uInt32 i = 0; i < kCacheHashTableSize; ++i)
		{
			fHashTable[i] = NULL;
		}
		SetBaseDBPtr(inBaseDBPtr);
	}
}

CDBBaseObjectCache::~CDBBaseObjectCache()
{
	BaseDBAssert_if (this != NULL)
	{
		TypeToIndexMapping* mappingPtr = NULL;
		TypeToIndexMapping* nextPtr = NULL;

		if (fCacheList != NULL)
		{
			delete [] fCacheList;
		}
		for (uInt32 i = 0; i < kCacheHashTableSize; ++i)
		{
			mappingPtr = fHashTable[i];
			while (mappingPtr != NULL)
			{
				nextPtr = mappingPtr->fNextPtr;
				delete mappingPtr;
				mappingPtr = nextPtr;
			}
		}
	}
}

void CDBBaseObjectCache::SetBaseDBPtr( CBaseDatabase *inBaseDBPtr )
{
	BaseDBAssert_if (this != NULL)
	{
		this->fBaseDBPtr = inBaseDBPtr;
		this->SetUpHashTable();
	}
}

void CDBBaseObjectCache::SetUpHashTable	( )
{
	BaseDBAssert_if (this != NULL)
	{
		// tear down existing hash table

		TypeToIndexMapping* mappingPtr = NULL;
		TypeToIndexMapping* nextPtr = NULL;

		if (fCacheList != NULL)
		{
			delete [] fCacheList;
		}
		for (uInt32 i = 0; i < kCacheHashTableSize; ++i)
		{
			mappingPtr = fHashTable[i];
			while (mappingPtr != NULL)
			{
				nextPtr = mappingPtr->fNextPtr;
				delete mappingPtr;
				mappingPtr = nextPtr;
			}
			fHashTable[i] = NULL;
		}

		// set up new hash table
		if ( fBaseDBPtr != NULL )
		{
			// We need to interrogate the database in order to set up a hash table to
			// map object types to cache lists. There should be one cache list per
			// object type so that we don't get weird deadlock situations from collisions.
			SObjTypeInfo objTypeInfo;
			uInt32 objTypeCount = 0;
			uInt32 hashIndex = 0;
			uInt32 i = 0;

			objTypeCount = fBaseDBPtr->GetObjTypeCount();
			fCacheList = new CDBBaseObjectCacheList[objTypeCount];
			fCacheListCount = objTypeCount;
			for (i = 0; i < objTypeCount; ++i)
			{
				TypeToIndexMapping* typeMapping = new TypeToIndexMapping;
				BaseDBAssert_if (typeMapping != NULL )
				{
					fBaseDBPtr->GetObjTypeInfo(i, objTypeInfo);
					hashIndex = objTypeInfo.fObjType % kCacheHashTableSize;

					typeMapping->fObjType = objTypeInfo.fObjType;
					typeMapping->fCacheListIndex = i;
					typeMapping->fNextPtr = fHashTable[hashIndex];

					fHashTable[hashIndex] = typeMapping;
				}
			}
		}
	}
}

CBaseDatabase	*CDBBaseObjectCache::GetBaseDBPtr()
{
	BaseDBAssert_if (this != NULL)
	{
		return (this->fBaseDBPtr);
	}

	return (NULL);
}

CDBBaseObject* CDBBaseObjectCache::AddObject		( CDBBaseObject *inObject )
{
	CDBBaseObject* 	aTempObj = NULL;
	uInt32		aListIndex = 0;

	BaseDBAssert_if (this != NULL)
	{
		aListIndex = this->DoListHash(inObject->GetObjectType(), inObject->GetObjectID());

		this->Wait(aListIndex);

		aTempObj = fCacheList[aListIndex].AddObject(inObject);

		this->Signal(aListIndex);
	}

	return (aTempObj);
}

Boolean CDBBaseObjectCache::RemoveObject ( CDBBaseObject *inObject )
{
	uInt32	aListIndex = 0;
	Boolean	result = false;
	BaseDBAssert_if (this != NULL)
	{
		aListIndex = this->DoListHash( inObject->GetObjectType(), inObject->GetObjectID() );
		this->Wait( aListIndex );
		result = fCacheList[aListIndex].RemoveObject(inObject);
		this->Signal(aListIndex);
	}

	return (result);
}

CDBBaseObject*	CDBBaseObjectCache::FindObject ( const ObjID inObjectID )
{
	CDBBaseObject	*anObject	= NULL;
	uInt32		aListIndex	= 0;

	BaseDBAssert_if (this != NULL)
	{
		for (aListIndex = 0; aListIndex < this->GetListCount(); aListIndex++)
		{
			this->Wait(aListIndex);

			anObject = fCacheList[aListIndex].FindObject(inObjectID);
			if (anObject != NULL)
			{
				break;
			}

			this->Signal(aListIndex);
		}
	}

	return (anObject);
}


CDBBaseObject*	CDBBaseObjectCache::FindObject ( const OSType inObjectType, const ObjID inObjectID )
{
	CDBBaseObject	*anObject	= NULL;
	uInt32		aListIndex	= 0;

	BaseDBAssert_if (this != NULL)
	{
		aListIndex = this->DoListHash( inObjectType, inObjectID );
		this->Wait( aListIndex );
		anObject = fCacheList[ aListIndex ].FindObject( inObjectID );
		this->Signal( aListIndex );
	}

	return (anObject);
}

uInt32	CDBBaseObjectCache::GetListCount		( void )
{
	BaseDBAssert_if (this != NULL)
	{
		return fCacheListCount;
	}

	return (0);
}

void CDBBaseObjectCache::FlushAllList ( const Boolean inWakeupThreads )
{
	BaseDBAssert_if (this != NULL)
	{
		this->Flush( kAllListFlushValue, inWakeupThreads );
	}
}


void CDBBaseObjectCache::FlushList ( const OSType inObjectType, const Boolean inWakeupThreads )
{
	BaseDBAssert_if ( this != NULL )
	{
		this->Flush( inObjectType, inWakeupThreads );
	}
}


void CDBBaseObjectCache::Flush ( const OSType inObjectType, const Boolean inWakeupThreads )
{
	uInt32		aListIndex	= 0;

	BaseDBAssert_if( this != NULL )
	{
		for ( aListIndex = 0; aListIndex < this->GetListCount(); aListIndex++ )
		{
			this->Wait( aListIndex );

			if ( (inObjectType == kAllListFlushValue) || (aListIndex == (this->DoListHash( inObjectType, 0 ))) )
			{
				fCacheList[ aListIndex ].FlushList( inWakeupThreads );
			}

			this->Signal( aListIndex );
		}
	}
}

CDBBaseObject* CDBBaseObjectCache::AddObject2List ( CDBBaseObject *inObject )
{
	CDBBaseObject* aTempObj = NULL;

	if (this != NULL)
	{
		 aTempObj = this->AddObject(inObject);
	}

	return aTempObj;
}

Boolean	CDBBaseObjectCache::RemoveObjFromList	( CDBBaseObject *inObject )
{
	if ( this != NULL )
	{
		return (this->RemoveObject(inObject));
	}

	return (false);
}

CDBBaseObject*	CDBBaseObjectCache::Search4Object	( const ObjID inObjectID )
{
	CDBBaseObject*	aCDBObject = NULL;

	if (this != NULL)
	{
		aCDBObject = this->FindObject(inObjectID);
	}

	return (aCDBObject);
}


CDBBaseObject*	CDBBaseObjectCache::Search4Object ( const OSType inObjectType, const ObjID inObjectID )
{
	CDBBaseObject*	aCDBObject = NULL;

	if (this != NULL)
	{
		aCDBObject = this->FindObject( inObjectType, inObjectID );
	}

	return (aCDBObject);
}


void CDBBaseObjectCache::LockTheCache ( const OSType inObjectType )
{
	uInt32	aListIndex = 0;

	if ( this != NULL )
	{
		aListIndex = this->DoListHash( inObjectType, 0 );
		this->Wait(aListIndex);
	}
}

void CDBBaseObjectCache::UnLockTheCache ( const OSType inObjectType )
{
	uInt32	aListIndex = 0;

	if (this != NULL)
	{
		aListIndex = this->DoListHash(inObjectType, 0);
		this->Signal(aListIndex);
	}
}

void CDBBaseObjectCache::Wait ( const uInt32 inIndex )
{
#ifndef DEBUG
	#pragma unused (inIndex)
#endif

	BaseDBAssert(this != NULL);
	BaseDBAssert(inIndex < this->GetListCount());
	BaseDBAssert(false);
}

void CDBBaseObjectCache::Signal ( const uInt32 inIndex )
{
#ifndef DEBUG
	#pragma unused (inIndex)
#endif

	BaseDBAssert(this != NULL);
	BaseDBAssert(inIndex < this->GetListCount());
	BaseDBAssert(false);
}

uInt32 CDBBaseObjectCache::DoListHash ( const OSType inObjectType, const ObjID )
{
	uInt32	anIndex = 0;

	BaseDBAssert_if ( this != NULL )
	{
		TypeToIndexMapping* mappingPtr = fHashTable[ inObjectType % kCacheHashTableSize ];
		while ( mappingPtr != NULL )
		{
			if ( mappingPtr->fObjType == inObjectType )
			{
				// found it
				anIndex = mappingPtr->fCacheListIndex;
				break;
			}
			else
			{
				mappingPtr = mappingPtr->fNextPtr;
			}
		}
	}

	BaseDBAssert( anIndex < this->GetListCount() );

	return( anIndex );
}

uInt32 CDBBaseObjectCache::CalcObjectCount ( void )
{
	BaseDBAssert_if (this != NULL)
	{
		return (this->CalcCacheObjCount());
	}

	return (0);
}

uInt32 CDBBaseObjectCache::CalcCacheObjCount ( void )
{
	uInt32	aListIndex	= 0;
	uInt32	aCount		= 0;

	if (this != NULL)
	{
		for (aListIndex = 0; aListIndex < this->GetListCount(); aListIndex++)
		{
			aCount += fCacheList[aListIndex].GetListCount();
		}
	}

	return (aCount);
}

#pragma mark -

CDBBaseObject::CDBBaseObject (	StdDBObjHeader	*inObjHeader,
				 	  			StdDBObjFooter	*inObjFooter,
				 	   			void			*inObjData,
				 	   			const OSType	inObjSignature,
				 	   			const uInt32	inObjVersion,
				 	   			const uInt32	inObjSize 		)
{
	fRefCount		= 0;
	fNextPtr		= NULL;
	fPrevPtr		= NULL;
	fDirty			= false;

	BaseDBAssert(inObjHeader != NULL);
	BaseDBAssert(inObjFooter != NULL);
	BaseDBAssert(inObjData != NULL);

        BaseDBAssert(inObjSize <= kMaxIDTableSize);
        
	fObjHeader = inObjHeader;
	fObjFooter = inObjFooter;
	fObjData   = inObjData;

	if (fObjHeader != NULL)
	{
		fObjHeader->fSignature	= inObjSignature;
		fObjHeader->fVersion	= inObjVersion;
		fObjHeader->fSize		= inObjSize;
	}

	if (fObjFooter != NULL)
	{
		fObjFooter->fSignature	= inObjSignature;
	}
}

CDBBaseObject::~CDBBaseObject ( void )
{
	BaseDBAssert(fRefCount 		== 0);
	BaseDBAssert(fNextPtr 		== NULL);
	BaseDBAssert(fPrevPtr 		== NULL);
	BaseDBAssert(fDirty 		== false);
	BaseDBAssert(fObjHeader		!= NULL);
	BaseDBAssert(fObjFooter		!= NULL);
	BaseDBAssert(fObjData		!= NULL);
}

void CDBBaseObject::ChkCompilerStructAlignment ( void )
{
}

CDBBaseObject* CDBBaseObject::UniversalFindByID	(	CBaseDatabase		*inBaseDB,
													const ObjID			inObjID,
													const OSType		inObjType,
													const uInt32		inObjVersion,
													const uInt32		inObjSize,
													ObjAllocFuncPtr		inObjAllocFuncPtr	)
{
	OSStatus	result;
	CDBBaseObject	*theObject	= NULL;
	CDBBaseObject	*tempObject	= NULL;

	if (inObjID == 0)
	{
		return (NULL);
	}

	Try_
	{
		(inBaseDB->GetBaseObjectCachePtr())->LockTheCache(inObjType);

		theObject = (inBaseDB->GetBaseObjectCachePtr())->Search4Object(inObjType, inObjID); 	// danger, danger, potential yield here!!
		if (theObject != NULL)
		{
			// We may find an object in cache that matches the inAccountID. if this is the
			//	case, we just return null.  Therefor, there is no need for this assert...
			// BaseDBAssert(theAccount->fAccountData.fDBHeader.fSignature == kAccountSignature);

			if ((theObject->GetObjectType() != inObjType) ||
				(theObject->GetObjectSize() != inObjSize) ||
				(theObject->GetObjectVersion() != inObjVersion) )
			{
				theObject->Done( theObject );
				theObject = NULL;
			}
		}
		else if (theObject == NULL)
		{
			theObject = inObjAllocFuncPtr();
			BaseDBAssert(theObject != NULL);
			ThrowIfNULL_(theObject);

			result = inBaseDB->GetObjData( inObjType, inObjID, theObject->GetObjectData() );	// danger, danger, potential yield here!!
			if (result != kNoErr)
			{
				delete theObject;
				theObject = NULL;
			}
			else
			{
				BaseDBAssert(theObject->GetObjectID()		== inObjID);
				BaseDBAssert(theObject->GetObjectVersion()	== inObjVersion);
				BaseDBAssert(theObject->GetObjectType()		== inObjType);
				BaseDBAssert(theObject->GetObjectSize()		== inObjSize);

				if ( (theObject->GetObjectType() 	!= inObjType) ||
					 (theObject->GetObjectVersion() != inObjVersion) ||
					 (theObject->GetObjectSize()	!= inObjSize))
				{
					delete theObject;
					theObject = NULL;
				}

				if ( theObject != NULL )
				{
					tempObject = theObject->Use(inBaseDB);	// danger, danger, potential yield here!!
					if (tempObject != theObject)
					{	// hmm the object got added to the list while we were futzing around in the
						// Database...one of those "danger" "danger" points caused us grief...
						// oh well we detected the case and now can fix it...
						// Use has incremented the use count of the object it "returned"
						// therefore all we need to do now is Dispose of the new copy we created..
						// and return the object that "use" found...

						delete theObject;
						theObject = NULL;

						// hold this "newly" found object to the "same" standards as the other object...
						BaseDBAssert(tempObject->GetObjectID()		== inObjID);
						BaseDBAssert(tempObject->GetObjectVersion()	== inObjVersion);
						BaseDBAssert(tempObject->GetObjectType()		== inObjType);
						BaseDBAssert(tempObject->GetObjectSize()		== inObjSize);
						if ( (tempObject->GetObjectType() 	!= inObjType) ||
							 (tempObject->GetObjectVersion() != inObjVersion) ||
					 		 (tempObject->GetObjectSize()	!= inObjSize))
					 	{
					 		// we're _VERY_ confused now, safest thing is to just return nothing....
					 		tempObject->Done(tempObject);
					 	}
					 	else
					 	{
							theObject = tempObject;
							tempObject = NULL;
					 	}
					}
				}
			}
		}

		(inBaseDB->GetBaseObjectCachePtr())->UnLockTheCache(inObjType);
	}
	Catch_ ( err )
	{
		(inBaseDB->GetBaseObjectCachePtr())->UnLockTheCache(inObjType);

		if ( theObject != NULL )
		{
			delete theObject;
			theObject = NULL;
		}
	}

	return( theObject );
}

CBaseDatabase *CDBBaseObject::GetOwnerDBPtr ( void )
{
	BaseDBAssert(this != NULL);
	BaseDBAssert(fObjHeader != NULL);
	BaseDBAssert(fObjFooter != NULL);
	BaseDBAssert(fObjHeader->fSignature == fObjFooter->fSignature);
	BaseDBAssert(this->fOwnerDBPtr != NULL);

	return ((CBaseDatabase *)this->fOwnerDBPtr);
}

OSType	CDBBaseObject::GetObjectType ( void )
{
	BaseDBAssert(fObjHeader != NULL);
	BaseDBAssert(fObjFooter != NULL);
	BaseDBAssert(fObjHeader->fSignature == fObjFooter->fSignature);

	return (fObjHeader->fSignature);
}

uInt32	CDBBaseObject::GetObjectVersion ( void )
{
	BaseDBAssert(fObjHeader != NULL);
	BaseDBAssert(fObjFooter != NULL);
	BaseDBAssert(fObjHeader->fSignature == fObjFooter->fSignature);

	return (fObjHeader->fVersion);
}

uInt32	CDBBaseObject::GetObjectSize ( void )
{
	BaseDBAssert(fObjHeader != NULL);
	BaseDBAssert(fObjFooter != NULL);
	BaseDBAssert(fObjHeader->fSignature == fObjFooter->fSignature);

	return (fObjHeader->fSize);
}

void *	CDBBaseObject::GetObjectData ( void )
{
	BaseDBAssert(fObjHeader != NULL);
	BaseDBAssert(fObjFooter != NULL);
	BaseDBAssert(fObjData != NULL);
	BaseDBAssert(fObjHeader->fSignature == fObjFooter->fSignature);

	return (fObjData);
}

void	CDBBaseObject::SetNextPtr	( CDBBaseObject* inNextPtr )
{
	BaseDBAssert_if (this != NULL)
	{
		this->fNextPtr = inNextPtr;
	}
}

CDBBaseObject *	CDBBaseObject::GetNextPtr	(void)
{
	BaseDBAssert_if (this != NULL)
	{
		return ((CDBBaseObject *)this->fNextPtr);
	}

	return NULL;
}

void	CDBBaseObject::SetPrevPtr	( CDBBaseObject* inPrevPtr )
{
	BaseDBAssert_if (this != NULL)
	{
		this->fPrevPtr = inPrevPtr;
	}
}

CDBBaseObject *	CDBBaseObject::GetPrevPtr	(void)
{
	BaseDBAssert_if (this != NULL)
	{
		return ((CDBBaseObject *)this->fPrevPtr);
	}

	return NULL;
}

uInt32	CDBBaseObject::GetObjectRefCount	( void )
{
	BaseDBAssert_if (this != NULL)
	{
		return (this->fRefCount);
	}

	return 0;
}

void	CDBBaseObject::SetObjectRefCount	( const uInt32 inObjectRefCount )
{
	BaseDBAssert_if (this != NULL)
	{
		this->fRefCount = inObjectRefCount;
	}
}

void CDBBaseObject::AddObjectReference ( void )
{
	BaseDBAssert_if (this != NULL)
	{
		this->SetObjectRefCount(this->GetObjectRefCount() + 1);
	}
}

void CDBBaseObject::RemoveObjectReference ( void )
{
	BaseDBAssert_if (this != NULL)
	{
		BaseDBAssert_if (this->GetObjectRefCount() != 0)
		{
			this->SetObjectRefCount(this->GetObjectRefCount() - 1);
		}
	}
}

CDBBaseObject* CDBBaseObject::Use ( CBaseDatabase	*inBaseDB )
{
	CDBBaseObject*	tempObject = NULL;

	BaseDBAssert_if ( this != NULL )
	{
		if (this->GetObjectRefCount() == 0)
		{
			this->AddObjectReference();
			this->fOwnerDBPtr = inBaseDB;
			tempObject = (inBaseDB->GetBaseObjectCachePtr())->AddObject2List(this);	// danger, danger, yield potential...{sigh}

			if (tempObject != this)
			{	// hmmm, we found an object with the same ID already on the list...
				// it must've been add while we were blocked on one of the list semaphores
				// if tempObject != this...that means we didn't successfully add "this" to the
				// CDBBaseObjectCache....and as a courtesy AddObject2List called use on the duplicate
				// already in the list... so we're done...we'll let who ever called "use" sort it out..

				// since we didn't add the object to the list...we should remove the reference that we've
				// added...
				this->RemoveObjectReference();
			}
			else if (tempObject == this)
			{
				tempObject = this;
			}
		}
		else
		{
			this->AddObjectReference();
			BaseDBAssert(this->fOwnerDBPtr == inBaseDB);
			tempObject = this;
		}
	}

	return (tempObject);
} // setDirty

void CDBBaseObject::Done ( CDBBaseObject* &inPtr )
{
	OSType	anObjType = NULL;
	CBaseDatabase *anOwnerDBPtr = NULL;
	CDBBaseObjectCache *aCachePtr = NULL;

	// CDBBaseObject is _VERY_ tricky code, do _NOT_ modify it unless you fully understand
	// all possible race conditions....code is commented with "danger, danger" around areas.
	// that cause potential yields/blockage......after passing one of these code sections...
	// you _MUST_ make sure that your changes don't blow away an object that may have been
	// referenced by another thread while you were blocked....

	BaseDBAssert( this == inPtr );
	if (inPtr != NULL)
	{
		anOwnerDBPtr = this->GetOwnerDBPtr();
		aCachePtr = anOwnerDBPtr->GetBaseObjectCachePtr();
		anObjType = this->GetObjectType();

		aCachePtr->LockTheCache(anObjType);

		BaseDBAssert( this->GetObjectRefCount() != 0 );
		if ( (this->GetObjectRefCount() - 1) == 0 ) 
		{
			if (this->GetDirtyFlag() == true)
			{
				this->SetDirtyFlag(false);
				this->setDirty(true);	// danger, danger, potential Yield HERE!!!
			}

			if (aCachePtr->RemoveObjFromList(this))
			{
				this->RemoveObjectReference();
				BaseDBAssert(this->GetObjectRefCount() == 0);

				delete inPtr;
				inPtr = NULL;
			}
			else
			{	// we yielded at some point, and someone has "re-referenced the object
				// while we were ditzing around...oh well, we'll still decrement the
				// refcount, we just won't remove it from the list...
				BaseDBAssert((this->GetObjectRefCount() - 1) != 0);
				this->RemoveObjectReference();
			}
		}
		else
		{
			this->RemoveObjectReference();
		}

		aCachePtr->UnLockTheCache(anObjType);
	}

	// always NULL out the inPtr...
	inPtr = NULL;
} // Done

Boolean CDBBaseObject::GetDirtyFlag ( void )
{
	BaseDBAssert_if (this != NULL)
	{
		return (this->fDirty);
	}

	return false;
}

void CDBBaseObject::SetDirtyFlag ( const Boolean inDirtyFlag )
{
	BaseDBAssert_if (this != NULL)
	{
		this->fDirty = inDirtyFlag;
	}
}


OSErr CDBBaseObject::WriteObject ( void )
{
	OSErr	result = -1;

	BaseDBAssert_if( (this != NULL) && (this->fOwnerDBPtr != NULL) )
	{
		BaseDBAssert( this->GetObjectID() != 0 );
		BaseDBAssert( this->GetObjectData() != NULL );
		BaseDBAssert( this->fObjHeader	!= NULL );
		BaseDBAssert( this->fObjFooter	!= NULL );
		BaseDBAssert( this->fObjData	!= NULL );
		BaseDBAssert( this->fObjHeader->fSignature == this->fObjFooter->fSignature );

		result = ( (this->GetOwnerDBPtr())->SetObjData( this->GetObjectType(),
														this->GetObjectID(),
														this->GetObjectData()));	// this can yield....
	}

	return (result);
}

//--------------------------------------------------------------------------------------------------
//	* GetObjectLock
//
//--------------------------------------------------------------------------------------------------

ExceptionCode CDBBaseObject::GetObjectLock ( const Boolean inWaitForObjectLock,
										 const uInt32  inMaxWaitTime )
{
	#pragma unused (inWaitForObjectLock)
	#pragma unused (inMaxWaitTime)

	return (kNoErr);
} // GetObjectLock



//--------------------------------------------------------------------------------------------------
//	* ReleaseObjectLock
//
//--------------------------------------------------------------------------------------------------

void CDBBaseObject::ReleaseObjectLock ( const Boolean inNotifyOtherThreadsFlag )
{
	#pragma unused (inNotifyOtherThreadsFlag)
} // ReleaseObjectLock

void CDBBaseObject::WakeUpObjectThreads ( void )
{
}


void CDBBaseObject::setDirty ( const Boolean inForceWriteFlag )
{
	OSErr	result = kNoErr;

	if ( (this->GetObjectRefCount() == 0) || (inForceWriteFlag == true) )
	{
		if (this->GetObjectID() != 0)
		{
			this->SetDirtyFlag(false);
			this->WriteObject();
		}
	}
	else
	{
		BaseDBAssert(this->GetObjectRefCount() != 0);
		BaseDBAssert(fObjHeader != NULL);
		BaseDBAssert(fObjFooter != NULL);
		BaseDBAssert(fObjData != NULL);
		BaseDBAssert(fObjHeader->fSignature == fObjFooter->fSignature);

		this->SetDirtyFlag(true);
	}
	BaseDBAssert(result == kNoErr);
} // setDirty


