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.
 
 
 
 
 
 

939 lines
32 KiB

// Ruler
// 1 2 3 4 5 6 7 8
//345678901234567890123456789012345678901234567890123456789012345678901234567890
/********************************************************************/
/* */
/* The standard layout. */
/* */
/* The standard layout for 'cpp' files for 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 that is not required is simply omitted. */
/* */
/********************************************************************/
#include "HeapPCH.hpp"
#include "Bucket.hpp"
#include "Cache.hpp"
#include "Heap.hpp"
/********************************************************************/
/* */
/* Constants local to the class. */
/* */
/* The constants supplied here relate the compuation of the */
/* current active page address range. */
/* */
/********************************************************************/
CONST SBIT32 HighestAddress = -1;
/********************************************************************/
/* */
/* Class constructor. */
/* */
/* Create a new allocation bucket and prepare it for use. */
/* We need to be sure to carefully check everything we have */
/* been supplied as it has come indirectly from the user. */
/* */
/********************************************************************/
BUCKET::BUCKET
(
SBIT32 NewAllocationSize,
SBIT32 NewChunkSize,
SBIT32 NewPageSize
)
{
//
// We want to make sure that the bucket configuration
// appears to make basic sense. If not we have no
// alternative than to throw an expection.
//
if
(
(NewAllocationSize > 0)
&&
(NewChunkSize >= NewAllocationSize)
&&
(NewChunkSize <= NewPageSize)
&&
PowerOfTwo( NewPageSize )
)
{
//
// Create the bucket and prepare it for use.
// Pre-compute any information we can here to
// save work later.
//
AllocationSize = NewAllocationSize;
ChunkSize = NewChunkSize;
PageSize = NewPageSize;
ActivePages = 0;
AllocationShift = 0;
ChunkShift = 0;
//
// Compute the optimization level from the available
// bucket information. The highest level means
// everything is a power of two (just shifts - yipee !!).
// The next means no chunks (i.e. simple scaling), The
// next means simple chunks (i.e. complex scaling).
// The final alternative is to horrid to discuss.
//
if ( ConvertDivideToShift( AllocationSize,& AllocationShift ) )
{
//
// If we are not using chunking we can skip the
// extra compuation that is needed. We can tell
// this is the case if the chunk size and the page
// size match or if the chunk size is a multiple
// of the allocation size.
//
if
(
(ChunkSize == PageSize)
||
((ChunkSize % AllocationSize) == 0)
)
{
//
// The best case is when we can use shifts
// instead of multiply and divide.
//
ComputeAddressFunction = ComputeAddressBestCase;
ComputeOffsetFunction = ComputeOffsetBestCase;
}
else
{
//
// When we have chunks we have to do more
// work invloving additional multiples and
// divides. Nonetheless, we try to minimize
// these as far as possible.
//
if ( ConvertDivideToShift( ChunkSize,& ChunkShift ) )
{
//
// We have managed to replace some of the
// multiplies and divides with shifts.
//
ComputeAddressFunction = ComputeAddressPoorCase;
ComputeOffsetFunction = ComputeOffsetPoorCase;
}
else
{
//
// We are unable to optimize this case.
// These calls will really hurt.
//
ComputeAddressFunction = ComputeAddressWorstCase;
ComputeOffsetFunction = ComputeOffsetWorstCase;
}
}
}
else
{
//
// If we are not using chunking we can skip the
// extra compuation that is needed. We can tell
// this is the case if the chunk size and the page
// size match or if the chunk size is a multiple
// of the allocation size.
//
if
(
(ChunkSize == PageSize)
||
((ChunkSize % AllocationSize) == 0)
)
{
//
// A good case is when we can use a few
// simple multiplies and divides to do simple
// scaling.
//
ComputeAddressFunction = ComputeAddressGoodCase;
ComputeOffsetFunction = ComputeOffsetGoodCase;
}
else
{
//
// When we have chunks we have to do more
// work invloving additional multiples and
// divides. Nonetheless, we try to minimize
// these as far as possible.
//
if ( ConvertDivideToShift( ChunkSize,& ChunkShift ) )
{
//
// We have managed to replace some of the
// multiplies and divides with shifts.
//
ComputeAddressFunction = ComputeAddressPoorCase;
ComputeOffsetFunction = ComputeOffsetPoorCase;
}
else
{
//
// We are unable to optimize this case.
// These calls will really hurt.
//
ComputeAddressFunction = ComputeAddressWorstCase;
ComputeOffsetFunction = ComputeOffsetWorstCase;
}
}
}
//
// Compute all the information that will be
// needed later to describe the allocation
// pages.
//
NumberOfElements =
((SBIT16) ((PageSize / ChunkSize) * (ChunkSize / AllocationSize)));
SizeOfChunks = (SBIT16)
((SBIT16) (ChunkSize / AllocationSize));
SizeOfElements = (SBIT16)
((SBIT16) (((NumberOfElements-1) / OverheadBitsPerWord) + 1));
SizeKey = NoSizeKey;
}
else
{ Failure( "Configuration in constructor for BUCKET" ); }
}
/********************************************************************/
/* */
/* Compute the allocation address. */
/* */
/* Compute the allocation address given the page address and */
/* the vector offset in the page. */
/* */
/********************************************************************/
VOID *BUCKET::ComputeAddressBestCase( CHAR *Address,SBIT32 Offset )
{ return ((VOID*) (Address + (Offset << AllocationShift))); }
/********************************************************************/
/* */
/* Compute the allocation address. */
/* */
/* Compute the allocation address given the page address and */
/* the vector offset in the page. */
/* */
/********************************************************************/
VOID *BUCKET::ComputeAddressGoodCase( CHAR *Address,SBIT32 Offset )
{ return ((VOID*) (Address + (Offset * AllocationSize))); }
/********************************************************************/
/* */
/* Compute the allocation address. */
/* */
/* Compute the allocation address given the page address and */
/* the vector offset in the page. */
/* */
/********************************************************************/
VOID *BUCKET::ComputeAddressPoorCase( CHAR *Address,SBIT32 Offset )
{
REGISTER SBIT32 ChunkNumber = (Offset / SizeOfChunks);
REGISTER SBIT32 ChunkOffset = (ChunkNumber * SizeOfChunks);
REGISTER SBIT32 AllocationNumber = (Offset - ChunkOffset);
return
((VOID*)
(
Address
+
(ChunkNumber << ChunkShift)
+
(AllocationNumber * AllocationSize)
)
);
}
/********************************************************************/
/* */
/* Compute the allocation address. */
/* */
/* Compute the allocation address given the page address and */
/* the vector offset in the page. */
/* */
/********************************************************************/
VOID *BUCKET::ComputeAddressWorstCase( CHAR *Address,SBIT32 Offset )
{
REGISTER SBIT32 ChunkNumber = (Offset / SizeOfChunks);
REGISTER SBIT32 ChunkOffset = (ChunkNumber * SizeOfChunks);
REGISTER SBIT32 AllocationNumber = (Offset - ChunkOffset);
return
((VOID*)
(
Address
+
(ChunkNumber * ChunkSize)
+
(AllocationNumber * AllocationSize)
)
);
}
/********************************************************************/
/* */
/* Compute the bit vector offset. */
/* */
/* Compute the bit vector offset given the address of the */
/* memory allocation in the page. */
/* */
/********************************************************************/
SBIT32 BUCKET::ComputeOffsetBestCase( SBIT32 Displacement,BOOLEAN *Found )
{
REGISTER SBIT32 ArrayOffset = (Displacement >> AllocationShift);
(*Found) = (Displacement == (ArrayOffset << AllocationShift));
#ifdef DEBUGGING
if ( ArrayOffset >= NumberOfElements )
{ Failure( "Array offset in ComputeOffsetBestCase" ); }
#endif
return ArrayOffset;
}
/********************************************************************/
/* */
/* Compute the bit vector offset. */
/* */
/* Compute the bit vector offset given the address of the */
/* memory allocation in the page. */
/* */
/********************************************************************/
SBIT32 BUCKET::ComputeOffsetGoodCase( SBIT32 Displacement,BOOLEAN *Found )
{
REGISTER SBIT32 ArrayOffset = (Displacement / AllocationSize);
(*Found) = (Displacement == (ArrayOffset * AllocationSize));
#ifdef DEBUGGING
if ( ArrayOffset >= NumberOfElements )
{ Failure( "Array offset in ComputeOffsetGoodCase" ); }
#endif
return ArrayOffset;
}
/********************************************************************/
/* */
/* Compute the bit vector offset. */
/* */
/* Compute the bit vector offset given the address of the */
/* memory allocation in the page. */
/* */
/********************************************************************/
SBIT32 BUCKET::ComputeOffsetPoorCase( SBIT32 Displacement,BOOLEAN *Found )
{
REGISTER SBIT32 ArrayOffset;
REGISTER SBIT32 ChunkNumber = (Displacement >> ChunkShift);
REGISTER SBIT32 ChunkAddress = (ChunkNumber << ChunkShift);
REGISTER SBIT32 ChunkOffset = (Displacement - ChunkAddress);
REGISTER SBIT32 AllocationNumber = (ChunkOffset / AllocationSize);
ArrayOffset = ((ChunkNumber * SizeOfChunks) + AllocationNumber);
(*Found) =
(
(Displacement)
==
(ChunkAddress + (AllocationNumber * AllocationSize))
);
#ifdef DEBUGGING
if ( ArrayOffset >= NumberOfElements )
{ Failure( "Array offset in ComputeOffsetPoorCase" ); }
#endif
return ArrayOffset;
}
/********************************************************************/
/* */
/* Compute the bit vector offset. */
/* */
/* Compute the bit vector offset given the address of the */
/* memory allocation in the page. */
/* */
/********************************************************************/
SBIT32 BUCKET::ComputeOffsetWorstCase( SBIT32 Displacement,BOOLEAN *Found )
{
REGISTER SBIT32 ArrayOffset;
REGISTER SBIT32 ChunkNumber = (Displacement / ChunkSize);
REGISTER SBIT32 ChunkAddress = (ChunkNumber * ChunkSize);
REGISTER SBIT32 ChunkOffset = (Displacement - ChunkAddress);
REGISTER SBIT32 AllocationNumber = (ChunkOffset / AllocationSize);
ArrayOffset = ((ChunkNumber * SizeOfChunks) + AllocationNumber);
(*Found) =
(
(Displacement)
==
(ChunkAddress + (AllocationNumber * AllocationSize))
);
#ifdef DEBUGGING
if ( ArrayOffset >= NumberOfElements )
{ Failure( "Array offset in ComputeOffsetWorstCase" ); }
#endif
return ArrayOffset;
}
/********************************************************************/
/* */
/* Delete a memory allocation. */
/* */
/* We need to delete a single memory allocation from a bucket. */
/* We do this by passing the request on to the page. */
/* */
/********************************************************************/
BOOLEAN BUCKET::Delete( VOID *Address,PAGE *Page,SBIT32 Version )
{
AUTO SEARCH_PAGE Details;
//
// When we delete an allocation we need to ensure
// the page has not radically changed since we found
// it. Hence, we compare the current page version
// number with the one we found earlier. If all is
// well we get the details relating to the allocation
// and then delete it.
//
return
(
((Page -> GetVersion()) == Version)
&&
(Page -> FindPage( Address,& Details,False ) != NULL)
&&
(Page -> Delete( & Details ))
);
}
/********************************************************************/
/* */
/* Delete a page from the bucket list. */
/* */
/* When a page becomes full it is removed from the bucket list */
/* so it will be no longer inspected when looking for free space. */
/* */
/********************************************************************/
VOID BUCKET::DeleteFromBucketList( PAGE *Page )
{
//
// We keep track of the number of active pages on the
// bucket list. This helps us when we need to scan the
// bucket list for some reason later.
//
if ( (-- ActivePages) >= 0 )
{
//
// Delete the page from the bucket list as it is
// no longer needed. There are two cases when this
// happens. When the page is full and when the page
// is about to be deleted.
//
Page -> DeleteFromBucketList( & BucketList );
//
// Compute the highest address on the first page. We
// use this information to figure out whether to
// recycle an allocation or pass it along for deletion
// in the cache.
//
Page = (PAGE::FirstInBucketList( & BucketList ));
if ( ! Page -> EndOfBucketList() )
{
CurrentPage =
(
((VOID*) (((LONG) Page -> GetAddress()) + (PageSize - 1)))
);
}
else
{ CurrentPage = ((VOID*) HighestAddress); }
}
else
{ Failure( "Active page count in DeleteFromBucketList" ); }
}
/********************************************************************/
/* */
/* Insert a page into the bucket list. */
/* */
/* When a page is created or when it changes from being full */
/* to having at least one free slot it is added to the bucket */
/* list so that it can be used to allocate space. */
/* */
/********************************************************************/
VOID BUCKET::InsertInBucketList( PAGE *Page )
{
//
// We keep track of the number of active pages on the
// bucket list. This helps us when we need to scan the
// bucket list for some reason later.
//
ActivePages ++;
//
// We insert pages into the list in ascending address
// order. This ensures that we always allocate the
// lowest addresses first. This is done to try to keep
// the working set small and compact.
//
if ( ! BucketList.EndOfList() )
{
REGISTER VOID *Address = (Page -> GetAddress());
REGISTER PAGE *Last = (Page -> LastInBucketList( & BucketList ));
//
// We are about to walk the entire page list
// trying to find where to insert this page.
// Lets see if the page needs to be inserted
// at the end of the list. If so have saved
// ourseleves a lot of work and we can exit
// early.
//
if ( Address < (Last -> GetAddress()) )
{
REGISTER PAGE *Current;
//
// Well it looks like we need to walk along
// the entire page list to find the correct
// place to insert this element.
//
for
(
Current = (Page -> FirstInBucketList( & BucketList ));
! Current -> EndOfBucketList();
Current = Current -> NextInBucketList()
)
{
//
// While the current address is lower
// than ours we need to keep on walking.
//
if ( Address < (Current -> GetAddress()) )
{
//
// We have found the spot so insert
// the bucket just before the current
// bucket.
//
Current -> InsertBeforeInBucketList( & BucketList,Page );
break;
}
}
}
else
{
//
// The page has the highest address so insert
// it at the end of the list.
//
Last -> InsertAfterInBucketList( & BucketList,Page );
}
}
else
{ Page -> InsertInBucketList( & BucketList ); }
//
// Compute the highest address on the first page. We can
// use this information to figure out whether to recycle an
// allocation or pass it along for deletion in the cache.
//
Page = (PAGE::FirstInBucketList( & BucketList ));
CurrentPage =
(
((VOID*) (((LONG) Page -> GetAddress()) + (PageSize - 1)))
);
}
/********************************************************************/
/* */
/* Multiple memory deallocations. */
/* */
/* When the delete cache becomes full we complete any pending */
/* delete requests. We also flush the delete cache when if */
/* we need to allocate additional memory unless recycling is */
/* enabled in which case we just steal it directly from the */
/* delete cache. */
/* */
/********************************************************************/
BOOLEAN BUCKET::MultipleDelete
(
ADDRESS_AND_PAGE *Array,
SBIT32 *Deleted,
SBIT32 Size
)
{
AUTO SEARCH_PAGE Details;
REGISTER SBIT32 Count;
//
// Zero the count of deleted items.
//
(*Deleted) = 0;
//
// Delete each element one at a time. We would love to
// delete them all at once but we haven't got a clue where
// they have come from so we have to do it one at a time.
//
for ( Count=0;Count < Size;Count ++ )
{
REGISTER ADDRESS_AND_PAGE *Current = & Array[ Count ];
REGISTER PAGE *Page = Current -> Page;
//
// It may see like a waste of time to batch up all
// the deletions. Why not do them as they arrive.
// There are a number of reasons. The deletes can
// be recycled, batchs of deletes is faster than
// single deletes (due to cache effects) and so on.
//
if
(
(Current -> Version == Page -> GetVersion())
&&
(Page -> FindPage( Current -> Address,& Details,False ) != NULL)
&&
(Page -> Delete( & Details ))
)
{ (*Deleted) ++; }
}
return ((*Deleted) == Size);
}
/********************************************************************/
/* */
/* Multiple memory allocations. */
/* */
/* We need to make a multiple memory allocation from this */
/* bucket so walk the bucket list allocating any available */
/* space and return it to the caller. */
/* */
/********************************************************************/
BOOLEAN BUCKET::MultipleNew
(
SBIT32 *Actual,
VOID *Array[],
SBIT32 Requested
)
{
//
// Zero the count of allocated elements.
//
(*Actual) = 0;
//
// We walk the sorted list of pages with available
// allocations searching for elements to allocate.
//
do
{
REGISTER PAGE *Page;
REGISTER PAGE *NextPage;
//
// Walk the bucket list looking for any available
// free space.
//
for
(
Page = (PAGE::FirstInBucketList( & BucketList ));
! Page -> EndOfBucketList();
Page = NextPage
)
{
REGISTER SBIT32 ActualSize = (Page -> GetPageSize());
//
// Lets find the next page now as the current
// bucket may be removed from the bucket list
// by the following allocation call.
//
NextPage = Page -> NextInBucketList();
//
// We allow the page size to be dynamically
// modified to support a variety of wierd
// data layouts for BBT. If the current page
// is not the standard size then skip it.
//
if ( (ActualSize == NoSize) || (ActualSize == PageSize) )
{
//
// We try allocate all the space we need
// from each page in the bucket list. If
// the page has enough space we can exit
// early if not we go round the loop and
// try the next page.
//
if ( Page -> MultipleNew( Actual,Array,Requested ) )
{ return True; }
}
}
}
while
(
NewPage -> CreatePage( (CACHE*) this )
!=
((PAGE*) AllocationFailure)
);
//
// We see if we managed to allocate all the elements
// we wanted. If so we are happy and we can get out
// of here.
//
if ( (*Actual) < Requested )
{
//
// We see if we managed to allocate any elements
// at all. If not we fail the request.
//
if ( (*Actual) > 0 )
{
REGISTER SBIT32 Count;
REGISTER SBIT32 Delta = ((Requested) - (*Actual));
//
// We are very naughty when we allocate multiple
// elements in that we put them in the array in
// reverse order. The logic is that this is just
// what we want when we allocate out of the cache.
// However, if we are unable to allocate all the
// elements we needed then we have to move the
// pointers down to the base of the array.
//
for ( Count=0;Count < (*Actual);Count ++ )
{ Array[ Count ] = Array[ (Count + Delta) ]; }
}
else
{ return False; }
}
return True;
}
/********************************************************************/
/* */
/* Memory allocation. */
/* */
/* We need to make a new memory allocation from this bucket */
/* so search the page list of available space and return a */
/* free element. */
/* */
/********************************************************************/
VOID *BUCKET::New( BOOLEAN SubDivided,SBIT32 NewSize )
{
do
{
REGISTER PAGE *Page;
//
// Walk the bucket list looking for any available
// free space.
//
for
(
Page = (PAGE::FirstInBucketList( & BucketList ));
! Page -> EndOfBucketList();
Page = Page -> NextInBucketList()
)
{
REGISTER SBIT32 ActualSize = (Page -> GetPageSize());
//
// We allow the page size to be dynamically
// modified to support a variety of wierd
// data layouts for BBT. If the current page
// is not the correct size then skip it.
//
if
(
(ActualSize == NoSize)
||
(ActualSize == ((NewSize == NoSize) ? PageSize : NewSize))
)
{
//
// We know that any page that appears in
// the bucket list will have at least one
// free element available. So if we find
// that the bucket list a suitable page
// then we know that we can allocate something.
//
return (Page -> New( SubDivided ));
}
}
}
while
(
NewPage -> CreatePage( ((CACHE*) this),NewSize )
!=
((PAGE*) AllocationFailure)
);
//
// We were unable to find anything we could allocate
// so fail the request.
//
return ((VOID*) AllocationFailure);
}
/********************************************************************/
/* */
/* Release free space. */
/* */
/* We sometimes do not release free space from a bucket as */
/* returning it to the operating system and getting it again */
/* later is very expensive. Here we flush any free space we */
/* have aquired over the user supplied limit. */
/* */
/********************************************************************/
VOID BUCKET::ReleaseSpace( SBIT32 MaxActivePages )
{
REGISTER SBIT32 Current = ActivePages;
//
// We only bother to try to trim the number of
// active pages if we are over the limit.
//
if ( Current > MaxActivePages )
{
REGISTER PAGE *NextPage;
REGISTER PAGE *Page;
//
// Walk the backwards along the bucket list
// and delete the highest addressed free pages
// if we are over the limit.
//
for
(
Page = (PAGE::LastInBucketList( & BucketList ));
(Current > MaxActivePages)
&&
(! Page -> EndOfBucketList());
Page = NextPage
)
{
//
// We are walking backwards down the bucket
// list looking for empty pages to delete.
// However, if we find a page we can remove
// it will be automatically removed from the
// list so we need to get the next pointer
// before this happens.
//
NextPage = Page -> PreviousInBucketList();
//
// We can only release a page if it is empty
// if not we must skip it.
//
if ( Page -> Empty() )
{
Current --;
DeletePage( Page );
}
}
}
}
/********************************************************************/
/* */
/* Update the bucket information. */
/* */
/* When we create the bucket there is some information that */
/* is not available. Here we update the bucket to make sure */
/* it has all the data we need. */
/* */
/********************************************************************/
VOID BUCKET::UpdateBucket
(
FIND *NewFind,
HEAP *NewHeap,
NEW_PAGE *NewPages,
CACHE *NewParentCache
)
{
REGISTER SBIT16 NewSizeKey = (NewPages -> FindSizeKey( NumberOfElements ));
//
// We compute and verify the size key to make sure
// it is suitable for all the pages that we will
// create after the heap constructor is completed.
//
if ( NewSizeKey != NoSizeKey )
{
//
// Update the size key and the connections.
//
SizeKey = NewSizeKey;
UpdateConnections
(
NewFind,
NewHeap,
NewPages,
NewParentCache
);
}
else
{ Failure( "Bucket can't get a size key in UpdateBucket" ); }
}
/********************************************************************/
/* */
/* Class destructor. */
/* */
/* Destory the allocation bucket. */
/* */
/********************************************************************/
BUCKET::~BUCKET( VOID )
{ /* void */ }