                          
//                                        Ruler
//       1         2         3         4         5         6         7         8
//345678901234567890123456789012345678901234567890123456789012345678901234567890

    /********************************************************************/
    /*                                                                  */
    /*   The standard layout.                                           */
    /*                                                                  */
    /*   The standard layout for 'cpp' files in this code is as         */
    /*   follows:                                                       */
    /*                                                                  */
    /*      1. Include files.                                           */
    /*      2. Constants local to the class.                            */
    /*      3. Data structures local to the class.                      */
    /*      4. Data initializations.                                    */
    /*      5. Static functions.                                        */
    /*      6. Class functions.                                         */
    /*                                                                  */
    /*   The constructor is typically the first function, class         */
    /*   member functions appear in alphabetical order with the         */
    /*   destructor appearing at the end of the file.  Any section      */
    /*   or function this is not required is simply omitted.            */
    /*                                                                  */
    /********************************************************************/

#include "HeapPCH.hpp"

#include "Cache.hpp"
#include "Find.hpp"
#include "Heap.hpp"
#include "New.hpp"

    /********************************************************************/
    /*                                                                  */
    /*   Constants local to the class.                                  */
    /*                                                                  */
    /*   The constants supplied here control the size of the hash       */
    /*   table and other related features.                              */
    /*                                                                  */
    /********************************************************************/

CONST SBIT32 MinHash				  = 1024;
CONST SBIT32 MinHashSpace			  = (100/25);
CONST SBIT32 MinLookAside			  = 128;

CONST UNATIVE NoAddressMask			  = ((BIT32) -1);
CONST SNATIVE NoCacheEntry			  = -1;

    /********************************************************************/
    /*                                                                  */
    /*   Class constructor.                                             */
    /*                                                                  */
    /*   Create the hash table and initialize it ready for use. The     */
    /*   configuration information supplied from the parameters needs   */
    /*   to be carefully checked as it has come indirectly from the     */
    /*   user and may be bogus.                                         */
    /*                                                                  */
    /********************************************************************/

FIND::FIND
		( 
		SBIT32						  NewMaxHash,
		SBIT32						  NewMaxLookAside,
		SBIT32						  NewFindThreshold,
		BOOLEAN						  NewPublic, 
		BOOLEAN						  NewResize, 
		ROCKALL_BACK_END			  *NewRockallBackEnd,
		THREAD_SAFE					  *NewThreadSafe 
		)
    {
	REGISTER SBIT32 AlignMask = (NewRockallBackEnd -> NaturalSize()-1);

	//
	//   We need to make sure that the size of the hash table
	//   makes sense.  The hash table size needs to be a reasonable
	//   size (say 1k or larger) and a power of 2 (so we don't need
	//   to do any divides).
	//   
	if 
			(
			PowerOfTwo( (AlignMask+1) )
				&&
			(NewFindThreshold >= 0 )
				&&
			(NewMaxHash >= MinHash) 
				&&
			(ConvertDivideToShift( NewMaxHash,& HashMask ))
				&& 
			(NewMaxLookAside >= MinLookAside) 
				&& 
			(ConvertDivideToShift( NewMaxLookAside,& LookAsideMask ))
			)
		{
		REGISTER SBIT32 HashSize = (NewMaxHash * sizeof(LIST));
		REGISTER SBIT32 LookAsideSize = (NewMaxLookAside * sizeof(LOOK_ASIDE));
		REGISTER SBIT32 TotalSize = (HashSize + LookAsideSize);

		//
		//   Set up the hash table.
		//
		MaxHash = NewMaxHash;

		HashShift = (32-HashMask);
		HashMask = ((1 << HashMask)-1);
		Public = NewPublic;
		Resize = NewResize;

		//
		//   Set up the lookaside table.
		//
		MaxLookAside = NewMaxLookAside;

		MaxAddressMask = NoAddressMask;
		MinAddressMask = NoAddressMask;

		LookAsideActions = 0;
		LookAsideShift = (32-LookAsideMask);
		LookAsideMask = ((1 << LookAsideMask)-1);
		LookAsideThreshold = NewFindThreshold;

		RockallBackEnd = NewRockallBackEnd;

		ThreadSafe = NewThreadSafe;

		//
		//   Create some space for the find table and the 
		//   look aside table.
		//
		Hash = 
			((LIST*) RockallBackEnd -> NewArea
				( 
				AlignMask,
				TotalSize,
				False 
				)
			);

		LookAside = ((LOOK_ASIDE*) & Hash[ MaxHash ]);

		//
		//   If the memory allocation request for the hash
		//   table fails we are doomed.  If it works we need
		//   to call the constructor for each linked list
		//   head node.
		//
		if ( Hash != ((LIST*) AllocationFailure) )
			{
			REGISTER SBIT32 Count;

			//
			//   Call the constructor for each hash table
			//   linked list header.
			//
			for ( Count=0;Count < NewMaxHash;Count ++ )
				{ PLACEMENT_NEW( & Hash[ Count ],LIST ); }

			//
			//   Zero the look aside structures.  We need
			//   to do this to ensure they do not match a
			//   valid allocation address later.
			//
			for ( Count=0;Count < MaxLookAside;Count ++ )
				{
				REGISTER LOOK_ASIDE *Current = & LookAside[ Count ];

				Current -> Address = ((VOID*) NoCacheEntry);
				Current -> Page = ((PAGE*) NoCacheEntry);
#ifdef DEBUGGING
				Current -> Version = ((SBIT32) NoCacheEntry);
#endif
				}
#ifdef ENABLE_HEAP_STATISTICS

			//
			//   Zero the statistics information.
			//
			Fills = 0;
			Hits = 0;
			MaxPages = 0;
			MaxTests = 0;
			Misses = 0;
			Scans = 0;
			Tests = 0;
#endif
			Used = 0;
			}
		else
			{ Failure( "Create hash fails in constructor for FIND" ); }
		}
	else
		{ Failure( "Hash table size in constructor for FIND" ); }
    }

    /********************************************************************/
    /*                                                                  */
    /*   Delete a memory allocation.                                    */
    /*                                                                  */
    /*   We need to delete a particular memory allocation.  All         */
    /*   we have is an address.  We use this to find the largest        */
    /*   allocation page this address is contained in and then          */
    /*   navigate through the sub-divisions of this page until we       */
    /*   find the allocation we need to delete.                         */
    /*                                                                  */
    /********************************************************************/

BOOLEAN FIND::Delete( VOID *Address,CACHE *ParentCache )
    {
	REGISTER PAGE *Page;
	REGISTER BOOLEAN Update;
	
	//
	//   If we need to be thread safe then claim a sharable lock
	//   on the hash table to stop it being changed under our feet.
	//
	ClaimFindShareLock();

	//
	//   Lets try the lookaside table.  There is a pretty
	//   good chance that we will have the details we need 
	//   already in the cache.  If not we need to find it
	//   the hard way.  During the process we add the mapping
	//   into the lookaside for next time.
	//
	if
			( 
			Update = 
				(
				! FindLookAside
					( 
					((VOID*) (((SNATIVE) Address) & ~MinAddressMask)),
					& Page 
					)
				)
			)
		{
		//
		//   Find the allocation page and get the details of entry.
		//   We do this by finding the parent of the top cache.
		//   We  know that this is the global root and will find
		//   the correct page even if it is on another heap (as
		//   long as the find table is globally shared).
		//
		Page = (ParentCache -> FindParentPage( Address,this ));

		if ( Page != ((PAGE*) NULL) )
			{ Page = (Page -> FindPage( Address,NULL,this,True )); }
		}

	//
	//   We may have failed to find the address.  If so
	//   we simply fail the call.  If not we put the deleted 
	//   element back in the associated cache.
	//
	if ( Page != ((PAGE*) NULL) )
 		{
		REGISTER CACHE *Cache = (Page -> GetCache());
		REGISTER SBIT32 Original = (Page -> GetVersion());

		//
		//   Prefetch the class data if we are running a
		//   Pentium III or better with locks.  We do this
		//   because prefetching hot SMP data structures
		//   really helps.  However, if the structures are
		//   not shared (i.e. no locks) then it is worthless
		//   overhead.
		//
		if ( ThreadSafe )
		{ PREFETCH::Nta( ((CHAR*) Cache),sizeof(CACHE) ); }

		//
		//   Release the lock if we claimed it earlier and
		//   update the lookaside if needed.
		//
		if ( Update )
			{ ReleaseFindShareLockAndUpdate( Address,Page,Original ); }
		else
			{ ReleaseFindShareLock(); }

		//
		//   We have found the associated page description
		//   so pass the delete request along to the cache
		//   and get out of here.
		//
		return (Cache -> Delete( Address,Page,Original ));
		}
	else
		{ 
		//
		//   Release the lock if we claimed it earlier.
		//
		ReleaseFindShareLock();

		return False; 
		}
  }

    /********************************************************************/
    /*                                                                  */
    /*   Delete an item from the find table.                            */
    /*                                                                  */
    /*   We need to delete page from the find list.  We expect          */
    /*   this to take quite a while as multiple threads can be          */
    /*   using this class at the same time.                             */
    /*                                                                  */
    /********************************************************************/

VOID FIND::DeleteFromFindList( PAGE *Page )
	{
	REGISTER VOID *Address = (Page -> GetAddress());

	//
	//   Claim an exclusive lock so we can update the 
	//   hash and lookaside as needed.
	//
	ClaimFindExclusiveLock();

	//
	//   Delete the page from the hash table.
	//
	if ( ! Public )
		{ Page -> DeleteFromPrivateFindList( FindHashHead( Address ) ); }
	else
		{ Page -> DeleteFromPublicFindList( FindHashHead( Address ) ); }

	//
	//   When we create very small heaps (i.e. a heap
	//   where only 20-30 allocations are requested)
	//   the various caches become a problem as they
	//   tend to front load work.  So we allow a limit
	//   to be set before which we run with caches 
	//   disabled.
	//   
	if ( LookAsideActions >= LookAsideThreshold )
		{
		REGISTER SBIT32 Count;
		REGISTER CACHE *Cache = (Page -> GetCache());
		REGISTER SBIT32 Stride = (Cache -> GetAllocationSize());

		//
		//   We are about look up various look aside entries
		//   and delete any that are stale.  We need to do
		//   this for every lookaside slot that relates to
		//   the page.  If the allocation size is smaller
		//   than the lookaside slot size we can save some
		//   iterations by increasing the stride size.
		//
		if ( Stride <= ((SBIT32) MinAddressMask) )
			{ Stride = ((SBIT32) (MinAddressMask+1)); }

		//
		//   Whenever we delete an entry from the hash table
		//   the lookaside is potentially corrupt.  So we 
		//   need to delete any look aside entries relating
		//   to this page.
		//
		for ( Count=0;Count < Cache -> GetPageSize();Count += Stride )
			{
			REGISTER VOID *Segment = 
				((VOID*) ((((SNATIVE) Address) + Count) & ~MinAddressMask));
			REGISTER LOOK_ASIDE *Current = 
				(FindLookAsideHead( Segment ));

			//
			//   Delete the look aside entry if it is stale.
			//
			if ( Segment == Current -> Address )
				{
				Current -> Address = ((VOID*) NoCacheEntry);
				Current -> Page = ((PAGE*) NoCacheEntry);
#ifdef DEBUGGING
				Current -> Version = ((SBIT32) NoCacheEntry);
#endif
				}
			}
		}

	//
	//   Update the statistics.
	//
	Used --;

	//
	//   Release the lock if we claimed it earlier.
	//
	ReleaseFindExclusiveLock();
	}

    /********************************************************************/
    /*                                                                  */
    /*   Details of a memory allocation.                                */
    /*                                                                  */
    /*   We need to the details of a particular memory allocation.      */
    /*   All we have is an address.  We use this to find the largest    */
    /*   allocation page this address is contained in and then          */
    /*   navigate through the sub-divisions of this page until we       */
    /*   find the allocation.                                           */
    /*                                                                  */
    /********************************************************************/

BOOLEAN FIND::Details
		( 
		VOID						  *Address,
		SEARCH_PAGE					  *Details,
		CACHE						  *ParentCache,
		SBIT32						  *Size 
		)
    {
	REGISTER PAGE *Page;
	REGISTER BOOLEAN Result;
	REGISTER BOOLEAN Update;
	
	//
	//   If we need to be thread safe then claim a sharable lock
	//   on the hash table to stop it being changed under our feet.
	//
	ClaimFindShareLock();

	//
	//   Lets try the lookaside table.  There is a pretty
	//   good chance that we will have the deatils we need 
	//   already in the cache.  If not we need to find it
	//   the hard way.  During the process we add the mapping
	//   into the lookaside for next time.
	//
	if
			( 
			Update = 
				(
				! FindLookAside
					( 
					((VOID*) (((SNATIVE) Address) & ~MinAddressMask)),
					& Page 
					)
				)
			)
		{
		//
		//   Find the allocation page and get the details of entry.
		//   We do this by finding the parent of the top cache.
		//   We  know that this is the global root and will find
		//   the correct page even if it is on another heap (as
		//   long as the find table is globally shared).
		//
		Page = (ParentCache -> FindParentPage( Address,this ));

		if ( Page != ((PAGE*) NULL) )
			{ Page = (Page -> FindPage( Address,Details,this,True )); }
		}
	else
		{
		//
		//   We may need to provide the all the details of the
		//   allocation for some reason.
		//
		if ( Details != NULL )
			{ Page = (Page -> FindPage( Address,Details,this,True )); }
		}

	//
	//   We may have failed to find the address.  If so
	//   we simply fail the call.  If not we extract the 
	//   information we want.
	//
	if ( Result = (Page != ((PAGE*) NULL)) )
 		{
		//
		//   Compute the size.  We would normally expect
		//   this to be the cache size.  However, there
		//   are some weird pages that sometimes have
		//   other sizes.
		//
		(*Size) = (Page -> ActualSize());
		}

	//
	//   Release the lock if we claimed it earlier and 
	//   update the lookaside if needed.
	//
	if ( (Update) && (Result) )
		{ ReleaseFindShareLockAndUpdate( Address,Page,Page -> GetVersion() ); }
	else
		{ ReleaseFindShareLock(); }

	return Result;
    }

    /********************************************************************/
    /*                                                                  */
    /*   Find in the look aside.                                        */
    /*                                                                  */
    /*   We need to find a particular page in the look aside.  So we    */
    /*   try a simple look up (no lists or chains).                     */
    /*                                                                  */
    /********************************************************************/

BOOLEAN FIND::FindLookAside( VOID *Address,PAGE **Page )
    {
	//
	//   When we create very small heaps (i.e. a heap
	//   where only 20-30 allocations are requested)
	//   the various caches become a problem as they
	//   tend to front load work.  So we allow a limit
	//   to be set before which we run with caches 
	//   disabled.
	//   
	if ( LookAsideActions >= LookAsideThreshold )
		{
		REGISTER LOOK_ASIDE *Current = FindLookAsideHead( Address );

		//
		//   We have hashed to a lookaside slot.  Maybe
		//   it contains what we want or maybe not.
		//
		if ( Address == Current -> Address )
			{
#ifdef DEBUGGING
			if ( Current -> Version == (Current -> Page -> GetVersion()) )
				{
#endif
				//
				//   We hit the lookaside and the 
				//   contents are valid.
				//
				(*Page) = (Current -> Page);
#ifdef ENABLE_HEAP_STATISTICS

				//
				//   Update the statistics.
				//
				Hits ++;
#endif

				return True;
#ifdef DEBUGGING
				}
			else
				{ Failure( "Deleted page in FindLookAside" ); }
#endif
			}
		}
	else
		{
		//
		//   We update number of times we tried to
		//   use the lookaside and it was disabled.  
		//   After a while this will lead to the 
		//   lookaside being enabled.
		//
		LookAsideActions ++; 
		}
#ifdef ENABLE_HEAP_STATISTICS

	//
	//   We missed the lookaside so update the 
	//   statistics to reflect our misfortune.
	//
	Misses ++;
#endif

	return False; 
    }

    /********************************************************************/
    /*                                                                  */
    /*   Find a page.                                                   */
    /*                                                                  */
    /*   We need to find a particular page in the hash table.  So we    */
    /*   scan along the associated linked list looking for a match.     */
    /*                                                                  */
    /********************************************************************/

PAGE *FIND::FindPage( VOID *Address,CACHE *ParentCache )
    {
#ifdef ENABLE_HEAP_STATISTICS
	REGISTER SBIT32 Cycles = 0;
	REGISTER PAGE *Result = NULL;
#endif
	//
	//   There are two find lists.  There is the
	//   private find list and the public find list.
	//   We choose which one to walk along.
	//
	if ( ! Public )
		{
		REGISTER PAGE *Page;

		//
		//   Find the associated hash bucket and then walk
		//   along the linked list for this looking for
		//   the correct page description.
		//
		for 
				( 
				Page = PAGE::FirstInPrivateFindList( FindHashHead( Address ) );
				! Page -> EndOfPrivateFindList();
				Page = Page -> NextInPrivateFindList()
				)
			{
				assert(Page && "Should never happen: First in private list didn't find anything");
#ifdef ENABLE_HEAP_STATISTICS
			//
			//   Count the number of iterations in when we
			//   are recording statistics so we can calculate
			//   the average chain length.
			//
			Cycles ++;

#endif
			//
			//   We can identify the the target page by two key
			//   characteristics.  These are the start address and
			//   the parent page.   Although we may have sub-divided
			//   a page into various chunks each chunk will have
			//   a different parent (although its start address
			//   may sometimes be the same).
			//
			if 
					( 
					(Address == (Page -> GetAddress())) 
						&& 
					(ParentCache == (Page -> GetParentPage()))
					)
				{
#ifdef ENABLE_HEAP_STATISTICS
				//
				//   We have found the target page.  So return it
				//   to the caller.
				//
				if ( Page -> ValidPage() )
					{
					Result = Page;
					break;
					}
				else
					{ Failure( "Deleted page in FindPage" ); }
#else
				return Page;
#endif
				}
			}
		}
	else
		{
		REGISTER PAGE *Page;

		//
		//   Find the associated hash bucket and then walk
		//   along the linked list for this looking for
		//   the correct page description.
		//
		for 
				( 
				Page = PAGE::FirstInPublicFindList( FindHashHead( Address ) );
				! Page -> EndOfPublicFindList();
				Page = Page -> NextInPublicFindList()
				)
			{
				assert(Page && "Should never happen: First in private list didn't find anything");
#ifdef ENABLE_HEAP_STATISTICS
			//
			//   Count the number of iterations in when we
			//   are recording statistics so we can calculate
			//   the average chain length.
			//
			Cycles ++;

#endif
			//
			//   We can identify the the target page by two key
			//   characteristics.  These are the start address and
			//   the parent page.   Although we may have sub-divided
			//   a page into various chunks each chunk will have
			//   a different parent (although its start address
			//   may sometimes be the same).
			//
			if 
					( 
					(Address == (Page -> GetAddress())) 
						&& 
					(ParentCache == (Page -> GetParentPage()))
					)
				{
#ifdef ENABLE_HEAP_STATISTICS
				//
				//   We have found the target page.  So return it
				//   to the caller.
				//
				if ( Page -> ValidPage() )
					{
					Result = Page;
					break;
					}
				else
					{ Failure( "Deleted page in FindPage" ); }
#else
				return Page;
#endif
				}
			}
		}

#ifdef ENABLE_HEAP_STATISTICS
	//
	//   When we are in statistics mode we need to update the
	//   information so we can output it at the end of the
	//   run.
	//
	if ( MaxTests < Cycles )
		{ MaxTests = Cycles; }

	Tests += Cycles;

	Scans ++;

	return Result;
#else
	return NULL;
#endif
    }

    /********************************************************************/
    /*                                                                  */
    /*   Insert an item into the find table.                            */
    /*                                                                  */
    /*   We need to insert a new page into the find table.  We expect   */
    /*   this to take quite a while as multiple threads can be using    */
    /*   this class at the same time.                                   */
    /*                                                                  */
    /********************************************************************/

VOID FIND::InsertInFindList( PAGE *Page )
	{
	REGISTER VOID *Address = (Page -> GetAddress());

	//
	//   Claim an exclusive lock so we can update the 
	//   find table and lookaside as needed.
	//
	ClaimFindExclusiveLock();

	//
	//   Insert a new page into the find table.
	//
	if ( ! Public )
		{ Page -> InsertInPrivateFindList( FindHashHead( Address ) ); }
	else
		{ Page -> InsertInPublicFindList( FindHashHead( Address ) ); }

	//
	//   When we create very small heaps (i.e. a heap
	//   where only 20-30 allocations are requested)
	//   the various caches become a problem as they
	//   tend to front load work.  So we allow a limit
	//   to be set before which we run with caches 
	//   disabled.
	//   
	if ( LookAsideActions >= LookAsideThreshold )
		{
		REGISTER SBIT32 Count;
		REGISTER CACHE *Cache = (Page -> GetCache());
		REGISTER SBIT32 Stride = (Cache -> GetAllocationSize());

		//
		//   We are about look up various lookaside entries
		//   and update any that are stale.  We need to do
		//   this for every lookaside slot that relates to
		//   the page.  If the allocation size is smaller
		//   than the lookaside slot size we can save some
		//   iterations by increasing the stride size.
		//
		if ( Stride <= ((SBIT32) MinAddressMask) )
			{ Stride = ((SBIT32) (MinAddressMask+1)); }

		//
		//   Whenever we add an entry from the find table
		//   the lookaside is potentially corrupt.  So we 
		//   need to update any lookaside entries relating
		//   to the page.
		//
		for ( Count=0;Count < Cache -> GetPageSize();Count += Stride )
			{
			REGISTER VOID *Segment = 
				((VOID*) ((((SNATIVE) Address) + Count) & ~MinAddressMask));
			REGISTER LOOK_ASIDE *Current = 
				(FindLookAsideHead( Segment ));

			//
			//   Add the new page to the lookaside as we
			//   expect it to get hit pretty soon one way
			//   or another.
			//
			Current -> Address = Segment;
			Current -> Page = Page;
#ifdef DEBUGGING
			Current -> Version = Page -> GetVersion();
#endif
			}
		}

	//
	//   Update the statistics and resize the find
	//   table if it is over capacity.
	//
	if ( ((++ Used) + (MaxHash / MinHashSpace)) > MaxHash )
		{ ResizeHashTable(); }
#ifdef ENABLE_HEAP_STATISTICS

	if ( Used > MaxPages )
		{ MaxPages = Used; }
#endif

	//
	//   Release the lock if we claimed it earlier.
	//
	ReleaseFindExclusiveLock();
	}

    /********************************************************************/
    /*                                                                  */
    /*   A known area.                                                  */
    /*                                                                  */
    /*   We have an address and don't have a clue which heap            */
    /*   owns the space.  Here we take a look at the address            */
    /*   and figure out if it is known to the current heap.             */
    /*                                                                  */
    /********************************************************************/

BOOLEAN FIND::KnownArea( VOID *Address,CACHE *ParentCache )
    {
	REGISTER PAGE *Page;
	
	//
	//   If we need to be thread safe then claim a sharable lock
	//   on the hash table to stop it being changed under our feet.
	//
	ClaimFindShareLock();

	//
	//   Find out if the address belongs to this heap
	//   or any other heap of which we are aware (i.e.
	//   when single image is active).
	//
	Page = (ParentCache -> FindParentPage( Address,this ));

	//
	//   Release the lock if we claimed it earlier.
	//
	ReleaseFindShareLock();

	return (Page != ((PAGE*) NULL));
	}

    /********************************************************************/
    /*                                                                  */
    /*   Release a shared lock and update.                              */
    /*                                                                  */
    /*   We have been asked to insert a page into the lookaside.        */
    /*   We assume the caller already has a share lock which we         */
    /*   release when we are finished.                                  */
    /*                                                                  */
    /********************************************************************/

VOID FIND::ReleaseFindShareLockAndUpdate
		( 
		VOID						  *Address,
		PAGE						  *Page,
		SBIT32						  Version
		)
    {
	//
	//   When we create very small heaps (i.e. a heap
	//   where only 20-30 allocations are requested)
	//   the various caches become a problem as they
	//   tend to front load work.  So we allow a limit
	//   to be set before which we run with caches 
	//   disabled.
	//   
	if ( LookAsideActions >= LookAsideThreshold )
		{
		//
		//   Claim an exclusive lock so we can update the 
		//   lookaside as needed.
		//
		ChangeToExclusiveLock();

#ifdef DEBUGGING
		if ( Page -> ValidPage() )
			{
#endif
			if ( Version == (Page -> GetVersion()) )
				{
				REGISTER SNATIVE Base = (((SNATIVE) Address) & ~MinAddressMask);
				REGISTER VOID *Segment = ((VOID*) Base);
				REGISTER LOOK_ASIDE *Current = FindLookAsideHead( Segment );

				//
				//   Overwrite any existing information.
				//
				Current -> Address = Segment;
				Current -> Page = Page;
#ifdef DEBUGGING
				Current -> Version = Page -> GetVersion();
#endif
#ifdef ENABLE_HEAP_STATISTICS

				//
				//   Update the statistics.
				//
				Fills ++;
#endif
				}
#ifdef DEBUGGING
			}
		else
			{ Failure( "Deleted page in ReleaseFindShareLockAndUpdate" ); }
#endif

		//
		//   Release the lock if we claimed it earlier.
		//
		ReleaseFindExclusiveLock();
		}
	else
		{ 
		//
		//   Release the lock if we claimed it earlier.
		//
		ReleaseFindShareLock(); 
		}
    }

    /********************************************************************/
    /*                                                                  */
    /*   Resize the find table.                                         */
    /*                                                                  */
    /*   We need to grow the hash table as it appears to be a little    */
    /*   small given the number of pages that have been created.        */
    /*                                                                  */
    /********************************************************************/

VOID FIND::ResizeHashTable( VOID )
    {
	AUTO SBIT32 NewHashMask;
	AUTO SBIT32 NewLookAsideMask;

	//
	//   When we need to resize the hash table it is a
	//   straight race.  The first thread to claim the
	//   lock gets to do the work.  Everyone else just
	//   exits.
	//
	if ( (Resize) && (Spinlock.ClaimLock(0)) )
		{
		REGISTER SBIT32 AlignMask = (RockallBackEnd -> NaturalSize()-1);
		REGISTER SBIT32 NewMaxHash = (MaxHash * ExpandStore);
		REGISTER SBIT32 NewMaxLookAside = (MaxLookAside * ExpandStore);
		REGISTER SBIT32 NewHashSize = (NewMaxHash * sizeof(LIST));
		REGISTER SBIT32 NewLookAsideSize = (NewMaxLookAside * sizeof(LOOK_ASIDE));
		REGISTER SBIT32 NewTotalSize = (NewHashSize + NewLookAsideSize);
		REGISTER SBIT32 HashSize = (MaxHash * sizeof(LIST));
		REGISTER SBIT32 LookAsideSize = (MaxLookAside * sizeof(LOOK_ASIDE));
		REGISTER SBIT32 TotalSize = (HashSize + LookAsideSize);

		//
		//   It is actually possible for a thread to get
		//   delayed for so long that it thinks the hash 
		//   table still needs to be resized long after the 
		//   work has been completed.  Additionally, we want
		//   to make sure that all the new values are sane.
		//
		if 
				(
				PowerOfTwo( (AlignMask+1) )
					&&
				(NewMaxHash > 0)
					&&
				(ConvertDivideToShift( NewMaxHash,& NewHashMask ))
					&&
				(NewMaxLookAside > 0)
					&& 
				(ConvertDivideToShift( NewMaxLookAside,& NewLookAsideMask ))
					&&
				((Used + (MaxHash / MinHashSpace)) > MaxHash)
				)
			{
			REGISTER LIST *NewHash;
			REGISTER LOOK_ASIDE *NewLookAside;

			//
			//   We have been picked as the victim who
			//   needs to resize the hash table.  We are
			//   going to call the external allocator 
			//   to get more memory.  As we know this is 
			//   likely to to nail us we drop the lock to 
			//   allow other threads to continue.
			//
			ReleaseFindExclusiveLock();

			//
			//   We know that allocating a new table and 
			//   initializing it is going to take ages.
			//   Well at least everyone else gets to carry
			//   on in the mean time.
			//
			NewHash = 
				((LIST*) RockallBackEnd -> NewArea
					( 
					AlignMask,
					NewTotalSize,
					False 
					)
				);

			NewLookAside = 
				((LOOK_ASIDE*) & NewHash[ NewMaxHash ]);

			//
			//   If the memory allocation request for the hash
			//   table fails we exit and try again later. 
			//
			if ( NewHash != ((LIST*) AllocationFailure) )
				{
				REGISTER SBIT32 Count;

				//
				//   Call the constructor for each hash table
				//   linked list header.
				//
				for ( Count=0;Count < NewMaxHash;Count ++ )
					{ PLACEMENT_NEW( & NewHash[ Count ],LIST ); }

				//
				//   Zero the look aside structure.
				//
				for ( Count=0;Count < NewMaxLookAside;Count ++ )
					{
					REGISTER LOOK_ASIDE *Current = & NewLookAside[ Count ];

					Current -> Address = ((VOID*) NoCacheEntry);
					Current -> Page = ((PAGE*) NoCacheEntry);
#ifdef DEBUGGING
					Current -> Version = ((SBIT32) NoCacheEntry);
#endif
					}
				}

			//
			//   Claim an exclusive lock so we can resize  
			//   the hash table.
			//
			ClaimFindExclusiveLock();

			//
			//   If we have allocated the new find table
			//   we can now rehash the existing entries.
			//   If not we are out of here.
			//
			if ( NewHash != ((LIST*) AllocationFailure) )
				{
				REGISTER SBIT32 Count;
				REGISTER SBIT32 MaxOldHash = MaxHash;
				REGISTER LIST *OldHash = Hash;

				//
				//   Update the control information 
				//   for the new hash table.
				//
				MaxHash = NewMaxHash;
				HashShift = (32-NewHashMask);
				HashMask = ((1 << NewHashMask)-1);

				MaxLookAside = NewMaxLookAside;
				LookAsideShift = (32-NewLookAsideMask);
				LookAsideMask = ((1 << NewLookAsideMask)-1);

				Hash = NewHash;
				LookAside = NewLookAside;

				//
				//   Delete all the existing records
				//   from the old hash table and insert
				//   them into the new hash table.
				//
				for ( Count=0;Count < MaxOldHash;Count ++ )
					{
					REGISTER LIST *Current = & OldHash[ Count ];

					//
					//   There are two find lists.  There 
					//   is the private find list and the
					//   public find list.  We choose which
					//   one to walk along.
					//
					if ( ! Public )
						{
						//
						//   Walk along each hash bucket 
						//   deleting the records and inserting
						//   them into the new hash table.
						//
						while ( ! Current -> EndOfList() )
							{
							REGISTER PAGE *Page = 
								(PAGE::FirstInPrivateFindList( Current ));
							REGISTER VOID *Address = 
								(Page -> GetAddress());
							REGISTER LIST *HeadOfList = 
								(FindHashHead( Address ));

							//
							//   Delele from the old find list
							//   and add into the new find
							//   list.
							//
							Page -> DeleteFromPrivateFindList( Current );

							Page -> InsertInPrivateFindList( HeadOfList );
							}
						}
					else
						{
						//
						//   Walk along each hash bucket 
						//   deleting the records and inserting
						//   them into the new hash table.
						//
						while ( ! Current -> EndOfList() )
							{
							REGISTER PAGE *Page = 
								(PAGE::FirstInPublicFindList( Current ));
							REGISTER VOID *Address = 
								(Page -> GetAddress());
							REGISTER LIST *HeadOfList = 
								(FindHashHead( Address ));

							//
							//   Delele from the old find list
							//   and add into the new find
							//   list.
							//
							Page -> DeleteFromPublicFindList( Current );

							Page -> InsertInPublicFindList( HeadOfList );
							}
						}

					}

				//
				//   Time to do more operating system work
				//   so lets drop the lock again.
				//
				ReleaseFindExclusiveLock();

				//
				//   Delete all the list heads and return the
				//   original allocation to the operating system.
				//
				for ( Count=0;Count < MaxOldHash;Count ++ )
					{ PLACEMENT_DELETE( & OldHash[ Count ],LIST ); }

				//
				//   Deallocate the old extent.
				//
				RockallBackEnd -> DeleteArea
					( 
					((VOID*) OldHash),
					TotalSize,
					False 
					);

				//
				//   We are finished so reclaim the lock
				//   so we can exit.
				//
				ClaimFindExclusiveLock();
				}
			else
				{ Resize = False; }
			}

		Spinlock.ReleaseLock();
		}
    }

    /********************************************************************/
    /*                                                                  */
    /*   Update the find table.                                         */
    /*                                                                  */
    /*   We need to update the find table with certain information      */
    /*   to ensure it is used correctly and consistently.               */
    /*                                                                  */
    /********************************************************************/

VOID FIND::UpdateFind( BIT32 NewMaxAddressMask,BIT32 NewMinAddressMask )
    {
	//
	//   When we have a single heap image all the 'TopCache' sizes
	//   must be the same.
	//
	if 
			( 
			(MaxAddressMask == NoAddressMask) 
				|| 
			(MaxAddressMask == NewMaxAddressMask) 
			)
		{
		//
		//   If we need to be thread safe then claim a sharable lock
		//   on the hash table to stop it being changed under our feet.
		//
		ClaimFindExclusiveLock();

		//
		//   Update the max address mask if it is not the current
		//   value but yet consistent.
		//
		MaxAddressMask = NewMaxAddressMask;

		//
		//   Update the address mask is the new heap has a smaller
		//   parent than all of the other heaps.
		//
		if ( MinAddressMask > NewMinAddressMask )
			{ MinAddressMask = NewMinAddressMask; }

		//
		//   Release the lock if we claimed it earlier.
		//
		ReleaseFindExclusiveLock();
		}
	else
		{ Failure( "Different 'TopCache' sizes with 'SingleImage'" ); }
	}

    /********************************************************************/
    /*                                                                  */
    /*   Walk the heap.                                                 */
    /*                                                                  */
    /*   We have been asked to walk the heap.  It is hard to know       */
    /*   whay anybody might want to do this given the rest of the       */
    /*   functionality available.  Nonetheless, we just do what is      */
    /*   required to keep everyone happy.                               */
    /*                                                                  */
    /********************************************************************/

BOOLEAN FIND::Walk
		( 
		BOOLEAN						  *Active,
		VOID						  **Address,
		CACHE						  *ParentCache,
		SBIT32						  *Size 
		)
    {
	REGISTER VOID *Memory = (*Address);
	REGISTER BOOLEAN Result;
	REGISTER BOOLEAN Update;
	REGISTER PAGE *Page;
	
	//
	//   If we need to be thread safe then claim a sharable lock
	//   on the hash table to stop it being changed under our feet.
	//
	ClaimFindShareLock();

	//
	//   When the address is null we need to set up the heap
	//   walk.  In all other cases we just extract the next
	//   allocation in the list.
	//
	if ( Memory != NULL )
		{
		AUTO SEARCH_PAGE Details;

		//
		//   Lets try the lookaside table.  There is a pretty
		//   good chance that we will have the details we need 
		//   already in the cache.  If not we need to find it
		//   the hard way.  During the process we add the mapping
		//   into the lookaside for next time.
		//
		if
				(
				Update =
					( 
					! FindLookAside
						( 
						((VOID*) (((SNATIVE) Memory) & ~MinAddressMask)),
						& Page 
						) 
					)
				)
			{
			//
			//   Find the allocation page and get the details of entry.
			//   We do this by finding the parent of the top cache.
			//   We  know that this is the global root and will find
			//   the correct page even if it is on another heap (as
			//   long as the find table is globally shared).
			//
			Page = (ParentCache -> FindParentPage( Memory,this ));
			}

		//
		//   We now compute all the details relating to the address
		//   so we can find any subsequent allocation.
		//
		if ( Page != ((PAGE*) NULL) )
			{ Page = (Page -> FindPage( Memory,& Details,this,True )); }

		//
		//   We may have failed to find the address  .If so
		//   we simply fail the call.  If not we find the next
		//   allocation in the heap.
		//
		if ( Result = ((Page != ((PAGE*) NULL)) && (Details.Found)) )
 			{
			//
			//   We need to walk the heap to get te details
			//   of the next allocation.
			//
			if ( Result = (Page -> Walk( & Details,this )) )
				{
				REGISTER BIT32 AllocationBit =
					((*Details.VectorWord) & Details.AllocationMask);

				(*Active) = (AllocationBit != 0);
				(*Address) = Details.Address;
				(*Size) = (Details.Page -> ActualSize());

				//
				//   If we are considering putting something
				//   in the lookaside lets make sure that
				//   we will get to hit the cache entry at
				//   least once.  If not lets forget putting
				//   it in the cache.
				//
				if ( Update )
					{
					Update =
						(
						(((SNATIVE) Memory) & ~MinAddressMask)
							==
						(((SNATIVE) Details.Address) & ~MinAddressMask)
						);
					}
				}
			}
		}
	else
		{
		AUTO SEARCH_PAGE Details;

		//
		//   We start a heap walk by setting the initial 
		//   address to the value null.
		//
		Details.Address = NULL;
		Details.Cache = ParentCache;
		Details.Page = NULL;

		Page = NULL;
		Update = False;

		//
		//   We walk the heap to get te details of the
		//   first heap allocation.
		//
		if ( Result = (Page -> Walk( & Details,this )) )
			{
			REGISTER BIT32 AllocationBit =
				((*Details.VectorWord) & Details.AllocationMask);

			(*Active) = (AllocationBit != 0);
			(*Address) = Details.Address;
			(*Size) = (Details.Page -> ActualSize());
			}
		}

	//
	//   Release the lock if we claimed it earlier and
	//   update the lookaside if needed.
	//
	if ( (Update) && (Result) )
		{ ReleaseFindShareLockAndUpdate( Memory,Page,Page -> GetVersion() ); }
	else
		{ ReleaseFindShareLock(); }

	return Result;
    }

    /********************************************************************/
    /*                                                                  */
    /*   Class destructor.                                              */
    /*                                                                  */
    /*   Delete the hash table and release all the associated memory.   */
    /*                                                                  */
    /********************************************************************/

FIND::~FIND( VOID )
    {
	REGISTER SBIT32 Count;
	REGISTER SBIT32 HashSize = (MaxHash * sizeof(LIST));
	REGISTER SBIT32 LookAsideSize = (MaxLookAside * sizeof(LOOK_ASIDE));
	REGISTER SBIT32 TotalSize = (HashSize + LookAsideSize);

	//
	//   Call the destructor for each hash table
	//   linked list header.
	//
	for ( Count=0;Count < MaxHash;Count ++ )
		{ PLACEMENT_DELETE( & Hash[ Count ],LIST ); }

	//
	//   Deallocate the area.
	//
	RockallBackEnd -> DeleteArea
		( 
		((VOID*) Hash),
		TotalSize,
		False 
		);
    }
