378 lines
18 KiB
C
378 lines
18 KiB
C
|
//
|
||
|
// rvHeap.h - Heap object (replacing the idHeap class)
|
||
|
// Date: 12/13/04
|
||
|
// Created by: Dwight Luetscher
|
||
|
//
|
||
|
|
||
|
#ifndef __RV_HEAP_H__
|
||
|
#define __RV_HEAP_H__
|
||
|
|
||
|
class rvHeapArena;
|
||
|
struct rvMemoryBlock_s;
|
||
|
struct rvFreeMemoryBlock_s;
|
||
|
|
||
|
//#define _DETECT_BLOCK_MERGE_PROBLEMS
|
||
|
|
||
|
// Define some flags used in describing various properties of allocations
|
||
|
// coming from this heap (these flags are passed into rvHeap::Init()).
|
||
|
static const uint rvHeapFlagDefault = 0;
|
||
|
static const uint rvHeapFlagWriteCombine = 0x0001; // flag specified if the memory allocated from this heap is intended to have a write-combine cache policy
|
||
|
static const uint rvHeapFlagNoCache = 0x0002; // flag specified if the memory allocated from this heap is intended to have a no cache policy
|
||
|
|
||
|
// Define some limits used by each heap
|
||
|
static const uint NUM_MEMORY_BLOCK_SIZE_BITS = 27;
|
||
|
static const uint NUM_TAG_BITS = 5;
|
||
|
|
||
|
static const uint MAX_SINGLE_ALLOCATION_SIZE = ((1 << (NUM_MEMORY_BLOCK_SIZE_BITS+2)) - 1); // maximum number of bytes that can be allocated in a single allocation
|
||
|
static const uint NUM_SMALL_BLOCK_TABLE_ENTRIES = 2048; // number of entries within each heap's small free memory block table
|
||
|
|
||
|
static const uint SMALL_BLOCK_ALIGN_SIZE_SHIFT = 2; // left shift of the size that all allocations are rounded to
|
||
|
static const uint SMALL_BLOCK_ALIGN_SIZE = 1 << SMALL_BLOCK_ALIGN_SIZE_SHIFT; // size that all allocations are rounded to
|
||
|
static const uint SMALL_BLOCK_ALIGN_SIZE_MASK = SMALL_BLOCK_ALIGN_SIZE-1; // mask of the bits used for the size that all allocations are rounded to
|
||
|
static const uint MAX_SMALL_BLOCK_SIZE = (NUM_SMALL_BLOCK_TABLE_ENTRIES << SMALL_BLOCK_ALIGN_SIZE_SHIFT) - 1; // maximum size of an allocation to be considered a "small block" allocation
|
||
|
|
||
|
static const uint PAGE_SIZE_SHIFT = 16; // left shift of the page size used by this heap object
|
||
|
static const uint PAGE_SIZE = 1 << PAGE_SIZE_SHIFT; // page size used by this heap object
|
||
|
static const uint PAGE_SIZE_MASK = PAGE_SIZE - 1;
|
||
|
|
||
|
static const uint MAX_SMALL_PAGE_RANGE = 128; // maximum number of contiguous pages for a single range to be considered "small"
|
||
|
static const uint MIN_LARGE_PAGE_RANGE = MAX_SMALL_PAGE_RANGE+1; // minimum number of contiguous pages for a single range to be considered "large"
|
||
|
|
||
|
// Define the freePageRange_t structure used by this rvHeap used to maintain linked-lists of contiguous page ranges below LARGE_PAGE_RANGE
|
||
|
typedef struct freePageRange_s
|
||
|
{
|
||
|
freePageRange_s* m_nextFree; // next range of free pages in a linked-list
|
||
|
}
|
||
|
freePageRange_t;
|
||
|
|
||
|
// Define the freePageRange_t structure used by this rvHeap used to maintain a linked-list of contiguous page ranges above LARGE_PAGE_RANGE
|
||
|
typedef struct freeLargePageRange_s
|
||
|
{
|
||
|
freeLargePageRange_s* m_nextFree; // next large range of free pages in a linked-list
|
||
|
uint m_firstPageOffset; // offset of the first page in this range
|
||
|
uint m_numContiguousPages; // count of the number of contiguous pages in this range
|
||
|
}
|
||
|
freeLargePageRange_t;
|
||
|
|
||
|
class rvHeap
|
||
|
{
|
||
|
public:
|
||
|
rvHeap( ); // constructor
|
||
|
~rvHeap( ); // destructor
|
||
|
|
||
|
void Init( rvHeapArena &heapArena, uint maxHeapSizeBytes, uint flags = rvHeapFlagDefault ); // initializes this heap for use within the given arena, and with the given size limit that it can grow to
|
||
|
void Shutdown( ); // releases this heap from use
|
||
|
|
||
|
ID_INLINE bool IsInitialized( ) const; // returns true if this heap is currently initialized, and not shutdown
|
||
|
|
||
|
ID_INLINE void *Allocate( uint sizeBytes, int debugTag = 0 ); // allocate memory
|
||
|
ID_INLINE void *Allocate16( uint sizeBytes, int debugTag = 0 ); // allocate memory aligned on a 16-byte boundary
|
||
|
void Free( void *allocation ); // free memory
|
||
|
|
||
|
int Msize( void *allocation ); // returns the size, in bytes, of the allocation at the given address (including header, alignment bytes, etc).
|
||
|
|
||
|
void FreeAll( ); // frees all the allocations from this heap (and decommits all pages)
|
||
|
|
||
|
ID_INLINE bool DoesAllocBelong( void *allocBase ); // returns true if the given address was allocated from this heap, false otherwise
|
||
|
|
||
|
ID_INLINE rvHeapArena *GetArena( ); // returns the arena this heap is associated with
|
||
|
|
||
|
ID_INLINE void PushCurrent( ); // makes this heap the new top of stack for its associated arena, thus making it current
|
||
|
ID_INLINE void PopCurrent( ); // pops this heap, or any other heap, from the top of stack of this heap's associated arena, thus making its predecessor current
|
||
|
|
||
|
ID_INLINE uint GetSize( ) const; // returns the current size that this heap has grown to, in bytes
|
||
|
ID_INLINE uint GetMaxSize( ) const; // returns the maximum size that this heap can grow to, in bytes
|
||
|
|
||
|
ID_INLINE uint GetCommittedSize( ) const; // returns the number of physical bytes this heap is currently using (total bytes of all committed pages)
|
||
|
ID_INLINE uint GetBytesAllocated( ) const; // returns the number of bytes currently allocated from this heap (actual memory footprint of all active allocations including internal headers and alignment bytes)
|
||
|
ID_INLINE uint GetBytesRequested( ) const;
|
||
|
ID_INLINE int GetNumAllocations( ) const; // returns the number of allocations made from this heap that are still active (have not been freed)
|
||
|
|
||
|
ID_INLINE uint GetBytesAllocatedByTag( Mem_Alloc_Types_t allocType ) const; // returns the number of bytes currently allocated from this heap with the given allocation tag (actual memory footprint of all active allocations including internal headers and alignment bytes)
|
||
|
ID_INLINE uint GetPeekBytesAllocatedByTag( Mem_Alloc_Types_t allocType ) const; // returns the peek number of bytes allocated from this heap with the given allocation tag (actual memory footprint of all active allocations including internal headers and alignment bytes)
|
||
|
ID_INLINE int GetNumAllocationsByTag( Mem_Alloc_Types_t allocType ) const; // returns the current number of allocation from this heap with the given allocation tag
|
||
|
|
||
|
ID_INLINE bool IsWriteCombine( ) const; // returns true if memory allocated from this heap has a write-combine cache policy, false otherwise
|
||
|
ID_INLINE bool IsNoCache( ) const; // returns true if memory allocated from this heap has a no-cache policy, false otherwise
|
||
|
|
||
|
int SmallBlockCount() const { return NUM_SMALL_BLOCK_TABLE_ENTRIES; }
|
||
|
int GetSmallBlockFreeCount( int block ) const;
|
||
|
dword GetSmallBlockFreeSize( int block ) const;
|
||
|
|
||
|
int GetLargeBlockFreeCount() const;
|
||
|
dword GetLargeBlockFreeSize() const;
|
||
|
|
||
|
int GetBlockFreeCount( rvFreeMemoryBlock_s* currentBlock ) const;
|
||
|
dword GetBlockFreeSize( rvFreeMemoryBlock_s* currentBlock ) const;
|
||
|
|
||
|
void SetName(const char* name);
|
||
|
const char* GetName(void) const;
|
||
|
void SetDebugID(byte debugID) {mDebugID=debugID;}
|
||
|
byte DebugID(void) const {return mDebugID;}
|
||
|
|
||
|
protected:
|
||
|
rvHeapArena *m_arena; // arena associated with this heap
|
||
|
rvHeap *m_next; // next heap belonging to the same arena
|
||
|
CRITICAL_SECTION m_criticalSection; // critical section associated with this heap
|
||
|
|
||
|
byte *m_baseAddress; // base address of this heap (virtual)
|
||
|
byte *m_zeroLengthAllocPage; // special page for zero-length allocations (remains uncommitted)
|
||
|
byte *m_heapStorageStart; // address of the start of this heap's storage (just past page table)
|
||
|
|
||
|
rvFreeMemoryBlock_s *m_smallFreeBlocks[NUM_SMALL_BLOCK_TABLE_ENTRIES]; // table used to manage linked-lists of small free memory blocks
|
||
|
rvFreeMemoryBlock_s *m_largeFreeBlocks; // linked-list of all of the large free memory blocks
|
||
|
|
||
|
dword *m_pageCommitStateTable; // a very long bit mask (1-bit per page) that describes whether the page has been committed or not to physical memory
|
||
|
freePageRange_t *m_smallFreePageRangeTable; // array of structures that each correspond to a particular page - the structures representing the pages at the start of a small range are added to lists within the m_smallFreePageRangeLists[] array below
|
||
|
|
||
|
freePageRange_t *m_smallFreePageRangeLists[MAX_SMALL_PAGE_RANGE]; // array where each entry is a linked-list of structures that individually describe a contiguous range of pages (where the range count matches the index into this array plus 1)
|
||
|
freeLargePageRange_t *m_largeFreePageRangeList; // linked-list of structures that individually describe a large contiguous range of pages
|
||
|
|
||
|
// the following two data members are just used to manage the storage of the freeLargePageRange_t structures
|
||
|
freeLargePageRange_t *m_largeFreeRangeReuseList; // linked-list of freeLargePageRange_t that are currently not part of the m_largeFreePageRangeList linked-list and are available for use
|
||
|
freeLargePageRange_t *m_largeFreeRangeStorage; // array that acts as a store of the freeLargePageRange_t structures
|
||
|
uint m_largeFreeRangeStorageSize; // number of entries in the m_largeFreeRangeStorage[] array
|
||
|
uint m_pageCommitStateArrayByteSize; // size of the m_pageCommitStateTable[] array in bytes
|
||
|
uint m_smallFreePageRangeByteSize; // size of the m_smallFreePageRangeTable[] array in bytes
|
||
|
|
||
|
uint m_numPages; // number of pages that can span this heap entirely
|
||
|
|
||
|
uint m_heapRangeBytes; // the number of bytes in the virtual address space of this heap (including zero-length allocation page)
|
||
|
|
||
|
uint m_committedSizeBytes; // the number of physical bytes this heap is currently using (total bytes of all committed pages)
|
||
|
uint m_maxHeapSizeBytes; // the maximum size that this heap can grow to, in bytes (allocatable space)
|
||
|
|
||
|
uint m_numBytesRequested; // the number of bytes currently requested for allocation from this heap (memory footprint without the overhaed of internal headers and alignment bytes)
|
||
|
uint m_numBytesAllocated; // the number of bytes currently allocated from this heap (actual memory footprint of all active allocations including internal headers and alignment bytes)
|
||
|
|
||
|
uint m_allocatedBytesByTag[MA_MAX]; // the number of bytes allocated from this heap - organized by usage tag
|
||
|
uint m_peekAllocatedBytesByTag[MA_MAX]; // the peek number of bytes allocated from this heap - organized by usage tag
|
||
|
int m_numAllocationsByTag[MA_MAX]; // the number of allocations - organized by usage tag
|
||
|
|
||
|
int m_numAllocations; // the number of allocations made from this heap that are still active (have not been freed)
|
||
|
int m_numZeroLengthAllocations; // the number of zero length allocations made from this heap
|
||
|
|
||
|
uint m_largestFreeSmallBlockOffset; // small block table offset of the largest free small block available (block that is part of m_smallFreeBlocks[] table)
|
||
|
|
||
|
uint m_flags; // flags that describe various properties of this heap
|
||
|
|
||
|
byte mDebugID; // ID that identifies heap. Needed to absolutely identify a heap.
|
||
|
char m_name[20]; // for debug display
|
||
|
|
||
|
void ResetValues( ); // resets the data members to their pre-initialized state
|
||
|
|
||
|
void BuildLargeFreeRangeReuseList( ); // builds the m_largeFreeRangeReuseList linked-list from the m_largeFreeRangeStorage[] array.
|
||
|
|
||
|
byte *AllocateMemory( uint sizeBytes, int debugTag, bool align16Flag ); // performs memory allocation for the given number of bytes from this heap
|
||
|
|
||
|
rvMemoryBlock_s *AllocateMemorySmallBlock( uint sizeBytes ); // perform a small block allocation - try to use an existing free block pointed to by the table.
|
||
|
rvMemoryBlock_s *AllocateBlockFromTable( uint smallBlockTableOffset, uint sizeBytes ); // allocates the a small memory block from the free block table and partitions it, if necessary, into an allocated chunk and a free chunk (which remains in the m_smallFreeBlocks[] table).
|
||
|
rvMemoryBlock_s *AllocateMemoryLargeBlock( uint sizeBytes ); // perform a large block allocation - try to use an existing free block from within the large free block linked-list.
|
||
|
|
||
|
void TestLargestFreeSmallBlockOffset( uint smallBlockTableOffset ); // checks to see if the largest free small block has moved down
|
||
|
|
||
|
void FixUpNextBlocksPrev( byte *newBlock, uint blockSize ); // fixes up the previous pointer of the memory block that lies immediately past the given newBlock, if it remains on the same page.
|
||
|
|
||
|
void AddFreeBlock( rvFreeMemoryBlock_s *freeBlock ); // adds the given free block to the small allocation table or large allocation list.
|
||
|
void RemoveFreeBlock( rvFreeMemoryBlock_s *freeBlock ); // removes the given block from the small allocation table or large allocation list.
|
||
|
|
||
|
void *PageCommit( uint numDesiredPages ); // commits the given number of contiguous pages to physical memory
|
||
|
void PageUncommit( byte *pageAddress, uint numPages ); // uncommits the given range of contiguous pages back to physical memory
|
||
|
|
||
|
void RemoveFromSmallFreeRangeList( uint pageOffset, uint freeBlockPageCount ); // removes the given page range from the m_smallFreePageRangeLists[]
|
||
|
void RemoveFromLargeFreeRangeList( uint pageOffset, uint &startPageOffset, uint &pageCount ); // removes the given page range from the m_largeFreePageRangeList
|
||
|
|
||
|
void *CommitPageRange( uint startPageOffset, uint numPages ); // commits the given range of pages and flags them as committed within the m_pageCommitStateTable array
|
||
|
void UncommitPageRange( uint startPageOffset, uint numPages ); // uncommits the given range of pages and clears their flags within the m_pageCommitStateTable array
|
||
|
|
||
|
void EnterHeapCriticalSection(); // enters this heap's critical section
|
||
|
void ExitHeapCriticalSection(); // exits this heap's critical section
|
||
|
|
||
|
#ifdef _DETECT_BLOCK_MERGE_PROBLEMS
|
||
|
void TestFreeBlockValidity(); // test for free memory block merge problems
|
||
|
#endif
|
||
|
|
||
|
friend class rvHeapArena; // give the rvHeapArena class access to the following methods
|
||
|
// {
|
||
|
ID_INLINE void SetArena( rvHeapArena *arena ); // sets the arena this heap is associated with
|
||
|
ID_INLINE void SetNext( rvHeap *next ); // sets the next heap associated with the same arena
|
||
|
ID_INLINE rvHeap *GetNext( ); // returns the next heap associated with the same arena
|
||
|
// }
|
||
|
};
|
||
|
|
||
|
// IsInitialized
|
||
|
//
|
||
|
// returns: true if this heap is currently initialized, and not shutdown
|
||
|
ID_INLINE bool rvHeap::IsInitialized( ) const
|
||
|
{
|
||
|
return m_arena != NULL;
|
||
|
}
|
||
|
|
||
|
// Allocate
|
||
|
//
|
||
|
// allocate memory
|
||
|
void *rvHeap::Allocate( uint sizeBytes, int debugTag )
|
||
|
{
|
||
|
return AllocateMemory( sizeBytes, debugTag, false );
|
||
|
}
|
||
|
|
||
|
// Allocate16
|
||
|
//
|
||
|
// allocate memory aligned on a 16-byte boundary
|
||
|
void *rvHeap::Allocate16( uint sizeBytes, int debugTag )
|
||
|
{
|
||
|
return AllocateMemory( sizeBytes, debugTag, true );
|
||
|
}
|
||
|
|
||
|
// DoesAllocBelong
|
||
|
//
|
||
|
// returns: true if the given address was allocated from this heap, false otherwise
|
||
|
ID_INLINE bool rvHeap::DoesAllocBelong( void *allocBase )
|
||
|
{
|
||
|
return allocBase >= m_zeroLengthAllocPage && allocBase < m_heapStorageStart + m_heapRangeBytes;
|
||
|
}
|
||
|
|
||
|
// GetArena
|
||
|
//
|
||
|
// returns: the arena this heap is associated with
|
||
|
ID_INLINE rvHeapArena *rvHeap::GetArena( )
|
||
|
{
|
||
|
return m_arena;
|
||
|
}
|
||
|
|
||
|
// GetNext
|
||
|
//
|
||
|
// returns: the next heap belonging to the same arena
|
||
|
ID_INLINE rvHeap *rvHeap::GetNext( )
|
||
|
{
|
||
|
return m_next;
|
||
|
}
|
||
|
|
||
|
// PushCurrent
|
||
|
//
|
||
|
// makes this heap the new top of stack for its associated arena, thus making it current.
|
||
|
ID_INLINE void rvHeap::PushCurrent( )
|
||
|
{
|
||
|
m_arena->Push( *this );
|
||
|
}
|
||
|
|
||
|
// PopCurrent
|
||
|
//
|
||
|
// pops this heap, or any other heap, from the top of stack of this heap's associated arena,
|
||
|
// thus making its predecessor current
|
||
|
ID_INLINE void rvHeap::PopCurrent( )
|
||
|
{
|
||
|
m_arena->Pop( );
|
||
|
}
|
||
|
|
||
|
// GetCommittedSize
|
||
|
//
|
||
|
// returns: the number of physical bytes this heap is currently using (total bytes of all committed pages)
|
||
|
ID_INLINE uint rvHeap::GetCommittedSize( ) const
|
||
|
{
|
||
|
return m_committedSizeBytes;
|
||
|
}
|
||
|
|
||
|
// GetMaxSize
|
||
|
//
|
||
|
// returns the maximum size that this heap can grow to, in bytes
|
||
|
ID_INLINE uint rvHeap::GetMaxSize( ) const
|
||
|
{
|
||
|
return m_maxHeapSizeBytes;
|
||
|
}
|
||
|
|
||
|
// GetBytesAllocated
|
||
|
//
|
||
|
// returns: the number of bytes currently allocated from this heap
|
||
|
// (actual memory footprint of all active allocations
|
||
|
// including internal headers and alignment bytes)
|
||
|
//
|
||
|
ID_INLINE uint rvHeap::GetBytesAllocated( ) const
|
||
|
{
|
||
|
return m_numBytesAllocated;
|
||
|
}
|
||
|
|
||
|
// GetBytesRequested
|
||
|
//
|
||
|
// returns: the number of bytes currently requested from this heap
|
||
|
// (i.e. without overhead)
|
||
|
//
|
||
|
ID_INLINE uint rvHeap::GetBytesRequested( ) const
|
||
|
{
|
||
|
return m_numBytesRequested;
|
||
|
}
|
||
|
|
||
|
// GetNumAllocations
|
||
|
//
|
||
|
// returns: the number of allocations made from this heap that are still active (have not been freed)
|
||
|
ID_INLINE int rvHeap::GetNumAllocations( ) const
|
||
|
{
|
||
|
return m_numAllocations;
|
||
|
}
|
||
|
|
||
|
// GetBytesAllocatedByTag
|
||
|
//
|
||
|
// returns: the number of bytes currently allocated from this heap with the given allocation tag
|
||
|
// (actual memory footprint of all active allocations including internal headers and
|
||
|
// alignment bytes).
|
||
|
ID_INLINE uint rvHeap::GetBytesAllocatedByTag( Mem_Alloc_Types_t allocType ) const
|
||
|
{
|
||
|
assert( allocType < MA_MAX );
|
||
|
return m_allocatedBytesByTag[(uint) allocType];
|
||
|
}
|
||
|
|
||
|
// GetPeekBytesAllocatedByTag
|
||
|
//
|
||
|
// returns: the peek number of bytes allocated from this heap with the given allocation tag (actual
|
||
|
// memory footprint of all active allocations including internal headers and alignment bytes)
|
||
|
ID_INLINE uint rvHeap::GetPeekBytesAllocatedByTag( Mem_Alloc_Types_t allocType ) const
|
||
|
{
|
||
|
assert( allocType < MA_MAX );
|
||
|
return m_peekAllocatedBytesByTag[(uint) allocType];
|
||
|
}
|
||
|
|
||
|
// GetNumAllocationsByTag
|
||
|
//
|
||
|
// returns: the current number of allocation from this heap with the given allocation tag
|
||
|
ID_INLINE int rvHeap::GetNumAllocationsByTag( Mem_Alloc_Types_t allocType ) const
|
||
|
{
|
||
|
assert( allocType < MA_MAX );
|
||
|
return m_numAllocationsByTag[(uint) allocType];
|
||
|
}
|
||
|
|
||
|
// IsWriteCombine
|
||
|
//
|
||
|
// returns: true if memory allocated from this heap has a write-combine cache policy, false otherwise
|
||
|
ID_INLINE bool rvHeap::IsWriteCombine( ) const
|
||
|
{
|
||
|
return (m_flags & rvHeapFlagWriteCombine) != 0;
|
||
|
}
|
||
|
|
||
|
// IsNoCache
|
||
|
//
|
||
|
// returns: true if memory allocated from this heap has a no-cache policy, false otherwise
|
||
|
ID_INLINE bool rvHeap::IsNoCache( ) const
|
||
|
{
|
||
|
return (m_flags & rvHeapFlagNoCache) != 0;
|
||
|
}
|
||
|
|
||
|
// SetArena
|
||
|
//
|
||
|
// sets the arena this heap is associated with
|
||
|
ID_INLINE void rvHeap::SetArena( rvHeapArena *arena )
|
||
|
{
|
||
|
m_arena = arena;
|
||
|
}
|
||
|
|
||
|
// SetNext
|
||
|
//
|
||
|
// sets the next heap associated with the same arena
|
||
|
ID_INLINE void rvHeap::SetNext( rvHeap *next )
|
||
|
{
|
||
|
m_next = next;
|
||
|
}
|
||
|
|
||
|
extern void Mem_FragmentationStats_f( const class idCmdArgs &args );
|
||
|
|
||
|
#endif // #ifndef __RV_HEAP_H__
|