/**********************************************************************/ /** 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 #include 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)); }