/*========================================================================== * * Copyright (C) 2001-2002 Microsoft Corporation. All Rights Reserved. * * File: fixedpool.cpp * Content: fixed size pool manager * * History: * Date By Reason * ====== == ====== * 07-21-2001 masonb Created * 10-16-2001 vanceo Tweaked release locking and freed memory if Alloc function fails * 02-22-2002 simonpow Removed c'tor and d'tor, which weren't consistently being called ***************************************************************************/ #include "dncmni.h" #undef DPF_MODNAME #define DPF_MODNAME "CFixedPool::Initialize" BOOL CFixedPool::Initialize(DWORD dwElementSize, FN_BLOCKALLOC pfnBlockAlloc, FN_BLOCKGET pfnBlockGet, FN_BLOCKRELEASE pfnBlockRelease, FN_BLOCKDEALLOC pfnBlockDeAlloc) { // Ensure that we stay heap aligned for SLISTs #ifdef _WIN64 DBG_CASSERT(sizeof(FIXED_POOL_ITEM) % 16 == 0); #else // !_WIN64 DBG_CASSERT(sizeof(FIXED_POOL_ITEM) % 8 == 0); #endif // _WIN64 #ifdef DBG if (!DNInitializeCriticalSection(&m_csInUse)) { DPFERR("Failed initializing pool critical section"); m_fInitialized = FALSE; return FALSE; } m_pInUseElements = NULL; #endif // DBG DNInitializeSListHead(&m_slAvailableElements); m_pfnBlockAlloc = pfnBlockAlloc; m_pfnBlockGet = pfnBlockGet; m_pfnBlockRelease = pfnBlockRelease; m_pfnBlockDeAlloc = pfnBlockDeAlloc; m_dwItemSize = dwElementSize; #ifdef DBG m_lAllocated = 0; #endif // DBG m_lInUse = 0; m_fInitialized = TRUE; return TRUE; } #undef DPF_MODNAME #define DPF_MODNAME "CFixedPool::DeInitialize" VOID CFixedPool::DeInitialize() { FIXED_POOL_ITEM* pItem; DNSLIST_ENTRY* pslEntry; if (m_fInitialized == FALSE) { return; } // Clean up entries sitting in the pool pslEntry = DNInterlockedPopEntrySList(&m_slAvailableElements); while(pslEntry != NULL) { pItem = CONTAINING_RECORD(pslEntry, FIXED_POOL_ITEM, slist); if (m_pfnBlockDeAlloc != NULL) { (*m_pfnBlockDeAlloc)(pItem + 1); } DNFree(pItem); #ifdef DBG DNInterlockedDecrement(&m_lAllocated); DNASSERT(m_lAllocated >=0); #endif // DBG pslEntry = DNInterlockedPopEntrySList(&m_slAvailableElements); } #ifdef DBG DumpLeaks(); DNDeleteCriticalSection(&m_csInUse); #endif // DBG m_fInitialized = FALSE; } #ifdef DPNBUILD_PREALLOCATEDMEMORYMODEL #undef DPF_MODNAME #define DPF_MODNAME "CFixedPool::Preallocate" DWORD CFixedPool::Preallocate( DWORD dwCount, PVOID pvContext ) { DWORD dwAllocated; FIXED_POOL_ITEM* pItem; DNASSERT(m_fInitialized == TRUE); for(dwAllocated = 0; dwAllocated < dwCount; dwAllocated++) { pItem = (FIXED_POOL_ITEM*)DNMalloc(sizeof(FIXED_POOL_ITEM) + m_dwItemSize); if (pItem == NULL) { DPFERR("Out of memory allocating new item for pool"); return NULL; } if ((m_pfnBlockAlloc != NULL) && !(*m_pfnBlockAlloc)(pItem + 1, pvContext)) { DPFERR("Alloc function returned FALSE allocating new item for pool"); // Can't stick the new item as available in the pool since pool assumes Alloc has // succeeded when it's in the pool. DNFree(pItem); break; } #ifdef DBG DNInterlockedIncrement(&m_lAllocated); #endif // DBG DNInterlockedPushEntrySList(&m_slAvailableElements, &pItem->slist); } return dwAllocated; } #endif // DPNBUILD_PREALLOCATEDMEMORYMODEL #undef DPF_MODNAME #define DPF_MODNAME "CFixedPool::Get" VOID* CFixedPool::Get( PVOID pvContext ) { FIXED_POOL_ITEM* pItem; DNSLIST_ENTRY* pslEntry; DNASSERT(m_fInitialized == TRUE); pslEntry = DNInterlockedPopEntrySList(&m_slAvailableElements); if (pslEntry == NULL) { #ifdef DPNBUILD_PREALLOCATEDMEMORYMODEL DPFX(DPFPREP, 0, "No more items in pool!"); DNASSERTX(! "No more items in pool!", 2); return NULL; #else // ! DPNBUILD_PREALLOCATEDMEMORYMODEL pItem = (FIXED_POOL_ITEM*)DNMalloc(sizeof(FIXED_POOL_ITEM) + m_dwItemSize); if (pItem == NULL) { DPFERR("Out of memory allocating new item for pool!"); return NULL; } if ((m_pfnBlockAlloc != NULL) && !(*m_pfnBlockAlloc)(pItem + 1, pvContext)) { DPFERR("Alloc function returned FALSE allocating new item for pool!"); // Can't stick the new item as available in the pool since pool assumes Alloc has // succeeded when it's in the pool. DNFree(pItem); return NULL; } #ifdef DBG DNInterlockedIncrement(&m_lAllocated); #endif // DBG #endif // ! DPNBUILD_PREALLOCATEDMEMORYMODEL } else { pItem = CONTAINING_RECORD(pslEntry, FIXED_POOL_ITEM, slist); } // At this point we have an item whether it was newly created or pulled from the pool. InterlockedIncrement(&m_lInUse); DNASSERT(m_lInUse > 0); #ifdef DBG // Note the callstack and add the item to the in use list. pItem->callstack.NoteCurrentCallStack(); DNEnterCriticalSection(&m_csInUse); pItem->slist.Next = m_pInUseElements; m_pInUseElements = &pItem->slist; DNLeaveCriticalSection(&m_csInUse); // In debug only, store the pool the item belongs to on the item for checking upon release pItem->pThisPool = this; #endif // DBG if (m_pfnBlockGet != NULL) { (*m_pfnBlockGet)(pItem + 1, pvContext); } return (pItem + 1); } #undef DPF_MODNAME #define DPF_MODNAME "CFixedPool::Release" VOID CFixedPool::Release(VOID* pvItem) { FIXED_POOL_ITEM* pItem; DNASSERT(m_fInitialized == TRUE); DNASSERT(pvItem != NULL); pItem = (FIXED_POOL_ITEM*)pvItem - 1; #ifdef DBG // Make sure the item comes from this pool. // If the item has already been released, pThisPool will be NULL. DNASSERT(pItem->pThisPool == this); #endif // DBG if (m_pfnBlockRelease != NULL) { (*m_pfnBlockRelease)(pvItem); } #ifdef DBG // Remove the item from the in use list. DNEnterCriticalSection(&m_csInUse); if (m_pInUseElements == &pItem->slist) { // Easy case, just reset m_pInUseElements to the next item in the list. m_pInUseElements = pItem->slist.Next; } else { DNSLIST_ENTRY* pslEntry; // We need to run the list and look for it pslEntry = m_pInUseElements; while (pslEntry != NULL) { if (pslEntry->Next == &pItem->slist) { // Found it, pull it out. pslEntry->Next = pItem->slist.Next; break; } pslEntry = pslEntry->Next; } } DNLeaveCriticalSection(&m_csInUse); #endif // DBG DNASSERT(m_lInUse != 0); InterlockedDecrement(&m_lInUse); DNInterlockedPushEntrySList(&m_slAvailableElements, &pItem->slist); } #undef DPF_MODNAME #define DPF_MODNAME "CFixedPool::GetInUseCount" DWORD CFixedPool::GetInUseCount( void ) { DNASSERT(m_fInitialized == TRUE); return m_lInUse; } #ifdef DBG #undef DPF_MODNAME #define DPF_MODNAME "CFixedPool::DumpLeaks" VOID CFixedPool::DumpLeaks() { // NOTE: It is important that this be a separate function because it consumes so much stack space. FIXED_POOL_ITEM* pItem; DNSLIST_ENTRY* pslEntry; TCHAR szCallStackBuffer[ CALLSTACK_BUFFER_SIZE ]; // Report any leaked items if(m_lAllocated) { DNASSERT(m_lInUse == m_lAllocated); DNASSERT(m_pInUseElements != NULL); DPFX(DPFPREP, 0, "(%p) Pool leaking %d items", this, m_lAllocated); pslEntry = m_pInUseElements; while(pslEntry != NULL) { pItem = CONTAINING_RECORD(pslEntry, FIXED_POOL_ITEM, slist); pItem->callstack.GetCallStackString( szCallStackBuffer ); DPFX(DPFPREP, 0, "(%p) Pool item leaked at address %p (user pointer: %p)\n%s", this, pItem, pItem + 1, szCallStackBuffer ); pslEntry = pslEntry->Next; } DNASSERT(0); } else { DNASSERT(m_pInUseElements == NULL); DNASSERT(m_lInUse == 0); } } #endif // DBG