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.
 
 
 
 
 
 

787 lines
25 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 "Heap.hpp"
#include "NewPage.hpp"
#include "Page.hpp"
/********************************************************************/
/* */
/* Constants local to the class. */
/* */
/* The constants supplied here allow the allocation bit vector */
/* to be rapidly searched for free storage. */
/* */
/********************************************************************/
CONST BIT32 AllocatedMask = 0x2;
CONST BIT32 FullSearchMask = 0xaaaaaaaa;
CONST BIT32 FullWordShift = (MaxBitsPerWord - OverheadBits);
CONST BIT32 SubDividedMask = 0x1;
CONST BIT32 WordSearchMask = (AllocatedMask << FullWordShift);
/********************************************************************/
/* */
/* Class constructor. */
/* */
/* */
/* All page descriptions are actually created and deleted by */
/* a separate class called 'NEW_PAGE'. Nonetheless, as a */
/* step the appropriate constructors and destructors are */
/* invoked to support the standard C++ programming methodology. */
/* */
/********************************************************************/
PAGE::PAGE
(
VOID *NewAddress,
CACHE *NewCache,
SBIT32 NewPageSize,
CACHE *NewParentPage,
SBIT32 NewVersion
)
{
REGISTER SBIT32 Count;
REGISTER SBIT16 NumberOfElements = (NewCache -> GetNumberOfElements());
REGISTER SBIT16 SizeOfElements = (NewCache -> GetSizeOfElements());
//
// Create a page description.
//
Address = (CHAR*) NewAddress;
PageSize = NewPageSize;
Version = NewVersion;
Allocated = 0;
Available = NumberOfElements;
FirstFree = 0;
//
// Set up the pointers to related classes.
//
Cache = NewCache;
ParentPage = NewParentPage;
//
// Zero the bit vector.
//
for ( Count=0;Count < SizeOfElements;Count ++ )
{ Vector[ Count ] = 0; }
}
/********************************************************************/
/* */
/* Compute the actual size. */
/* */
/* Almost all allocation sizes are derived from the associated */
/* caches. However, there are a few special pages that contain */
/* a single allocation of some weird size. */
/* */
/********************************************************************/
SBIT32 PAGE::ActualSize( VOID )
{
return
(
(ParentPage == ((CACHE*) GlobalRoot))
? PageSize
: (Cache -> GetAllocationSize())
);
}
/********************************************************************/
/* */
/* Delete an allocation. */
/* */
/* We need to delete the memory allocation described by the */
/* parameters. However, as we are of an untrusting nature */
/* we carefully check the request to ensure it is valid. */
/* */
/********************************************************************/
BOOLEAN PAGE::Delete( SEARCH_PAGE *Details )
{
//
// We know that no one would deallocate memory
// they had not previously allocated (yea - right).
// So here we check for this case.
//
if ( (*Details -> VectorWord) & Details -> AllocationMask )
{
//
// Nasty: it is possible for the user to give us an
// address that points to the middle of the element
// to be freed instead of the beginning. This is no
// problem for us but we have to ponder whether the
// caller knew what they were doing. If this is the
// case we fail the request.
//
if ( Details -> Found )
{
//
// We have found that the element is allocated
// (as one might expect) so lets deallocate it
// and update the various counters.
//
(*Details -> VectorWord) &=
~(Details -> AllocationMask | Details ->SubDivisionMask);
//
// We may need to push back the pointer to the
// first free element. This will ensure that
// we can quickly locate the freed element for
// later so we can reuse it.
//
if ( FirstFree > Details -> VectorOffset )
{ FirstFree = ((SBIT16) Details -> VectorOffset); }
//
// If the page was full and now has an empty
// slot then add it to the bucket list so that
// the free space can be found.
//
if ( Full() )
{ Cache -> InsertInBucketList( this ); }
//
// Update the allocation information.
//
Allocated --;
Available ++;
//
// If the page is now empty then delete
// the page to conserve space.
//
if ( Empty() )
{
//
// We immediately delete empty pages
// except at the top level where it is
// under user control.
//
if ( ! Cache -> TopCache() )
{ Cache -> DeletePage( this ); }
else
{
REGISTER SBIT32 MaxFreePages =
(Cache -> GetHeap() -> GetMaxFreePages());
((BUCKET*) Cache) -> ReleaseSpace( MaxFreePages );
}
}
return True;
}
}
return False;
}
/********************************************************************/
/* */
/* Delete all simple allocations. */
/* */
/* Although this routine may seem insignificant its effects are */
/* dramatic. When called this function deletes all the none */
/* sub-allocated elements and updates the control values. */
/* */
/********************************************************************/
VOID PAGE::DeleteAll( VOID )
{
REGISTER BOOLEAN PageFull = Full();
//
// Simply reset the allocation counts.
//
Allocated = 0;
Available = (Cache -> GetNumberOfElements());
FirstFree = 0;
//
// We know that if this cache does not have any
// child allocations that it is safe to simply
// zero the bit vector. If not we have to do it
// the long way.
//
if ( Cache -> GetNumberOfChildren() > 0 )
{
REGISTER SBIT32 Count;
REGISTER SBIT16 SizeOfElements = (Cache -> GetSizeOfElements());
//
// We examine each word of the bit vector
// and delete all the elements that are
// not sub-divided into smaller sizes.
//
for ( Count=0;Count < SizeOfElements;Count ++ )
{
REGISTER BIT32 *Word = & Vector[ Count ];
REGISTER BIT32 AllAllocations = ((*Word) & FullSearchMask);
REGISTER BIT32 AllSubDivided = ((*Word) & (AllAllocations >> 1));
REGISTER BIT32 FinalMask = (AllSubDivided | (AllSubDivided << 1));
//
// Delete all normal allocations.
//
(*Word) &= FinalMask;
//
// If the final mask is not zero then
// we still have some allocations active.
// We need to count these and update the
// control information.
//
if ( FinalMask != 0 )
{
REGISTER SBIT32 Total = 0;
//
// Count the allocations.
//
for ( /* void */;FinalMask != 0;FinalMask >>= OverheadBits )
{ Total += (FinalMask & 1); }
//
// Update the control information.
//
Allocated = ((SBIT16) (Allocated + Total));
Available = ((SBIT16) (Available - Total));
}
}
}
else
{
REGISTER SBIT32 Count;
REGISTER SBIT16 SizeOfElements = (Cache -> GetSizeOfElements());
//
// Zero the bit vector.
//
for ( Count=0;Count < SizeOfElements;Count ++ )
{ Vector[ Count ] = 0; }
}
//
// If the page was full and now has empty
// slots then add it to the bucket list so
// that the free space can be found.
//
if ( (PageFull) && (! Full()) )
{ Cache -> InsertInBucketList( this ); }
}
/********************************************************************/
/* */
/* Find an allocation page. */
/* */
/* When we receive a request to delete an allocation we don't */
/* have a clue about where to find it. All we have is a hash */
/* table (see 'FIND') of allocated pages. So we mask off the */
/* low order bits of the address and try to find the top level */
/* external allocation. If this works we see if the area we */
/* are looking at has been sub-divided and if so we try the */
/* same trick again until we get the the origibal allocation */
/* page. */
/* */
/********************************************************************/
PAGE *PAGE::FindPage( VOID *Memory,SEARCH_PAGE *Details,BOOLEAN Recursive )
{
//
// We navigate through the pages trying to find
// the allocation page associated with the address.
// If we find a page that has no children then
// we can assume we have arrived and exit early
// unless the caller has requested all the realated
// details.
//
if ( (Cache -> GetNumberOfChildren() > 0) || (Details != NULL) )
{
AUTO BOOLEAN Found;
REGISTER SBIT32 Displacement =
((SBIT32) (((CHAR*) Memory) - Address));
REGISTER SBIT32 ArrayOffset =
(Cache -> ComputeOffset( Displacement,& Found ));
REGISTER SBIT32 VectorOffset =
(ArrayOffset / OverheadBitsPerWord);
REGISTER SBIT32 WordOffset =
(ArrayOffset - (VectorOffset * OverheadBitsPerWord));
REGISTER SBIT32 WordShift =
(((OverheadBitsPerWord-1) - WordOffset) * OverheadBits);
REGISTER BIT32 AllocationMask =
(AllocatedMask << WordShift);
REGISTER BIT32 SubDivisionMask =
(SubDividedMask << WordShift);
REGISTER BIT32 *VectorWord =
& Vector[ VectorOffset ];
//
// We will recursively search and find the target
// address if requested otherwise we will just
// return the details of the next level in the tree.
//
if
(
(Recursive)
&&
((*VectorWord) & AllocationMask)
&&
((*VectorWord) & SubDivisionMask)
)
{
REGISTER PAGE *Page = (Cache -> FindChildPage( Memory ));
//
// We have found the element and checked it.
// So lets pass this request on to the
// child page. However, there is a slight
// chance of a race condition here. It
// might be that the original page was
// deleted and a new page is currently
// being created. If this is the case
// then we will not find the page in the
// hash table so we just exit and fail the
// call.
//
if ( Page != ((PAGE*) NULL) )
{ return (Page -> FindPage( Memory,Details,Recursive )); }
else
{ return NULL; }
}
//
// We see if the caller is interested in the
// details relating to this address at the
// current level in the tree.
//
if ( Details != NULL )
{
//
// We have computed the details relating
// to this address at the current level
// in the tree so load them into the
// caller supplied structure.
//
Details -> Address = Memory;
Details -> Cache = Cache;
Details -> Found = Found;
Details -> Page = this;
Details -> AllocationMask = AllocationMask;
Details -> SubDivisionMask = SubDivisionMask;
Details -> VectorWord = VectorWord;
Details -> ArrayOffset = ArrayOffset;
Details -> VectorOffset = VectorOffset;
Details -> WordShift = WordShift;
}
}
return this;
}
/********************************************************************/
/* */
/* Multiple memory allocations. */
/* */
/* Allocate available memeory elements from a page. This is */
/* done by scanning the bit vector looking for unallocated */
/* slots. */
/* */
/********************************************************************/
BOOLEAN PAGE::MultipleNew( SBIT32 *Actual,VOID *Array[],SBIT32 Requested )
{
//
// We begin by making sure that there is at least
// one element to allocate and that we need to
// allocated at least one element.
//
if ( (! Full()) && ((*Actual) < Requested) )
{
REGISTER SBIT16 SizeOfElements = (Cache -> GetSizeOfElements());
//
// Search the bit vector from low addresses to
// high addresses looking for a free slots.
// We keep a pointer to the first word with
// a free element in 'FirstFree'. Sometimes
// the current word may be fully allocated so
// we might need to scan. However, there can
// never be any free memory before this point
// in the bit vector.
//
for ( /* void */;FirstFree < SizeOfElements;FirstFree ++ )
{
REGISTER SBIT32 ArrayOffset = (FirstFree * OverheadBitsPerWord);
REGISTER BIT32 AvailableMask = WordSearchMask;
REGISTER BIT32 *VectorWord = & Vector[ FirstFree ];
REGISTER SBIT32 WordOffset = 0;
//
// We scan the bit vector word at a time
// looking for any free allocation slots.
//
while ( ((*VectorWord) & FullSearchMask) != FullSearchMask )
{
REGISTER BIT32 Value = (*VectorWord);
//
// We know there is an at least one empty
// slot availabale in the current word but
// don't know which one We search for the
// slot with the lowest address and stop
// when we find it.
//
for
(
/* void */;
(AvailableMask & Value) != 0;
AvailableMask >>= OverheadBits, WordOffset ++
);
//
// We should never fail to find a free
// allocation slot so if we do then the
// heap must be corrupt.
//
if ( WordOffset < OverheadBitsPerWord )
{
REGISTER SBIT32 VectorOffset = (ArrayOffset + WordOffset);
//
// We need to ensure that the element
// we have chosen if not outside the
// valid range for this page.
//
if ( VectorOffset < (Cache -> GetNumberOfElements()) )
{
//
// Update the allocation information.
//
Allocated ++;
Available --;
//
// Turn on the bits indicating that this
// element is in use.
//
(*VectorWord) |= AvailableMask;
//
// If the page is full we remove it
// from the bucket list so we will no
// longer look at it when we are
// trying to find free space.
//
if ( Full() )
{ Cache -> DeleteFromBucketList( this ); }
//
// Add the element to the allocation array
// so it can be returned to the caller.
//
Array[ (Requested - ((*Actual) ++) - 1) ] =
(
Cache -> ComputeAddress
(
Address,
VectorOffset
)
);
//
// When we have got what we need we exit.
//
if ( ((*Actual) >= Requested) )
{ return True; }
}
else
{ break; }
}
else
{ Failure( "Bit vector is corrupt in MultipleNew" ); }
}
}
}
return ((*Actual) >= Requested);
}
/********************************************************************/
/* */
/* A single memory allocation. */
/* */
/* Allocate an available memeory element from the page. This */
/* is done by scanning the bit vector looking for unallocated */
/* slots. */
/* */
/********************************************************************/
VOID *PAGE::New( BOOLEAN SubDivided )
{
//
// We begin by making sure that there is at least
// one element to allocate.
//
if ( ! Full() )
{
REGISTER SBIT16 SizeOfElements = (Cache -> GetSizeOfElements());
//
// Search the bit vector from low addresses to
// high addresses looking for a free slot.
// We keep a pointer to the first word with
// a free element in 'FirstFree'. Sometimes
// the current word may be fully allocated so
// we might need to scan. However, there can
// never be any free memory before this point
// in the bit vector.
//
for ( /* void */;FirstFree < SizeOfElements;FirstFree ++ )
{
REGISTER BIT32 *VectorWord = & Vector[ FirstFree ];
//
// We scan the bit vector word at a time
// looking for any free allocation slots.
//
if ( ((*VectorWord) & FullSearchMask) != FullSearchMask )
{
REGISTER BIT32 AvailableMask = WordSearchMask;
REGISTER BIT32 Value = (*VectorWord);
REGISTER SBIT32 WordOffset = 0;
//
// We know there is an at least one empty
// slot availabale in the current word but
// don't know which one We search for the
// slot with the lowest address and stop
// when we find it.
//
for
(
/* void */;
(AvailableMask & Value) != 0;
AvailableMask >>= OverheadBits, WordOffset ++
);
//
// We should never fail to find a free
// allocation slot so if we do then the
// heap must be corrupt.
//
if ( WordOffset < OverheadBitsPerWord )
{
REGISTER SBIT32 VectorOffset =
((FirstFree * OverheadBitsPerWord) + WordOffset);
//
// We need to ensure that the element
// we have chosen if not outside the
// valid range for this page.
//
if ( VectorOffset < (Cache -> GetNumberOfElements()) )
{
//
// Update the allocation information.
//
Allocated ++;
Available --;
//
// Turn on the bit indicating that this
// element is in use. If the allocation
// is to be sub-divided then trun on this
// bit as well.
//
(*VectorWord) |=
(
AvailableMask
|
(SubDivided ? (AvailableMask >> 1) : 0)
);
//
// If the page is full we remove it
// from the bucket list so we will no
// longer look at it when we are
// trying to find free space.
//
if ( Full() )
{ Cache -> DeleteFromBucketList( this ); }
//
// Return the address of the allocated
// memory to the caller.
//
return
(
Cache -> ComputeAddress
(
Address,
VectorOffset
)
);
}
}
else
{ Failure( "Bit vector is corrupt in New" ); }
}
}
#ifdef DEBUGGING
if ( ! Full() )
{ Failure( "Available count corrupt in New" ); }
#endif
}
return ((VOID*) AllocationFailure);
}
/********************************************************************/
/* */
/* Walk the heap. */
/* */
/* We have been asked to walk the heap. It is hard to know */
/* why 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 PAGE::Walk( SEARCH_PAGE *Details )
{
REGISTER BOOLEAN FreshPage = False;
//
// We have been handed the details of an allocation.
// We need to walk along this allocation and find
// the next non-subdivided allocation.
do
{
//
// We need to setup the heap walk if the address
// is null so we skip the heap walk code.
//
if ( Details -> Address != NULL )
{
REGISTER SBIT32 Count;
REGISTER SBIT32 End = Details -> Cache -> GetNumberOfElements();
REGISTER SBIT32 Start = Details -> ArrayOffset;
REGISTER PAGE *Page = Details -> Page;
//
// Walk the current page looking for a suitable
// memory allocation to report to the user. When
// we reach the end of the page we need to get
// another page to walk.
//
for
(
Count = ((FreshPage) ? 0 : 1);
(Start + Count) < End;
Count ++
)
{
//
// Compute the new address.
//
Details -> Address =
(
Page -> Cache -> ComputeAddress
(
Page -> Address,
(Start + Count)
)
);
//
// Compute the new allocation details.
//
Page -> FindPage
(
Details -> Address,
Details,
False
);
//
// We skip all sub-divided allocations as they
// will get reported elsewhere.
//
if (! ((*Details -> VectorWord) & Details -> SubDivisionMask) )
{ return True; }
}
}
//
// Update the flag to show that we have
// had to go and get a new page.
//
FreshPage = True;
}
while ( Details -> Cache -> Walk( Details ) );
return False;
}
/********************************************************************/
/* */
/* Class destructor. */
/* */
/* Destory the current page structure. */
/* */
/********************************************************************/
PAGE::~PAGE( VOID )
{
#ifdef DEBUGGING
//
// Destroy the page structure.
//
Address = NULL;
PageSize = 0;
ParentPage = NULL;
Allocated = 0;
Available = 0;
FirstFree = 0;
#endif
//
// We update the version number whenever a page is created
// or destroyed. We use the version number to ensure that
// a page has not been deleteed and/or recreated between
// releasing one lock and claiming a another other lock.
//
Version ++;
}