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
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 */ }
|