Files
mars-flaim/flaim/src/flfixed.cpp

2129 lines
46 KiB
C++

//-------------------------------------------------------------------------
// Desc: Memory management using fixed-size allocators - for dealing with
// memory fragmentation issues.
// Tabs: 3
//
// Copyright (c) 2004-2006 Novell, Inc. All Rights Reserved.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, contact Novell, Inc.
//
// To contact Novell about this file by physical or electronic mail,
// you may find current contact information at www.novell.com
//
// $Id: flfixed.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $
//-------------------------------------------------------------------------
#include "flaimsys.h"
#ifdef FLM_NLM
extern "C"
{
extern LONG gv_lAllocRTag;
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
F_SlabManager::F_SlabManager()
{
m_hMutex = F_MUTEX_NULL;
m_pFirstInSlabList = NULL;
m_pLastInSlabList = NULL;
m_uiTotalSlabs = 0;
m_uiAvailSlabs = 0;
m_uiInUseSlabs = 0;
m_pLowPrealloc = NULL;
m_pHighPrealloc = NULL;
}
/****************************************************************************
Desc:
****************************************************************************/
F_SlabManager::~F_SlabManager()
{
flmAssert( !m_uiInUseSlabs);
flmAssert( m_uiAvailSlabs == m_uiTotalSlabs);
freeAllSlabs();
if( m_hMutex != F_MUTEX_NULL)
{
f_mutexDestroy( &m_hMutex);
}
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_SlabManager::setup(
FLMUINT uiPreallocSize,
FLMUINT uiMinSlabSize)
{
RCODE rc = FERR_OK;
FLMUINT uiSysSlabSize = 0;
FLMUINT uiSlabSize = uiMinSlabSize;
if( RC_BAD( rc = f_mutexCreate( &m_hMutex)))
{
goto Exit;
}
// Determine the slab size
#ifdef FLM_WIN
{
SYSTEM_INFO sysInfo;
GetSystemInfo( &sysInfo);
uiSysSlabSize = sysInfo.dwAllocationGranularity;
}
#endif
if( !uiSysSlabSize)
{
uiSysSlabSize = uiSlabSize;
}
// Round the given slab size up to the closest operating
// system slab size so we don't waste any memory.
if( uiSlabSize % uiSysSlabSize)
{
m_uiSlabSize = ((uiSlabSize / uiSysSlabSize) + 1) * uiSysSlabSize;
}
else
{
m_uiSlabSize = uiSlabSize;
}
// Pre-allocate the requested amount of memory from the system
if( uiPreallocSize)
{
if( RC_BAD( rc = resize( uiPreallocSize)))
{
goto Exit;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_SlabManager::resize(
FLMUINT uiNumBytes,
FLMUINT * puiActualSize)
{
RCODE rc = FERR_OK;
FLMUINT uiSlabsNeeded;
void * pSlab;
f_mutexLock( m_hMutex);
if( puiActualSize)
{
*puiActualSize = 0;
}
uiSlabsNeeded = (uiNumBytes / m_uiSlabSize) +
((uiNumBytes % m_uiSlabSize) ? 1 : 0);
if( !uiSlabsNeeded && !m_uiInUseSlabs)
{
freeAllSlabs();
}
else if( m_uiTotalSlabs > uiSlabsNeeded)
{
// Do the best we can to free slabs. We can only get rid of
// slabs that aren't in use.
if( RC_BAD( rc = sortSlabList()))
{
goto Exit;
}
while( m_pLastInSlabList && m_uiTotalSlabs > uiSlabsNeeded)
{
pSlab = m_pLastInSlabList;
if( (m_pLastInSlabList = ((SLABHEADER *)pSlab)->pPrev) != NULL)
{
((SLABHEADER *)m_pLastInSlabList)->pNext = NULL;
}
else
{
m_pFirstInSlabList = NULL;
}
releaseSlabToSystem( pSlab);
flmAssert( m_uiTotalSlabs);
flmAssert( m_uiInUseSlabs);
m_uiAvailSlabs--;
m_uiTotalSlabs--;
}
if( !m_uiTotalSlabs)
{
flmAssert( !m_pFirstInSlabList);
flmAssert( !m_pLastInSlabList);
m_pLowPrealloc = NULL;
m_pHighPrealloc = NULL;
}
else if( !uiNumBytes)
{
// Set the low and high pre-allocation pointers to NULL so that
// slabs will be released back to the system when they are returned
// to the slab manager.
m_pLowPrealloc = NULL;
m_pHighPrealloc = NULL;
}
else
{
if( m_pFirstInSlabList < m_pLowPrealloc)
{
m_pLowPrealloc = m_pFirstInSlabList;
}
if( m_pLastInSlabList > m_pHighPrealloc)
{
m_pHighPrealloc = m_pLastInSlabList;
}
}
}
else
{
// Allocate the required number of slabs
while( m_uiTotalSlabs < uiSlabsNeeded)
{
if( (pSlab = allocSlabFromSystem()) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
if( !m_pLowPrealloc || pSlab < m_pLowPrealloc)
{
m_pLowPrealloc = pSlab;
}
if( pSlab > m_pHighPrealloc)
{
m_pHighPrealloc = pSlab;
}
// Touch every byte in the slab so that the operating system is
// forced to immediately assign physical memory.
f_memset( pSlab, 0, m_uiSlabSize);
// Link the slab into the avail list
if( m_pFirstInSlabList)
{
((SLABHEADER *)m_pFirstInSlabList)->pPrev = pSlab;
}
((SLABHEADER *)pSlab)->pNext = m_pFirstInSlabList;
m_pFirstInSlabList = pSlab;
if( !m_pLastInSlabList)
{
m_pLastInSlabList = pSlab;
}
m_uiTotalSlabs++;
m_uiAvailSlabs++;
}
}
if( puiActualSize)
{
*puiActualSize = m_uiTotalSlabs * m_uiSlabSize;
}
Exit:
if( RC_BAD( rc))
{
freeAllSlabs();
}
f_mutexUnlock( m_hMutex);
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_SlabManager::allocSlab(
void ** ppSlab)
{
RCODE rc = FERR_OK;
f_mutexLock( m_hMutex);
if( m_pFirstInSlabList)
{
*ppSlab = m_pFirstInSlabList;
if( (m_pFirstInSlabList =
((SLABHEADER *)m_pFirstInSlabList)->pNext) != NULL)
{
((SLABHEADER *)m_pFirstInSlabList)->pPrev = NULL;
}
else
{
m_pLastInSlabList = NULL;
}
((SLABHEADER *)*ppSlab)->pNext = NULL;
flmAssert( m_uiAvailSlabs);
m_uiAvailSlabs--;
m_uiInUseSlabs++;
}
else
{
flmAssert( !m_uiAvailSlabs);
if( (*ppSlab = allocSlabFromSystem()) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
m_uiTotalSlabs++;
m_uiInUseSlabs++;
}
Exit:
f_mutexUnlock( m_hMutex);
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
void F_SlabManager::freeSlab(
void ** ppSlab)
{
flmAssert( ppSlab && *ppSlab);
f_mutexLock( m_hMutex);
// There's no guarantee that out-of-band allocations will
// fall outside of the preallocated address space, but
// this is the best we can do. The FLAIM cache system
// is generally well-behaved. Thus, even if an out-of-band
// allocation falls within the prealloc range, we won't
// exceed our allocation limits by too much. It is better
// to hold onto a little extra memory than to fail an allocation
// request.
if( m_pLowPrealloc && *ppSlab >= m_pLowPrealloc && *ppSlab <= m_pHighPrealloc)
{
((SLABHEADER *)*ppSlab)->pPrev = NULL;
if( (((SLABHEADER *)*ppSlab)->pNext = m_pFirstInSlabList) != NULL)
{
((SLABHEADER *)m_pFirstInSlabList)->pPrev = *ppSlab;
}
else
{
m_pLastInSlabList = *ppSlab;
}
m_pFirstInSlabList = *ppSlab;
*ppSlab = NULL;
flmAssert( m_uiInUseSlabs);
m_uiInUseSlabs--;
m_uiAvailSlabs++;
}
else
{
releaseSlabToSystem( *ppSlab);
*ppSlab = NULL;
flmAssert( m_uiTotalSlabs);
flmAssert( m_uiInUseSlabs);
m_uiTotalSlabs--;
m_uiInUseSlabs--;
}
f_mutexUnlock( m_hMutex);
}
/****************************************************************************
Desc:
****************************************************************************/
void F_SlabManager::freeAllSlabs( void)
{
void * pNextSlab;
SLABHEADER * pSlabHeader;
while( m_pFirstInSlabList)
{
pSlabHeader = (SLABHEADER *)m_pFirstInSlabList;
pNextSlab = pSlabHeader->pNext;
releaseSlabToSystem( m_pFirstInSlabList);
m_pFirstInSlabList = pNextSlab;
}
m_uiTotalSlabs = 0;
m_uiAvailSlabs = 0;
m_pLowPrealloc = NULL;
m_pHighPrealloc = NULL;
m_pLastInSlabList = NULL;
}
/****************************************************************************
Desc:
****************************************************************************/
void * F_SlabManager::allocSlabFromSystem( void)
{
void * pSlab;
#if defined( FLM_OSX) && !defined( MAP_ANONYMOUS)
#define MAP_ANONYMOUS MAP_ANON
#endif
#ifdef FLM_WIN
pSlab = VirtualAlloc( NULL,
(DWORD)m_uiSlabSize, MEM_COMMIT, PAGE_READWRITE);
#elif defined( FLM_UNIX) && !defined( FLM_SOLARIS)
if( (pSlab = mmap( 0, m_uiSlabSize,
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) == MAP_FAILED)
{
return( NULL);
}
// We don't use mmap on Solaris because of the amount of address
// space it consumes for red-zones and rounding. The following is from
// the Solaris mmap man page:
//
// The mmap() function aligns based on the length of the map-
// ping. When determining the amount of space to add to the
// address space, mmap() includes two 8-Kbyte pages, one at
// each end of the mapping that are not mapped and are there-
// fore used as "red-zone" pages. Attempts to reference these
// pages result in access violations.
//
// The size requested is incremented by the 16 Kbytes for these
// pages and is then subject to rounding constraints. The con-
// straints are:
//
// o For 32-bit processes:
//
// If length > 4 Mbytes
// round to 4-Mbyte multiple
// elseif length > 512 Kbytes
// round to 512-Kbyte multiple
// else
// round to 64-Kbyte multiple
//
// o For 64-bit processes:
//
// If length > 4 Mbytes
// round to 4-Mbyte multiple
// else
// round to 1-Mbyte multiple
//
// The net result is that for a 32-bit process:
//
// o If an mmap() request is made for 4 Mbytes, it results
// in 4 Mbytes + 16 Kbytes and is rounded up to 8 Mbytes.
//
// o If an mmap() request is made for 512 Kbytes, it results
// in 512 Kbytes + 16 Kbytes and is rounded up to 1 Mbyte.
//
// o If an mmap() request is made for 1 Mbyte, it results in
// 1 Mbyte + 16 Kbytes and is rounded up to 1.5 Mbytes.
//
// o Each 8-Kbyte mmap request "consumes" 64 Kbytes of vir-
// tual address space.
#else
if( RC_BAD( f_alloc( m_uiSlabSize, &pSlab)))
{
return( NULL);
}
#endif
return( pSlab);
}
/****************************************************************************
Desc:
****************************************************************************/
void F_SlabManager::releaseSlabToSystem(
void * pSlab)
{
flmAssert( pSlab);
#ifdef FLM_WIN
VirtualFree( pSlab, 0, MEM_RELEASE);
#elif defined( FLM_UNIX) && !defined( FLM_SOLARIS)
munmap( pSlab, m_uiSlabSize);
#else
f_free( &pSlab);
#endif
}
/****************************************************************************
Desc:
****************************************************************************/
FLMINT F_SlabManager::slabAddrCompareFunc(
void * pvBuffer,
FLMUINT uiPos1,
FLMUINT uiPos2)
{
void * pSlab1 = (((void **)pvBuffer)[ uiPos1]);
void * pSlab2 = (((void **)pvBuffer)[ uiPos2]);
flmAssert( pSlab1 != pSlab2);
if( pSlab1 < pSlab2)
{
return( -1);
}
return( 1);
}
/****************************************************************************
Desc:
****************************************************************************/
void F_SlabManager::slabAddrSwapFunc(
void * pvBuffer,
FLMUINT uiPos1,
FLMUINT uiPos2)
{
void ** ppSlab1 = &(((void **)pvBuffer)[ uiPos1]);
void ** ppSlab2 = &(((void **)pvBuffer)[ uiPos2]);
void * pTmp;
pTmp = *ppSlab1;
*ppSlab1 = *ppSlab2;
*ppSlab2 = pTmp;
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_SlabManager::sortSlabList( void)
{
RCODE rc = FERR_OK;
FLMUINT uiLoop;
void ** pSortBuf = NULL;
FLMUINT uiMaxSortEntries;
FLMUINT uiSortEntries = 0;
#define SMALL_SORT_BUF_SIZE 256
void * smallSortBuf[ SMALL_SORT_BUF_SIZE];
void * pCurSlab;
void * pPrevSib;
if( m_uiAvailSlabs <= 1)
{
goto Exit;
}
uiMaxSortEntries = m_uiAvailSlabs;
// Sort the avail list according to the starting memory addresses of the
// slabs
if( uiMaxSortEntries <= SMALL_SORT_BUF_SIZE)
{
pSortBuf = smallSortBuf;
}
else
{
if( RC_BAD( rc = f_alloc( uiMaxSortEntries * sizeof( void *), &pSortBuf)))
{
goto Exit;
}
}
pCurSlab = m_pFirstInSlabList;
while( pCurSlab)
{
flmAssert( uiSortEntries != uiMaxSortEntries);
pSortBuf[ uiSortEntries++] = pCurSlab;
pCurSlab = ((SLABHEADER *)pCurSlab)->pNext;
}
flmAssert( uiSortEntries == uiMaxSortEntries);
// Quick sort
flmAssert( uiSortEntries);
f_qsort( (FLMBYTE *)pSortBuf, 0, uiSortEntries - 1,
F_SlabManager::slabAddrCompareFunc,
F_SlabManager::slabAddrSwapFunc);
// Re-link the items in the list according to the new
// sort order
m_pFirstInSlabList = NULL;
m_pLastInSlabList = NULL;
pCurSlab = NULL;
pPrevSib = NULL;
for( uiLoop = 0; uiLoop < uiSortEntries; uiLoop++)
{
pCurSlab = pSortBuf[ uiLoop];
((SLABHEADER *)pCurSlab)->pNext = NULL;
((SLABHEADER *)pCurSlab)->pPrev = NULL;
if( pPrevSib)
{
((SLABHEADER *)pCurSlab)->pPrev = pPrevSib;
((SLABHEADER *)pPrevSib)->pNext = pCurSlab;
}
else
{
m_pFirstInSlabList = pCurSlab;
}
pPrevSib = pCurSlab;
}
m_pLastInSlabList = pCurSlab;
Exit:
if( pSortBuf && pSortBuf != smallSortBuf)
{
f_free( &pSortBuf);
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
F_FixedAlloc::F_FixedAlloc()
{
m_pSlabManager = NULL;
m_pFirstBlock = NULL;
m_pLastBlock = NULL;
m_pFirstBlockWithAvailCells = NULL;
m_pLastBlockWithAvailCells = NULL;
m_uiBlocksWithAvailCells = 0;
m_bAvailListSorted = TRUE;
m_uiTotalFreeCells = 0;
m_fnCanRelocate = NULL;
m_fnRelocate = NULL;
m_puiTotalBytesAllocated = NULL;
m_uiSlabSize = 0;
m_hLocalMutex = F_MUTEX_NULL;
m_phMutex = NULL;
m_uiAllocatedSlabs = 0;
m_uiAllocatedCells = 0;
m_uiAllocatedCellWatermark = 0;
m_uiEverFreedCells = 0;
}
/****************************************************************************
Desc: Destructor for F_FixedAlloc. checks for memory leaks, and
frees all memory in use.
****************************************************************************/
F_FixedAlloc::~F_FixedAlloc()
{
#ifdef FLM_DEBUG
testForLeaks();
#endif
freeAll();
if( m_pSlabManager)
{
m_pSlabManager->Release();
}
if( m_hLocalMutex != F_MUTEX_NULL)
{
f_mutexDestroy( &m_hLocalMutex);
}
}
/****************************************************************************
Desc: Setup method for any setup that can fail
****************************************************************************/
RCODE F_FixedAlloc::setup(
F_SlabManager * pSlabManager,
FLMBOOL bUseMutex,
FLMUINT uiCellSize,
FLMUINT * puiTotalBytesAllocated)
{
RCODE rc = FERR_OK;
flmAssert( pSlabManager);
flmAssert( uiCellSize);
m_pSlabManager = pSlabManager;
m_pSlabManager->AddRef();
m_uiCellSize = uiCellSize;
m_puiTotalBytesAllocated = puiTotalBytesAllocated;
m_uiSlabSize = m_pSlabManager->getSlabSize();
// Get the alloc-aligned versions of all the sizes
m_uiBlockHeaderSize = getAllocAlignedSize( sizeof( BLOCK));
m_uiCellHeaderSize = getAllocAlignedSize( sizeof( CELLHEADER));
m_uiCellSize = getAllocAlignedSize( m_uiCellSize);
// Ensure that there's enough space for our overhead
flmAssert( m_uiCellSize >= sizeof( CELLAVAILNEXT));
m_uiSizeOfCellAndHeader = m_uiCellHeaderSize + m_uiCellSize;
m_uiCellsPerBlock =
(m_uiSlabSize - m_uiBlockHeaderSize) /
m_uiSizeOfCellAndHeader;
flmAssert( m_uiCellsPerBlock);
flmAssert( (m_uiCellsPerBlock * m_uiCellSize) < m_uiSlabSize);
if( bUseMutex)
{
if( RC_BAD( rc = f_mutexCreate( &m_hLocalMutex)))
{
goto Exit;
}
m_phMutex = &m_hLocalMutex;
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Setup method for any setup that can fail
****************************************************************************/
RCODE F_FixedAlloc::setup(
F_SlabManager * pSlabManager,
F_MUTEX * phMutex,
FLMUINT uiCellSize,
FLMUINT * puiTotalBytesAllocated)
{
RCODE rc = FERR_OK;
if( RC_BAD( rc = setup( pSlabManager, (FLMBOOL)FALSE,
uiCellSize, puiTotalBytesAllocated)))
{
goto Exit;
}
if( phMutex)
{
m_phMutex = phMutex;
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Private, internal method to fetch a cell
****************************************************************************/
void * F_FixedAlloc::getCell( void)
{
BLOCK * pBlock = NULL;
FLMBYTE * pCell = NULL;
CELLHEADER * pHeader;
// If there's a block that has an avail cell, that one gets priority
if( (pBlock = m_pFirstBlockWithAvailCells) != NULL)
{
flmAssert( pBlock->ui32AvailCellCount <= m_uiTotalFreeCells);
flmAssert( m_uiTotalFreeCells);
flmAssert( pBlock->ui32AllocatedCells < m_uiCellsPerBlock);
pCell = m_pFirstBlockWithAvailCells->pLocalAvailCellListHead;
flmAssert( pCell);
pHeader = (CELLHEADER *)((FLMBYTE *)pCell - m_uiCellHeaderSize);
pBlock->ui32AllocatedCells++;
pBlock->ui32AvailCellCount--;
m_uiTotalFreeCells--;
// An avail cell holds as its contents the next pointer in the avail chain.
// Avail chains do not span blocks.
pBlock->pLocalAvailCellListHead = ((CELLAVAILNEXT *)pCell)->pNextInList;
// If there are no other avail cells in this block at this point,
// then we need to unlink the block from the
// blocks-with-avail-cells list, headed by m_pFirstBlockWithAvailCells
if( !pBlock->pLocalAvailCellListHead)
{
// Save a copy of the block we're going to unlink
BLOCK * pBlockToUnlink = pBlock;
// Need to keep the NULLNESS of the content of the cell consistent
// with the block's ui32AvailCellCount being equal to 0
flmAssert( !pBlockToUnlink->ui32AvailCellCount);
// There can't be a pPrevBlockWithAvailCells since
// we're positioned to the first one
flmAssert( !pBlockToUnlink->pPrevBlockWithAvailCells);
// Update m_pFirstBlockWithAvailCells to point to the next one
if( (m_pFirstBlockWithAvailCells =
pBlockToUnlink->pNextBlockWithAvailCells) == NULL)
{
flmAssert( m_pLastBlockWithAvailCells == pBlockToUnlink);
m_pLastBlockWithAvailCells = NULL;
}
// Unlink from blocks-with-avail-cells list
if( pBlockToUnlink->pNextBlockWithAvailCells)
{
pBlockToUnlink->
pNextBlockWithAvailCells->pPrevBlockWithAvailCells =
pBlockToUnlink->pPrevBlockWithAvailCells;
pBlockToUnlink->pNextBlockWithAvailCells = NULL;
flmAssert( !pBlockToUnlink->pPrevBlockWithAvailCells);
}
// Decrement the block count
flmAssert( m_uiBlocksWithAvailCells);
m_uiBlocksWithAvailCells--;
}
}
else
{
// If our m_pFirstBlock is completely full, or there is no
// m_pFirstBlock, it is time to allocate a new block
if( !m_pFirstBlock ||
(m_pFirstBlock->ui32NextNeverUsedCell == m_uiCellsPerBlock))
{
BLOCK * pNewBlock;
if( (pNewBlock = getAnotherBlock()) == NULL)
{
goto Exit;
}
if( m_pFirstBlock)
{
pNewBlock->pNext = m_pFirstBlock;
m_pFirstBlock->pPrev = pNewBlock;
}
else
{
m_pLastBlock = pNewBlock;
}
m_pFirstBlock = pNewBlock;
}
pBlock = m_pFirstBlock;
pBlock->ui32AllocatedCells++;
flmAssert( pBlock->ui32AllocatedCells <= m_uiCellsPerBlock);
pHeader = (CELLHEADER *)
((FLMBYTE *)pBlock + m_uiBlockHeaderSize +
(m_uiSizeOfCellAndHeader * m_pFirstBlock->ui32NextNeverUsedCell));
pCell = ((FLMBYTE *)pHeader + m_uiCellHeaderSize);
m_pFirstBlock->ui32NextNeverUsedCell++;
}
pHeader->pContainingBlock = pBlock;
#ifdef FLM_DEBUG
if (gv_FlmSysData.bTrackLeaks && gv_FlmSysData.bStackWalk)
{
pHeader->puiStack = memWalkStack();
}
else
{
pHeader->puiStack = NULL;
}
#endif
m_uiAllocatedCells++;
m_uiAllocatedCellWatermark =
f_max( m_uiAllocatedCells, m_uiAllocatedCellWatermark);
Exit:
return( pCell);
}
/****************************************************************************
Desc: Public method to free a cell of memory back to the system.
****************************************************************************/
void F_FixedAlloc::freeCell(
void * pCell,
FLMBOOL bMutexLocked,
FLMBOOL bFreeIfEmpty,
FLMBOOL * pbFreedBlock)
{
CELLAVAILNEXT * pCellContents;
CELLHEADER * pHeader;
BLOCK * pBlock;
FLMBOOL bUnlockMutex = FALSE;
if( pbFreedBlock)
{
*pbFreedBlock = FALSE;
}
if( !pCell)
{
return;
}
if( !bMutexLocked && m_phMutex)
{
f_mutexLock( *m_phMutex);
bUnlockMutex = TRUE;
}
pCellContents = (CELLAVAILNEXT *)pCell;
pHeader = (CELLHEADER *)(((FLMBYTE *)pCell) - m_uiCellHeaderSize);
pBlock = pHeader->pContainingBlock;
flmAssert( pBlock);
flmAssert( pBlock->pvAllocator == (void *)this);
pHeader->pContainingBlock = NULL;
#ifdef FLM_DEBUG
if( pHeader->puiStack)
{
os_free( pHeader->puiStack);
pHeader->puiStack = NULL;
}
#endif
// Should always be set on a free
flmAssert( m_pFirstBlock);
// Add the cell to the pBlock's free list
pCellContents->pNextInList = pBlock->pLocalAvailCellListHead;
#ifdef FLM_DEBUG
// Write out a string that's easy to see in memory when debugging
f_strcpy( pCellContents->szDebugPattern, "FREECELL");
#endif
flmAssert( pCell);
pBlock->pLocalAvailCellListHead = (FLMBYTE *)pCell;
pBlock->ui32AvailCellCount++;
flmAssert( pBlock->ui32AllocatedCells);
pBlock->ui32AllocatedCells--;
// If there's no chain, make this one the first
if( !m_pFirstBlockWithAvailCells)
{
m_pFirstBlockWithAvailCells = pBlock;
m_pLastBlockWithAvailCells = pBlock;
flmAssert( !pBlock->pNextBlockWithAvailCells);
flmAssert( !pBlock->pPrevBlockWithAvailCells);
m_uiBlocksWithAvailCells++;
m_bAvailListSorted = TRUE;
}
else if( pBlock->ui32AvailCellCount == 1)
{
// This item is not linked in to the chain, so link it in
if( m_bAvailListSorted && pBlock > m_pFirstBlockWithAvailCells)
{
m_bAvailListSorted = FALSE;
}
pBlock->pNextBlockWithAvailCells = m_pFirstBlockWithAvailCells;
pBlock->pPrevBlockWithAvailCells = NULL;
m_pFirstBlockWithAvailCells->pPrevBlockWithAvailCells = pBlock;
m_pFirstBlockWithAvailCells = pBlock;
m_uiBlocksWithAvailCells++;
}
// Adjust counter, because the cell is now considered free
m_uiTotalFreeCells++;
// If this block is now totally avail
if( pBlock->ui32AvailCellCount == m_uiCellsPerBlock)
{
flmAssert( !pBlock->ui32AllocatedCells);
// If we have met our threshold for being able to free a block
if( m_uiTotalFreeCells >= m_uiCellsPerBlock || bFreeIfEmpty)
{
freeBlock( pBlock);
if( pbFreedBlock)
{
*pbFreedBlock = TRUE;
}
}
else if( pBlock != m_pFirstBlockWithAvailCells)
{
// Link the block to the front of the avail list so that
// it can be freed quickly at some point in the future
if( pBlock->pPrevBlockWithAvailCells)
{
pBlock->pPrevBlockWithAvailCells->pNextBlockWithAvailCells =
pBlock->pNextBlockWithAvailCells;
}
if( pBlock->pNextBlockWithAvailCells)
{
pBlock->pNextBlockWithAvailCells->pPrevBlockWithAvailCells =
pBlock->pPrevBlockWithAvailCells;
}
else
{
flmAssert( m_pLastBlockWithAvailCells == pBlock);
m_pLastBlockWithAvailCells = pBlock->pPrevBlockWithAvailCells;
}
if( m_pFirstBlockWithAvailCells)
{
m_pFirstBlockWithAvailCells->pPrevBlockWithAvailCells = pBlock;
}
pBlock->pPrevBlockWithAvailCells = NULL;
pBlock->pNextBlockWithAvailCells = m_pFirstBlockWithAvailCells;
m_pFirstBlockWithAvailCells = pBlock;
}
}
m_uiAllocatedCells--;
m_uiEverFreedCells++;
if( bUnlockMutex)
{
f_mutexUnlock( *m_phMutex);
}
}
/****************************************************************************
Desc: Grabs another slab of memory from the operating system
****************************************************************************/
F_FixedAlloc::BLOCK * F_FixedAlloc::getAnotherBlock( void)
{
BLOCK * pBlock = NULL;
if( RC_BAD( m_pSlabManager->allocSlab( (void **)&pBlock)))
{
goto Exit;
}
if (m_puiTotalBytesAllocated)
{
(*m_puiTotalBytesAllocated) += m_uiSlabSize;
}
// Initialize the block header fields
f_memset( pBlock, 0, sizeof( BLOCK));
pBlock->pvAllocator = (void *)this;
m_uiAllocatedSlabs++;
Exit:
return( pBlock);
}
/****************************************************************************
Desc: Private internal method to free an unused empty block back to the OS.
****************************************************************************/
void F_FixedAlloc::freeBlock(
BLOCK * pBlock)
{
flmAssert( pBlock);
flmAssert( !pBlock->ui32AllocatedCells);
flmAssert( pBlock->pvAllocator == this);
// Unlink from all-blocks-list
if( pBlock->pNext)
{
pBlock->pNext->pPrev = pBlock->pPrev;
}
else
{
m_pLastBlock = pBlock->pPrev;
}
if( pBlock->pPrev)
{
pBlock->pPrev->pNext = pBlock->pNext;
}
else
{
m_pFirstBlock = pBlock->pNext;
}
// Unlink from blocks-with-avail-cells list
if( pBlock->pNextBlockWithAvailCells)
{
pBlock->pNextBlockWithAvailCells->pPrevBlockWithAvailCells =
pBlock->pPrevBlockWithAvailCells;
}
else
{
m_pLastBlockWithAvailCells = pBlock->pPrevBlockWithAvailCells;
}
if( pBlock->pPrevBlockWithAvailCells)
{
pBlock->pPrevBlockWithAvailCells->pNextBlockWithAvailCells =
pBlock->pNextBlockWithAvailCells;
}
else
{
m_pFirstBlockWithAvailCells = pBlock->pNextBlockWithAvailCells;
}
flmAssert( m_uiBlocksWithAvailCells);
m_uiBlocksWithAvailCells--;
flmAssert( m_uiTotalFreeCells >= pBlock->ui32AvailCellCount);
m_uiTotalFreeCells -= pBlock->ui32AvailCellCount;
m_uiAllocatedSlabs--;
if (m_puiTotalBytesAllocated)
{
flmAssert( *m_puiTotalBytesAllocated >= m_uiSlabSize);
(*m_puiTotalBytesAllocated) -= m_uiSlabSize;
}
m_pSlabManager->freeSlab( (void **)&pBlock);
}
/****************************************************************************
Desc: Public method to free all the memory in the system.
****************************************************************************/
void F_FixedAlloc::freeAll( void)
{
BLOCK * pFreeMe;
if( m_phMutex)
{
f_mutexLock( *m_phMutex);
}
while( m_pFirstBlock)
{
pFreeMe = m_pFirstBlock;
m_pFirstBlock = m_pFirstBlock->pNext;
freeBlock( pFreeMe);
}
flmAssert( !m_uiTotalFreeCells);
m_pFirstBlock = NULL;
m_pLastBlock = NULL;
m_pFirstBlockWithAvailCells = NULL;
m_pLastBlockWithAvailCells = NULL;
m_uiBlocksWithAvailCells = 0;
m_bAvailListSorted = TRUE;
m_uiTotalFreeCells = 0;
m_uiAllocatedSlabs = 0;
m_uiAllocatedCells = 0;
if( m_phMutex)
{
f_mutexUnlock( *m_phMutex);
}
}
/****************************************************************************
Desc:
****************************************************************************/
void F_FixedAlloc::getStats(
FLM_ALLOC_USAGE * pUsage)
{
f_memset( pUsage, 0, sizeof( FLM_ALLOC_USAGE));
if( m_phMutex)
{
f_mutexLock( *m_phMutex);
}
pUsage->ui64Slabs = m_uiAllocatedSlabs;
pUsage->ui64SlabBytes = m_uiAllocatedSlabs * m_uiSlabSize;
pUsage->ui64AllocatedCells = m_uiAllocatedCells;
pUsage->ui64FreeCells = m_uiTotalFreeCells;
if( m_phMutex)
{
f_mutexUnlock( *m_phMutex);
}
}
/****************************************************************************
Desc: If a relocation callback function has been registered, and memory
can be compressed, the avail list will be compressed
****************************************************************************/
void F_FixedAlloc::defragmentMemory( void)
{
BLOCK * pCurBlock;
BLOCK * pPrevSib;
CELLHEADER * pCellHeader;
FLMBOOL bBlockFreed;
FLMBYTE * pucOriginal;
FLMBYTE * pucReloc = NULL;
FLMUINT uiLoop;
BLOCK ** pSortBuf = NULL;
FLMUINT uiMaxSortEntries;
FLMUINT uiSortEntries = 0;
#define SMALL_SORT_BUF_SIZE 256
BLOCK * smallSortBuf[ SMALL_SORT_BUF_SIZE];
if( m_phMutex)
{
f_mutexLock( *m_phMutex);
}
if( !m_fnRelocate || m_uiTotalFreeCells < m_uiCellsPerBlock)
{
goto Exit;
}
uiMaxSortEntries = m_uiBlocksWithAvailCells;
// Re-sort the blocks in the avail list according to
// their memory addresses to help reduce logical fragmentation
if( !m_bAvailListSorted && uiMaxSortEntries > 1)
{
if( uiMaxSortEntries <= SMALL_SORT_BUF_SIZE)
{
pSortBuf = smallSortBuf;
}
else
{
if( RC_BAD( f_alloc( uiMaxSortEntries * sizeof( BLOCK *), &pSortBuf)))
{
goto Exit;
}
}
pCurBlock = m_pFirstBlockWithAvailCells;
while( pCurBlock)
{
flmAssert( uiSortEntries != uiMaxSortEntries);
pSortBuf[ uiSortEntries++] = pCurBlock;
pCurBlock = pCurBlock->pNextBlockWithAvailCells;
}
// Quick sort
flmAssert( uiSortEntries);
f_qsort( (FLMBYTE *)pSortBuf, 0, uiSortEntries - 1,
F_FixedAlloc::blockAddrCompareFunc,
F_FixedAlloc::blockAddrSwapFunc);
// Re-link the items in the list according to the new
// sort order
m_pFirstBlockWithAvailCells = NULL;
m_pLastBlockWithAvailCells = NULL;
pCurBlock = NULL;
pPrevSib = NULL;
for( uiLoop = 0; uiLoop < uiSortEntries; uiLoop++)
{
pCurBlock = pSortBuf[ uiLoop];
pCurBlock->pNextBlockWithAvailCells = NULL;
pCurBlock->pPrevBlockWithAvailCells = NULL;
if( pPrevSib)
{
pCurBlock->pPrevBlockWithAvailCells = pPrevSib;
pPrevSib->pNextBlockWithAvailCells = pCurBlock;
}
else
{
m_pFirstBlockWithAvailCells = pCurBlock;
}
pPrevSib = pCurBlock;
}
m_pLastBlockWithAvailCells = pCurBlock;
m_bAvailListSorted = TRUE;
}
// Process the avail list (which should be sorted unless
// we are too low on memory)
pCurBlock = m_pLastBlockWithAvailCells;
while( pCurBlock)
{
if( m_uiTotalFreeCells < m_uiCellsPerBlock)
{
// No need to continue ... we aren't above the
// free cell threshold
goto Exit;
}
pPrevSib = pCurBlock->pPrevBlockWithAvailCells;
if( pCurBlock == m_pFirstBlockWithAvailCells ||
!pCurBlock->ui32AvailCellCount)
{
// We've either hit the beginning of the avail list or
// the block that we are now positioned on has been
// removed from the avail list. In either case,
// we are done.
break;
}
if( pCurBlock->ui32AvailCellCount == m_uiCellsPerBlock ||
pCurBlock->ui32NextNeverUsedCell == pCurBlock->ui32AvailCellCount)
{
freeBlock( pCurBlock);
pCurBlock = pPrevSib;
continue;
}
for( uiLoop = 0; uiLoop < pCurBlock->ui32NextNeverUsedCell &&
pCurBlock != m_pFirstBlockWithAvailCells &&
m_uiTotalFreeCells >= m_uiCellsPerBlock; uiLoop++)
{
pCellHeader = (CELLHEADER *)
((FLMBYTE *)pCurBlock + m_uiBlockHeaderSize +
(uiLoop * m_uiSizeOfCellAndHeader));
if( pCellHeader->pContainingBlock)
{
// If pContainingBlock is non-NULL, the cell is currently allocated
flmAssert( pCellHeader->pContainingBlock == pCurBlock);
pucOriginal = ((FLMBYTE *)pCellHeader + m_uiCellHeaderSize);
if( !m_fnCanRelocate || m_fnCanRelocate( pucOriginal))
{
if( (pucReloc = (FLMBYTE *)getCell()) == NULL)
{
goto Exit;
}
f_memcpy( pucReloc, pucOriginal, m_uiCellSize);
m_fnRelocate( pucOriginal, pucReloc);
freeCell( pucOriginal, TRUE, TRUE, &bBlockFreed);
if( bBlockFreed)
{
break;
}
}
}
}
pCurBlock = pPrevSib;
}
Exit:
if( m_phMutex)
{
f_mutexUnlock( *m_phMutex);
}
if( pSortBuf && pSortBuf != smallSortBuf)
{
f_free( &pSortBuf);
}
}
/****************************************************************************
Desc:
****************************************************************************/
void F_FixedAlloc::incrementTotalBytesAllocated(
FLMUINT uiCount)
{
if( m_puiTotalBytesAllocated)
{
if( m_phMutex)
{
f_mutexLock( *m_phMutex);
}
*m_puiTotalBytesAllocated += uiCount;
if( m_phMutex)
{
f_mutexUnlock( *m_phMutex);
}
}
}
/****************************************************************************
Desc:
****************************************************************************/
FLMUINT F_FixedAlloc::getTotalBytesAllocated( void)
{
FLMUINT uiTotal = 0;
if( m_puiTotalBytesAllocated)
{
if( m_phMutex)
{
f_mutexLock( *m_phMutex);
}
uiTotal = *m_puiTotalBytesAllocated;
if( m_phMutex)
{
f_mutexUnlock( *m_phMutex);
}
}
return( uiTotal);
}
/****************************************************************************
Desc:
****************************************************************************/
void F_FixedAlloc::decrementTotalBytesAllocated(
FLMUINT uiCount)
{
if( m_puiTotalBytesAllocated)
{
if( m_phMutex)
{
f_mutexLock( *m_phMutex);
}
flmAssert( *m_puiTotalBytesAllocated >= uiCount);
*m_puiTotalBytesAllocated -= uiCount;
if( m_phMutex)
{
f_mutexUnlock( *m_phMutex);
}
}
}
/****************************************************************************
Desc:
****************************************************************************/
void F_FixedAlloc::freeUnused( void)
{
BLOCK * pBlock;
if( m_phMutex)
{
f_mutexLock( *m_phMutex);
}
if( (pBlock = m_pFirstBlockWithAvailCells) != NULL &&
!pBlock->ui32AllocatedCells)
{
freeBlock( pBlock);
}
if( (pBlock = m_pFirstBlock) != NULL &&
!pBlock->ui32AllocatedCells)
{
freeBlock( pBlock);
}
if( m_phMutex)
{
f_mutexUnlock( *m_phMutex);
}
}
/****************************************************************************
Desc: Debug method to do mem leak testing. Any cells allocated via
allocCell but not freed via freeCell() will be triggered here.
****************************************************************************/
#ifdef FLM_DEBUG
void F_FixedAlloc::testForLeaks( void)
{
BLOCK * pBlockRover = m_pFirstBlock;
CELLHEADER * pHeader;
FLMUINT uiLoop;
F_MEM_HDR memHeader;
// Test for leaks
while( pBlockRover)
{
for( uiLoop = 0; uiLoop < pBlockRover->ui32NextNeverUsedCell; uiLoop++)
{
pHeader = (CELLHEADER*)
((FLMBYTE*)pBlockRover + m_uiBlockHeaderSize +
(uiLoop * m_uiSizeOfCellAndHeader));
// Nonzero here means we have a leak
if( pHeader->pContainingBlock)
{
// We have a leak, so let's call logMemLeak with the
// appropriate header passed in
f_memset( &memHeader, 0, sizeof( F_MEM_HDR));
memHeader.uiDataSize = m_uiCellSize;
memHeader.puiStack = pHeader->puiStack;
logMemLeak( &memHeader);
}
}
pBlockRover = pBlockRover->pNext;
}
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
F_BufferAlloc::~F_BufferAlloc()
{
FLMUINT uiLoop;
if( m_phMutex)
{
f_mutexLock( *m_phMutex);
}
for (uiLoop = 0; uiLoop < NUM_BUF_ALLOCATORS; uiLoop++)
{
if( m_ppAllocators[ uiLoop])
{
m_ppAllocators[ uiLoop]->Release();
m_ppAllocators[ uiLoop] = NULL;
}
}
if( m_pSlabManager)
{
m_pSlabManager->Release();
}
if( m_phMutex)
{
f_mutexUnlock( *m_phMutex);
}
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_BufferAlloc::setup(
F_SlabManager * pSlabManager,
F_MUTEX * phMutex,
FLMUINT * puiTotalBytesAllocated)
{
RCODE rc = FERR_OK;
FLMUINT uiLoop;
FLMUINT uiSize;
flmAssert( pSlabManager);
m_pSlabManager = pSlabManager;
m_pSlabManager->AddRef();
m_puiTotalBytesAllocated = puiTotalBytesAllocated;
for( uiLoop = 0; uiLoop < NUM_BUF_ALLOCATORS; uiLoop++)
{
if( (m_ppAllocators[ uiLoop] = f_new F_FixedAlloc) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
switch (uiLoop)
{
case 0:
uiSize = CELL_SIZE_0;
break;
case 1:
uiSize = CELL_SIZE_1;
break;
case 2:
uiSize = CELL_SIZE_2;
break;
case 3:
uiSize = CELL_SIZE_3;
break;
case 4:
uiSize = CELL_SIZE_4;
break;
case 5:
uiSize = CELL_SIZE_5;
break;
case 6:
uiSize = CELL_SIZE_6;
break;
case 7:
uiSize = CELL_SIZE_7;
break;
case 8:
uiSize = CELL_SIZE_8;
break;
case 9:
uiSize = CELL_SIZE_9;
break;
case 10:
uiSize = CELL_SIZE_10;
break;
case 11:
uiSize = CELL_SIZE_11;
break;
case 12:
uiSize = CELL_SIZE_12;
break;
case 13:
uiSize = CELL_SIZE_13;
break;
case 14:
uiSize = CELL_SIZE_14;
break;
case 15:
uiSize = CELL_SIZE_15;
break;
case 16:
uiSize = CELL_SIZE_16;
break;
case 17:
uiSize = CELL_SIZE_17;
break;
case 18:
uiSize = CELL_SIZE_18;
break;
case 19:
uiSize = CELL_SIZE_19;
break;
case 20:
uiSize = CELL_SIZE_20;
break;
case 21:
uiSize = CELL_SIZE_21;
break;
default:
uiSize = 0;
flmAssert( 0);
break;
}
if (RC_BAD( rc = m_ppAllocators[ uiLoop]->setup(
pSlabManager, (FLMBOOL)FALSE, uiSize, puiTotalBytesAllocated)))
{
goto Exit;
}
}
m_phMutex = phMutex;
Exit:
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
void F_BufferAlloc::setRelocationFuncs(
FLM_CAN_RELOC_FUNC fnCanRelocate,
FLM_RELOC_FUNC fnRelocate)
{
FLMUINT uiLoop;
flmAssert( fnCanRelocate);
flmAssert( fnRelocate);
if( m_phMutex)
{
f_mutexLock( *m_phMutex);
}
for( uiLoop = 0; uiLoop < NUM_BUF_ALLOCATORS; uiLoop++)
{
if( m_ppAllocators[ uiLoop])
{
m_ppAllocators[ uiLoop]->setRelocationFuncs(
fnCanRelocate, fnRelocate);
}
uiLoop++;
}
if( m_phMutex)
{
f_mutexUnlock( *m_phMutex);
}
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_BufferAlloc::allocBuf(
FLMUINT uiSize,
void * pvInitialData,
FLMUINT uiDataSize,
FLMBYTE ** ppucBuffer,
FLMBOOL * pbAllocatedOnHeap)
{
RCODE rc = FERR_OK;
F_FixedAlloc * pAllocator = getAllocator( uiSize);
if( m_phMutex)
{
f_mutexLock( *m_phMutex);
}
if( pbAllocatedOnHeap)
{
*pbAllocatedOnHeap = FALSE;
}
if( pAllocator)
{
flmAssert( pAllocator->getCellSize() >= uiSize);
if( (*ppucBuffer = (FLMBYTE *)pAllocator->allocCell(
pvInitialData, uiDataSize)) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
}
else
{
if( RC_BAD( rc = f_alloc( uiSize, ppucBuffer)))
{
goto Exit;
}
if( m_puiTotalBytesAllocated)
{
(*m_puiTotalBytesAllocated) += f_msize( *ppucBuffer);
}
if( pvInitialData)
{
f_memcpy( *ppucBuffer, pvInitialData, uiDataSize);
}
if( pbAllocatedOnHeap)
{
*pbAllocatedOnHeap = TRUE;
}
}
Exit:
if( m_phMutex)
{
f_mutexUnlock( *m_phMutex);
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_BufferAlloc::reallocBuf(
FLMUINT uiOldSize,
FLMUINT uiNewSize,
void * pvInitialData,
FLMUINT uiDataSize,
FLMBYTE ** ppucBuffer,
FLMBOOL * pbAllocatedOnHeap)
{
RCODE rc = FERR_OK;
FLMBYTE * pucTmp;
F_FixedAlloc * pOldAllocator;
F_FixedAlloc * pNewAllocator;
FLMBOOL bMutexLocked = FALSE;
flmAssert( uiNewSize);
if( !uiOldSize)
{
rc = allocBuf( uiNewSize, pvInitialData, uiDataSize,
ppucBuffer, pbAllocatedOnHeap);
goto Exit;
}
if( m_phMutex)
{
f_mutexLock( *m_phMutex);
bMutexLocked = TRUE;
}
if( pbAllocatedOnHeap)
{
*pbAllocatedOnHeap = FALSE;
}
pOldAllocator = getAllocator( uiOldSize);
pNewAllocator = getAllocator( uiNewSize);
if( pOldAllocator)
{
if( pNewAllocator)
{
if( pOldAllocator == pNewAllocator)
{
// The allocation will still fit in the same cell
goto Exit;
}
if( (pucTmp = (FLMBYTE *)pNewAllocator->allocCell()) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
}
else
{
if( RC_BAD( rc = f_alloc( uiNewSize, &pucTmp)))
{
goto Exit;
}
if( m_puiTotalBytesAllocated)
{
(*m_puiTotalBytesAllocated) += f_msize( pucTmp);
}
if( pbAllocatedOnHeap)
{
*pbAllocatedOnHeap = TRUE;
}
}
f_memcpy( pucTmp, *ppucBuffer, f_min( uiOldSize, uiNewSize));
pOldAllocator->freeCell( *ppucBuffer);
*ppucBuffer = pucTmp;
}
else
{
if( pNewAllocator)
{
if( m_puiTotalBytesAllocated)
{
FLMUINT uiAllocSize = f_msize( *ppucBuffer);
flmAssert( *m_puiTotalBytesAllocated >= uiAllocSize);
(*m_puiTotalBytesAllocated) -= uiAllocSize;
}
if( (pucTmp = (FLMBYTE *)pNewAllocator->allocCell(
*ppucBuffer, f_min( uiOldSize, uiNewSize))) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
f_free( ppucBuffer);
*ppucBuffer = pucTmp;
}
else
{
FLMUINT uiAllocSize = f_msize( *ppucBuffer);
flmAssert( uiOldSize > m_ppAllocators[ NUM_BUF_ALLOCATORS - 1]->getCellSize());
flmAssert( uiNewSize > m_ppAllocators[ NUM_BUF_ALLOCATORS - 1]->getCellSize());
if( RC_BAD( rc = f_realloc( uiNewSize, ppucBuffer)))
{
goto Exit;
}
if( m_puiTotalBytesAllocated)
{
flmAssert( *m_puiTotalBytesAllocated >= uiAllocSize);
(*m_puiTotalBytesAllocated) -= uiAllocSize;
(*m_puiTotalBytesAllocated) += f_msize( *ppucBuffer);
}
if( pbAllocatedOnHeap)
{
*pbAllocatedOnHeap = TRUE;
}
}
}
Exit:
if( bMutexLocked)
{
f_mutexUnlock( *m_phMutex);
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
void F_BufferAlloc::freeBuf(
FLMUINT uiSize,
FLMBYTE ** ppucBuffer)
{
F_FixedAlloc * pAllocator = getAllocator( uiSize);
if( m_phMutex)
{
f_mutexLock( *m_phMutex);
}
if( pAllocator)
{
pAllocator->freeCell( *ppucBuffer, FALSE, TRUE, NULL);
*ppucBuffer = NULL;
}
else
{
if( m_puiTotalBytesAllocated)
{
FLMUINT uiAllocSize = f_msize( *ppucBuffer);
flmAssert( *m_puiTotalBytesAllocated >= uiAllocSize);
(*m_puiTotalBytesAllocated) -= uiAllocSize;
}
f_free( ppucBuffer);
}
if( m_phMutex)
{
f_mutexUnlock( *m_phMutex);
}
}
/****************************************************************************
Desc:
****************************************************************************/
void F_BufferAlloc::defragmentMemory( void)
{
FLMUINT uiLoop;
if( m_phMutex)
{
f_mutexLock( *m_phMutex);
}
for( uiLoop = 0; uiLoop < NUM_BUF_ALLOCATORS; uiLoop++)
{
if( m_ppAllocators[ uiLoop])
{
m_ppAllocators[ uiLoop]->defragmentMemory();
m_ppAllocators[ uiLoop]->freeUnused();
}
uiLoop++;
}
if( m_phMutex)
{
f_mutexUnlock( *m_phMutex);
}
}
/****************************************************************************
Desc:
****************************************************************************/
FLMUINT F_BufferAlloc::getTrueSize(
FLMUINT uiSize,
FLMBYTE * pucBuffer)
{
FLMUINT uiTrueSize;
F_FixedAlloc * pAllocator;
if( !uiSize)
{
uiTrueSize = 0;
}
else if( (pAllocator = getAllocator( uiSize)) != NULL)
{
uiTrueSize = pAllocator->getCellSize();
}
else
{
uiTrueSize = f_msize( pucBuffer);
}
return( uiTrueSize);
}
/****************************************************************************
Desc:
****************************************************************************/
F_FixedAlloc * F_BufferAlloc::getAllocator(
FLMUINT uiSize)
{
F_FixedAlloc * pAllocator;
flmAssert( uiSize);
if( uiSize <= CELL_SIZE_10)
{
if( uiSize <= CELL_SIZE_4)
{
if( uiSize <= CELL_SIZE_2)
{
if( uiSize <= CELL_SIZE_0)
{
pAllocator = (F_FixedAlloc *)m_ppAllocators [0];
}
else
{
pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_1
? m_ppAllocators [1]
: m_ppAllocators [2]);
}
}
else
{
pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_3
? m_ppAllocators [3]
: m_ppAllocators [4]);
}
}
else if( uiSize <= CELL_SIZE_7)
{
if( uiSize <= CELL_SIZE_5)
{
pAllocator = (F_FixedAlloc *)m_ppAllocators [5];
}
else
{
pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_6
? m_ppAllocators [6]
: m_ppAllocators [7]);
}
}
else
{
if( uiSize <= CELL_SIZE_8)
{
pAllocator = (F_FixedAlloc *)m_ppAllocators [8];
}
else
{
pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_9
? m_ppAllocators [9]
: m_ppAllocators [10]);
}
}
}
else if( uiSize <= CELL_SIZE_16)
{
if( uiSize <= CELL_SIZE_13)
{
if( uiSize <= CELL_SIZE_11)
{
pAllocator = (F_FixedAlloc *)m_ppAllocators [11];
}
else
{
pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_12
? m_ppAllocators [12]
: m_ppAllocators [13]);
}
}
else
{
if( uiSize <= CELL_SIZE_14)
{
pAllocator = (F_FixedAlloc *)m_ppAllocators [14];
}
else
{
pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_15
? m_ppAllocators [15]
: m_ppAllocators [16]);
}
}
}
else if( uiSize <= CELL_SIZE_19)
{
if( uiSize <= CELL_SIZE_17)
{
pAllocator = (F_FixedAlloc *)m_ppAllocators [17];
}
else
{
pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_18
? m_ppAllocators [18]
: m_ppAllocators [19]);
}
}
else if( uiSize <= CELL_SIZE_21)
{
pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_20
? m_ppAllocators [20]
: m_ppAllocators [21]);
}
else
{
pAllocator = NULL;
}
return( pAllocator);
}