Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1291 lines
39 KiB

// 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 BIT32 NoAddressMask = ((BIT32) -1);
CONST SBIT32 NoCacheEntry = -1;
#ifndef ENABLE_RECURSIVE_LOCKS
/********************************************************************/
/* */
/* Static member initialization. */
/* */
/* Static member initialization sets the initial value for all */
/* static members. */
/* */
/********************************************************************/
THREAD_LOCAL_STORE FIND::LockCount;
#endif
/********************************************************************/
/* */
/* 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,
ROCKALL *NewRockall,
BOOLEAN NewResize,
BOOLEAN NewThreadSafe
)
{
REGISTER SBIT32 AlignMask = (NewRockall -> 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);
Resize = NewResize;
//
// Set up the lookaside table.
//
MaxLookAside = NewMaxLookAside;
MaxAddressMask = NoAddressMask;
MinAddressMask = NoAddressMask;
LookAsideActions = 0;
LookAsideShift = (32-LookAsideMask);
LookAsideMask = ((1 << LookAsideMask)-1);
LookAsideThreshold = NewFindThreshold;
ThreadSafe = NewThreadSafe;
//
// Create some space for the find table and the
// look aside table.
//
Hash = ((LIST*) NewRockall -> NewArea( AlignMask,TotalSize,False ));
LookAside = ((LOOK_ASIDE*) & Hash[ MaxHash ]);
Rockall = NewRockall;
//
// 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*) (((LONG) 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 ));
if ( Page != ((PAGE*) NULL) )
{ Page = (Page -> FindPage( Address,NULL,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.
//
Page -> DeleteFromFindList( 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*) ((((LONG) 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*) (((LONG) 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 ));
if ( Page != ((PAGE*) NULL) )
{ Page = (Page -> FindPage( Address,Details,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,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
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::FirstInFindList( FindHashHead( Address ) );
! Page -> EndOfFindList();
Page = Page -> NextInFindList()
)
{
#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.
//
Page -> InsertInFindList( 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*) ((((LONG) 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 75% full.
//
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 ));
//
// 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 LONG Base = (((LONG) 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 = (Rockall -> 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*) Rockall -> 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 ];
//
// Walk along each hash bucket
// deleting the records and inserting
// them into the new hash table.
//
while ( ! Current -> EndOfList() )
{
REGISTER PAGE *Page = PAGE::FirstInFindList( Current );
REGISTER VOID *Address = (Page -> GetAddress());
Page -> DeleteFromFindList( Current );
Page -> InsertInFindList( FindHashHead( Address ) );
}
}
//
// 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.
//
Rockall -> 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*) (((LONG) 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 ));
}
//
// 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,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 )) )
{
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 =
(
(((LONG) Memory) & ~MinAddressMask)
==
(((LONG) 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 )) )
{
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.
//
Rockall -> DeleteArea( ((VOID*) Hash),TotalSize,False );
}