Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

445 lines
13 KiB

/**********************************************************************/
/** Microsoft Windows NT **/
/** Copyright(c) Microsoft Corp., 1997 **/
/**********************************************************************/
/*
madel.cxx
This module contains the code for a memory allocation class that doesn't
delete memory until the class goes away.
FILE HISTORY:
1/12/98 michth created
*/
#include "precomp.hxx"
#define DLL_IMPLEMENTATION
#define IMPLEMENTATION_EXPORT
#include <manodel.hxx>
#include <madel.hxx>
MEMORY_ALLOC_DELETE::MEMORY_ALLOC_DELETE( DWORD dwAllocSize,
DWORD dwAlignment,
DWORD dwReserveNum,
DWORD dwMinBlockMultiple,
DWORD dwMaxBlockMultiple,
HANDLE hHeap):
m_dwAllocSize(dwAllocSize),
m_dwAlignment(dwAlignment),
m_dwReserveNum(dwReserveNum),
m_dwBlockMultiple(dwMinBlockMultiple),
m_dwMaxBlockMultiple(dwMaxBlockMultiple),
m_hHeap(hHeap),
m_dwNumAlloced(0),
m_dwNumFree(0),
m_dwNumBlocks(0),
m_dwBlockHeaderSpace(sizeof(MADEL_BLOCK_HEADER)),
m_dwAllocHeaderSpace(sizeof(MADEL_ALLOC_HEADER))
{
//DebugBreak();
//DBG_ASSERT ((dwAlignment == 4) || (dwAlignment == 8));
//
// Make sure there really is an alignment
//
if (m_dwAlignment == 0) {
m_dwAlignment = 4;
}
//
// Now Make sure the alignment is a multiple of 4
//
AlignAdjust(m_dwAlignment, 4);
//
// The alloc header uses 7 bits to store the block number, so max block multiple
// is 128
//
m_dwMaxBlockMultiple = LESSER_OF(m_dwMaxBlockMultiple, MADEL_MAX_ALLOWED_BLOCK_MULTIPLE);
m_dwBlockMultiple = LESSER_OF(m_dwBlockMultiple, MADEL_MAX_ALLOWED_BLOCK_MULTIPLE);
if (m_dwBlockMultiple > m_dwMaxBlockMultiple) {
m_dwBlockMultiple = m_dwMaxBlockMultiple;
}
m_bMaxBlockMultipleEqualsMin = (m_dwMaxBlockMultiple == m_dwBlockMultiple) ? TRUE : FALSE;
//
// Align the size
//
AlignAdjust(m_dwAllocSize, m_dwAlignment);
DBG_ASSERT(m_dwAllocSize != 0);
DBG_ASSERT(m_dwAllocSize <= MADEL_MAX_ALLOWED_SIZE);
//
// Determine space to alloc for block header & Alloc header
// The block header just needs to be aligned by 4. It will be
// placed appropriately in the block so that the first alloc
// header will be appropriately aligned.
//
AlignAdjust(m_dwBlockHeaderSpace, LESSER_OF(8, m_dwAlignment));
AlignAdjust(m_dwAllocHeaderSpace, m_dwAlignment);
if (m_dwAlignment > 8) {
m_dwAlignBytes = m_dwAlignment - 8;
}
else {
m_dwAlignBytes = 0;
}
//
// Get Heap Handle if not passed in
//
if (m_hHeap == MADEL_USE_PROCESS_HEAP) {
m_hHeap = GetProcessHeap();
}
DBG_ASSERT(m_hHeap != NULL);
INITIALIZE_CRITICAL_SECTION(&m_csLock);
SET_CRITICAL_SECTION_SPIN_COUNT( &m_csLock, 4000);
InitializeListHead( &m_leBlockList );
InitializeListHead( &m_leFreeList );
InitializeListHead( &m_leDeleteList );
}
MEMORY_ALLOC_DELETE::~MEMORY_ALLOC_DELETE()
{
LockThis();
PLIST_ENTRY pleIndex, pleNext;
for ( pleIndex = m_leDeleteList.Flink ;
pleIndex != &m_leDeleteList ;
pleIndex = pleNext )
{
pleNext = pleIndex->Flink;
RemoveEntryList(pleIndex);
HeapFree(m_hHeap,
/* Flags */ 0,
(PVOID)((PBYTE)pleIndex - ((PMADEL_BLOCK_HEADER)pleIndex)->byteAlignBytes));
}
DBG_ASSERT(IsListEmpty(&m_leBlockList));
DBG_ASSERT(IsListEmpty(&m_leFreeList));
UnlockThis();
DeleteCriticalSection(&m_csLock);
}
PVOID
MEMORY_ALLOC_DELETE::Alloc()
{
PVOID pvAlloc = NULL;
LockThis();
pvAlloc = GetAllocFromList(&m_leFreeList);
if (pvAlloc == NULL) {
pvAlloc = GetAllocFromList(&m_leDeleteList);
}
if (pvAlloc == NULL) {
if (AllocBlock()) {
pvAlloc = GetAllocFromList(&m_leFreeList);
DBG_ASSERT (pvAlloc != NULL);
}
}
UnlockThis();
return (PVOID)((PBYTE)pvAlloc + m_dwAllocHeaderSpace);
}
BOOL
MEMORY_ALLOC_DELETE::Free (PVOID pvMem)
{
if (pvMem != NULL) {
PMADEL_BLOCK_HEADER pmbhCurrentBlock;
PMADEL_ALLOC_HEADER pmahCurrentAlloc;
PLIST_ENTRY pleIndex, pleNext;
LockThis();
//
// pvMem points to usable mem, header precedes it.
//
pmahCurrentAlloc = (PMADEL_ALLOC_HEADER)((PBYTE)pvMem - m_dwAllocHeaderSpace);
//
// First find the block this is on
//
pmbhCurrentBlock = GetBlockFromAlloc(pmahCurrentAlloc);
//
// Add it to the free list
//
*((PVOID *)pvMem) = pmbhCurrentBlock->pvFreeList;
pmbhCurrentBlock->pvFreeList = (PVOID)pmahCurrentAlloc;
pmbhCurrentBlock->byteNumFree++;
m_dwNumFree++;
DBG_ASSERT(&(pmbhCurrentBlock->m_leBlockList) == (PLIST_ENTRY)pmbhCurrentBlock);
if (pmbhCurrentBlock->byteNumFree == pmbhCurrentBlock->byteBlockMultiple) {
//
// Move to Delete List
//
if (IsListEmpty(&m_leDeleteList)) {
m_byteLeastAllocsOnFreeList = pmbhCurrentBlock->byteBlockMultiple;
}
else {
m_byteLeastAllocsOnFreeList =
LESSER_OF(m_byteLeastAllocsOnFreeList, pmbhCurrentBlock->byteBlockMultiple);
}
RemoveEntryList((PLIST_ENTRY)pmbhCurrentBlock);
InsertHeadList(&m_leDeleteList,(PLIST_ENTRY)pmbhCurrentBlock);
}
else if ( pmbhCurrentBlock->byteNumFree == 1 ) {
//
// It's on the block list, Move to free list
//
RemoveEntryList((PLIST_ENTRY)pmbhCurrentBlock);
InsertHeadList(&m_leFreeList, (PLIST_ENTRY)pmbhCurrentBlock);
}
//
// Now see if a block can be deleted. This could be because one was added above,
// or because m_dwNumFree is now high enough.
//
if (!IsListEmpty(&m_leDeleteList) && (m_dwNumFree >= (m_dwReserveNum + m_byteLeastAllocsOnFreeList))) {
//
// Remove Block and reset min
//
if (m_bMaxBlockMultipleEqualsMin) {
//
// Don't need to find a block that fits or recalculate the minblock multiple.
// Just delete a block;
//
pleIndex = m_leDeleteList.Flink;
RemoveEntryList(pleIndex);
m_dwNumFree -= ((PMADEL_BLOCK_HEADER)pleIndex)->byteNumFree;
m_dwNumAlloced -= ((PMADEL_BLOCK_HEADER)pleIndex)->byteNumFree;
m_dwNumBlocks--;
HeapFree(m_hHeap,
/* Flags */ 0,
(PVOID)((PBYTE)pleIndex - ((PMADEL_BLOCK_HEADER)pleIndex)->byteAlignBytes));
}
else {
m_byteLeastAllocsOnFreeList = (BYTE)m_dwMaxBlockMultiple;
for ( pleIndex = m_leDeleteList.Flink ;
pleIndex != &m_leDeleteList ;
pleIndex = pleNext )
{
pleNext = pleIndex->Flink;
if (m_dwNumFree >= (m_dwReserveNum + ((PMADEL_BLOCK_HEADER)pleIndex)->byteBlockMultiple) ) {
RemoveEntryList(pleIndex);
m_dwNumFree -= ((PMADEL_BLOCK_HEADER)pleIndex)->byteNumFree;
m_dwNumAlloced -= ((PMADEL_BLOCK_HEADER)pleIndex)->byteNumFree;
m_dwNumBlocks--;
HeapFree(m_hHeap,
/* Flags */ 0,
(PVOID)((PBYTE)pleIndex - ((PMADEL_BLOCK_HEADER)pleIndex)->byteAlignBytes));
}
else {
m_byteLeastAllocsOnFreeList =
LESSER_OF(m_byteLeastAllocsOnFreeList, ((PMADEL_BLOCK_HEADER)pleIndex)->byteBlockMultiple);
}
}
}
}
UnlockThis();
}
return TRUE;
}
VOID
MEMORY_ALLOC_DELETE::GetNewBlockMultiple()
{
DWORD dwCalculatedMultiple = LESSER_OF((m_dwNumAlloced / 5), m_dwMaxBlockMultiple);
m_dwBlockMultiple = GREATER_OF(m_dwBlockMultiple, dwCalculatedMultiple);
}
PVOID
MEMORY_ALLOC_DELETE::GetAllocFromList(PLIST_ENTRY pleListHead)
{
PVOID pvAlloc = NULL;
PLIST_ENTRY pleFreeBlock;
PMADEL_BLOCK_HEADER pmbhFreeBlock;
//
// Remove from list
//
if (!IsListEmpty(pleListHead)) {
pleFreeBlock = RemoveHeadList(pleListHead);
DBG_ASSERT(pleFreeBlock != NULL);
pmbhFreeBlock = (PMADEL_BLOCK_HEADER)pleFreeBlock;
DBG_ASSERT(pmbhFreeBlock == (CONTAINING_RECORD(pleFreeBlock, MADEL_BLOCK_HEADER, m_leBlockList)));
pvAlloc = pmbhFreeBlock->pvFreeList;
DBG_ASSERT(pvAlloc != NULL);
pmbhFreeBlock->pvFreeList = *((PVOID *)((PBYTE)(pmbhFreeBlock->pvFreeList) + m_dwAllocHeaderSpace));
pmbhFreeBlock->byteNumFree--;
m_dwNumFree--;
//
// Put the block back on a list.
// Just removed one element, so know it doesn't go on the Delete list.
//
if (pmbhFreeBlock->byteNumFree == 0) {
InsertHeadList(&m_leBlockList, pleFreeBlock);
}
else {
InsertHeadList(&m_leFreeList, pleFreeBlock);
}
}
return pvAlloc;
}
BOOL
MEMORY_ALLOC_DELETE::AllocBlock()
{
PVOID pvNewBlock = NULL;
DWORD dwBlockSize;
BOOL bReturn = FALSE;
GetNewBlockMultiple();
dwBlockSize = ((m_dwAllocSize + m_dwAllocHeaderSpace) * m_dwBlockMultiple) +
m_dwBlockHeaderSpace + m_dwAlignBytes;
pvNewBlock = HeapAlloc(m_hHeap,
/* Flags */ 0,
dwBlockSize);
if (pvNewBlock == NULL) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
else {
//
// Put the block on the free list. Since all allocs are available,
// it really belongs on the delete list, but one will immediately be
// taken off, so just put it on the free list.
//
PBYTE pbFirstAlloc = (PBYTE)pvNewBlock + m_dwBlockHeaderSpace;
if (m_dwAlignment > 8) {
//
// May need to put some bytes in front to align allocs
// Find the first place that is aligned.
//
ULONG_PTR firstAlloc = (ULONG_PTR)pbFirstAlloc;
AlignAdjust(firstAlloc, (ULONG_PTR)m_dwAlignment);
pbFirstAlloc = (PBYTE)firstAlloc;
}
PMADEL_BLOCK_HEADER pmbhBlockHeader = (PMADEL_BLOCK_HEADER)((PBYTE)pbFirstAlloc - m_dwBlockHeaderSpace);
InsertHeadList(&m_leFreeList, (PLIST_ENTRY)pmbhBlockHeader);
pmbhBlockHeader->byteBlockMultiple = (BYTE)m_dwBlockMultiple;
pmbhBlockHeader->byteNumFree = (BYTE)m_dwBlockMultiple;
pmbhBlockHeader->byteAlignBytes = DIFF((PBYTE)pmbhBlockHeader - (PBYTE)pvNewBlock);
CreateBlockFreeList(pmbhBlockHeader);
m_dwNumAlloced += m_dwBlockMultiple;
m_dwNumFree += m_dwBlockMultiple;
m_dwNumBlocks++;
bReturn = TRUE;
}
/* INTRINSA suppress = leaks */
return bReturn;
}
VOID
MEMORY_ALLOC_DELETE::CreateBlockFreeList(PMADEL_BLOCK_HEADER pmbhNewBlock)
{
PVOID pvEnd = (PVOID)((PBYTE)pmbhNewBlock + m_dwBlockHeaderSpace +
(m_dwBlockMultiple * (m_dwAllocSize + m_dwAllocHeaderSpace)));
DBG_ASSERT(pmbhNewBlock != NULL);
pmbhNewBlock->pvFreeList = NULL;
BYTE i;
PVOID pvAllocIndex;
for ((pvAllocIndex = (PVOID)((PBYTE)pmbhNewBlock + m_dwBlockHeaderSpace)), i = 0;
pvAllocIndex < pvEnd;
pvAllocIndex = (PVOID)((PBYTE)pvAllocIndex + m_dwAllocSize + m_dwAllocHeaderSpace), i++) {
InitAllocHead((PMADEL_ALLOC_HEADER)pvAllocIndex, i);
*((PVOID *)((PBYTE)pvAllocIndex + m_dwAllocHeaderSpace)) = pmbhNewBlock->pvFreeList;
pmbhNewBlock->pvFreeList = pvAllocIndex;
}
}
VOID
MEMORY_ALLOC_DELETE::AlignAdjust(DWORD &rdwSize, DWORD dwAlignment)
{
if ((rdwSize % dwAlignment != 0)) {
rdwSize &= (0xFFFFFFFF - dwAlignment + 1);
rdwSize += dwAlignment;
}
}
#ifdef _WIN64
VOID
MEMORY_ALLOC_DELETE::AlignAdjust(ULONG_PTR &rSize, ULONG_PTR Alignment)
{
rSize = ( rSize + Alignment - 1 ) & ~( Alignment - 1 );
}
#endif
VOID
MEMORY_ALLOC_DELETE::InitAllocHead(PMADEL_ALLOC_HEADER pvAlloc,
DWORD dwAllocIndex)
{
pvAlloc->dwSize = m_dwAllocSize;
pvAlloc->bNumAlloc = dwAllocIndex;
pvAlloc->bMadelAlloc = 1;
}
PMADEL_BLOCK_HEADER
MEMORY_ALLOC_DELETE::GetBlockFromAlloc(PMADEL_ALLOC_HEADER pmahMem)
{
return (PMADEL_BLOCK_HEADER)((PBYTE)pmahMem -
((pmahMem->bNumAlloc * (m_dwAllocSize + m_dwAllocHeaderSpace)) + m_dwBlockHeaderSpace));
}