blob: b86769cb8474521638b49ed6b2278c673ed81b70 [file] [log] [blame]
/* ------------------------------------------------------------------
* Copyright (C) 1998-2009 PacketVideo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied.
* See the License for the specific language governing permissions
* and limitations under the License.
* -------------------------------------------------------------------
*/
#include "oscl_mem_mempool.h"
#include <utils/Log.h>
/**
* OsclMemPoolFixedChunkAllocator section
**/
OSCL_EXPORT_REF OsclMemPoolFixedChunkAllocator::OsclMemPoolFixedChunkAllocator(const uint32 numchunk, const uint32 chunksize, Oscl_DefAlloc* gen_alloc) :
iNumChunk(1), iChunkSize(0), iChunkSizeMemAligned(0),
iMemPoolAllocator(gen_alloc), iMemPool(NULL),
iCheckNextAvailableFreeChunk(false), iObserver(NULL),
iNextAvailableContextData(NULL),
iRefCount(1),
iEnableNullPtrReturn(false)
{
iNumChunk = numchunk;
iChunkSize = chunksize;
if (iNumChunk == 0)
{
iNumChunk = 1;
}
if (iChunkSize > 0)
{
createmempool();
}
}
OSCL_EXPORT_REF void OsclMemPoolFixedChunkAllocator::enablenullpointerreturn()
{
iEnableNullPtrReturn = true;
}
OSCL_EXPORT_REF void OsclMemPoolFixedChunkAllocator::addRef()
{
// Just increment the ref count
++iRefCount;
}
OSCL_EXPORT_REF void OsclMemPoolFixedChunkAllocator::removeRef()
{
// Decrement the ref count
--iRefCount;
// If ref count reaches 0 then destroy this object automatically
if (iRefCount <= 0)
{
OSCL_DELETE(this);
}
}
OSCL_EXPORT_REF OsclMemPoolFixedChunkAllocator::~OsclMemPoolFixedChunkAllocator()
{
// Decrement the ref count
--iRefCount;
// If ref count reaches 0 then destroy this object
if (iRefCount <= 0)
{
destroymempool();
}
}
OSCL_EXPORT_REF OsclAny* OsclMemPoolFixedChunkAllocator::allocate(const uint32 n)
{
// Create the memory pool if it hasn't been created yet.
// Use the allocation size, n, as the chunk size for memory pool
if (iChunkSize == 0)
{
iChunkSize = n;
createmempool();
}
else if (n > iChunkSize)
{
OSCL_LEAVE(OsclErrArgument);
// OSCL_UNUSED_RETURN(NULL); This statement was removed to avoid compiler warning for Unreachable Code
}
if (iFreeMemChunkList.empty())
{
// No free chunk is available
if (iEnableNullPtrReturn)
{
return NULL;
}
else
{
OSCL_LEAVE(OsclErrNoResources);
}
}
// Return the next available chunk from the pool
OsclAny* freechunk = iFreeMemChunkList.back();
// Remove the chunk from the free list
iFreeMemChunkList.pop_back();
addRef();
return freechunk;
}
OSCL_EXPORT_REF void OsclMemPoolFixedChunkAllocator::deallocate(OsclAny* p)
{
if (iMemPool == NULL)
{
// Memory pool hasn't been allocated yet so error
OSCL_LEAVE(OsclErrNotReady);
}
uint8* ptmp = (uint8*)p;
uint8* mptmp = (uint8*)iMemPool;
if ((ptmp < mptmp) || ptmp >= (mptmp + iNumChunk*iChunkSizeMemAligned))
{
// Returned memory is not part of this memory pool
OSCL_LEAVE(OsclErrArgument);
}
if (((ptmp - mptmp) % iChunkSizeMemAligned) != 0)
{
// Returned memory is not aligned to the chunk.
OSCL_LEAVE(OsclErrArgument);
}
// Put the returned chunk in the free pool
iFreeMemChunkList.push_back(p);
// Notify the observer about free chunk available if waiting for such callback
if (iCheckNextAvailableFreeChunk)
{
iCheckNextAvailableFreeChunk = false;
if (iObserver)
{
iObserver->freechunkavailable(iNextAvailableContextData);
}
}
else if (iObserver && iCheckNextAvailableFreeChunk)
{
LOGW("We may have lost some free chunk %p available notification for memory pool %p with chunk size %d", ptmp, mptmp, iChunkSizeMemAligned);
}
// Decrement the refcount since deallocating succeeded
removeRef();
}
OSCL_EXPORT_REF void OsclMemPoolFixedChunkAllocator::notifyfreechunkavailable(OsclMemPoolFixedChunkAllocatorObserver& obs, OsclAny* aContextData)
{
iCheckNextAvailableFreeChunk = true;
iObserver = &obs;
iNextAvailableContextData = aContextData;
}
OSCL_EXPORT_REF void OsclMemPoolFixedChunkAllocator::CancelFreeChunkAvailableCallback()
{
iCheckNextAvailableFreeChunk = false;
iObserver = NULL;
iNextAvailableContextData = NULL;
}
OSCL_EXPORT_REF void OsclMemPoolFixedChunkAllocator::createmempool()
{
if (iChunkSize == 0 || iNumChunk == 0)
{
OSCL_LEAVE(OsclErrArgument);
}
// Create one block of memory for the memory pool
iChunkSizeMemAligned = oscl_mem_aligned_size(iChunkSize);
int32 leavecode = 0;
if (iMemPoolAllocator)
{
OSCL_TRY(leavecode, iMemPool = iMemPoolAllocator->ALLOCATE(iNumChunk * iChunkSizeMemAligned));
}
else
{
iMemPool = OSCL_MALLOC(iNumChunk * iChunkSizeMemAligned);
}
if (leavecode || iMemPool == NULL)
{
OSCL_LEAVE(OsclErrNoMemory);
}
#if OSCL_MEM_FILL_WITH_PATTERN
oscl_memset(iMemPool, 0x55, iNumChunk*iChunkSizeMemAligned);
#endif
// Set up the free mem chunk list vector
iFreeMemChunkList.reserve(iNumChunk);
uint8* chunkptr = (uint8*)iMemPool;
for (uint32 i = 0; i < iNumChunk; ++i)
{
iFreeMemChunkList.push_back((OsclAny*)chunkptr);
chunkptr += iChunkSizeMemAligned;
}
}
OSCL_EXPORT_REF void OsclMemPoolFixedChunkAllocator::destroymempool()
{
// If ref count reaches 0 then destroy this object
if (iRefCount <= 0)
{
#if OSCL_MEM_CHECK_ALL_MEMPOOL_CHUNKS_ARE_RETURNED
// Assert if all of the chunks were not returned
OSCL_ASSERT(iFreeMemChunkList.size() == iNumChunk);
#endif
iFreeMemChunkList.clear();
if (iMemPool)
{
if (iMemPoolAllocator)
{
iMemPoolAllocator->deallocate(iMemPool);
}
else
{
OSCL_FREE(iMemPool);
}
iMemPool = NULL;
}
}
}
/**
* OsclMemPoolResizableAllocator section
**/
#define OSCLMEMPOOLRESIZABLEALLOCATOR_DEFAULT_NUMBLOCKPERBUFFER 10
#define OSCLMEMPOOLRESIZABLEALLOCATOR_PREFENCE_PATTERN 0x55
#define OSCLMEMPOOLRESIZABLEALLOCATOR_POSTFENCE_PATTERN 0xAA
#define OSCLMEMPOOLRESIZABLEALLOCATOR_MIN_BUFFERSIZE 8
OSCL_EXPORT_REF OsclMemPoolResizableAllocator::OsclMemPoolResizableAllocator(uint32 aMemPoolBufferSize, uint32 aMemPoolBufferNumLimit, uint32 aExpectedNumBlocksPerBuffer, Oscl_DefAlloc* gen_alloc) :
iMemPoolBufferSize(aMemPoolBufferSize),
iMemPoolBufferNumLimit(aMemPoolBufferNumLimit),
iExpectedNumBlocksPerBuffer(aExpectedNumBlocksPerBuffer),
iMemPoolBufferAllocator(gen_alloc),
iCheckNextAvailable(false),
iRequestedNextAvailableSize(0),
iNextAvailableContextData(NULL),
iObserver(NULL),
iCheckFreeMemoryAvailable(false),
iRequestedAvailableFreeMemSize(0),
iFreeMemContextData(NULL),
iFreeMemPoolObserver(NULL),
iRefCount(1),
iEnableNullPtrReturn(false)
{
OSCL_ASSERT(aMemPoolBufferSize > OSCLMEMPOOLRESIZABLEALLOCATOR_MIN_BUFFERSIZE);
iMaxNewMemPoolBufferSz = 0;
// Calculate and save the mem aligned size of buffer and block info header structures
iBufferInfoAlignedSize = oscl_mem_aligned_size(sizeof(MemPoolBufferInfo));
iBlockInfoAlignedSize = oscl_mem_aligned_size(sizeof(MemPoolBlockInfo));
// Pre-allocate memory for vector
if (iMemPoolBufferNumLimit > 0)
{
iMemPoolBufferList.reserve(iMemPoolBufferNumLimit);
}
else
{
iMemPoolBufferList.reserve(2);
}
// Determine the size of memory pool buffer and create one
uint32 buffersize = oscl_mem_aligned_size(iMemPoolBufferSize) + iBufferInfoAlignedSize;
if (iExpectedNumBlocksPerBuffer > 0)
{
buffersize += (iExpectedNumBlocksPerBuffer * iBlockInfoAlignedSize);
}
else
{
buffersize += (OSCLMEMPOOLRESIZABLEALLOCATOR_DEFAULT_NUMBLOCKPERBUFFER * iBlockInfoAlignedSize);
}
addnewmempoolbuffer(buffersize);
}
OSCL_EXPORT_REF void OsclMemPoolResizableAllocator::enablenullpointerreturn()
{
iEnableNullPtrReturn = true;
}
OSCL_EXPORT_REF OsclAny* OsclMemPoolResizableAllocator::allocate(const uint32 aNumBytes)
{
MemPoolBlockInfo* freeblock = NULL;
uint32 alignednumbytes = oscl_mem_aligned_size(aNumBytes);
if (aNumBytes == 0)
{
OSCL_LEAVE(OsclErrArgument);
// OSCL_UNUSED_RETURN(NULL); This statement was removed to avoid compiler warning for Unreachable Code
}
// Find a free block that would accomodate the requested size with a block info header
freeblock = findfreeblock(alignednumbytes + iBlockInfoAlignedSize);
if (freeblock == NULL)
{
//We could not find the new buffer, the only way we can allocate the chunk is by allocating newmempool buffer
//Validate is size is less than the regrow size.
if (iMemPoolBufferNumLimit > 0 && iMaxNewMemPoolBufferSz > 0 && iMaxNewMemPoolBufferSz < alignednumbytes)
{
//cannot create the new buffer
if (iEnableNullPtrReturn)
{
return NULL;
}
else
{
// Leave with resource limitation
OSCL_LEAVE(OsclErrNoResources);
}
}
// Check if the requested size is bigger than the specified buffer size
if (alignednumbytes > iMemPoolBufferSize)
{
// Would need to create a new buffer to accomodate this request
// Check if another buffer can be created
if (iMemPoolBufferNumLimit > 0 && iMemPoolBufferList.size() >= iMemPoolBufferNumLimit)
{
// Check if there is a memory pool buffer that has no outstanding buffers
// If present then remove it so a new one can be added
bool emptybufferfound = false;
for (uint32 j = 0; j < iMemPoolBufferList.size(); ++j)
{
if (iMemPoolBufferList[j]->iNumOutstanding == 0)
{
// Free the memory
if (iMemPoolBufferAllocator)
{
iMemPoolBufferAllocator->deallocate((OsclAny*)iMemPoolBufferList[j]);
}
else
{
OSCL_FREE((OsclAny*)iMemPoolBufferList[j]);
}
// Remove the mempool buffer from the list
iMemPoolBufferList.erase(iMemPoolBufferList.begin() + j);
emptybufferfound = true;
break;
}
}
// Need to leave and return if empty buffer not found
if (!emptybufferfound)
{
if (iEnableNullPtrReturn)
{
return NULL;
}
else
{
// Leave with resource limitation
OSCL_LEAVE(OsclErrNoResources);
}
}
// Continue on to create a new buffer
OSCL_ASSERT(iMemPoolBufferList.size() < iMemPoolBufferNumLimit);
}
// Determine the size of memory pool buffer and create one
uint32 buffersize = alignednumbytes + iBufferInfoAlignedSize;
if (iExpectedNumBlocksPerBuffer > 0)
{
buffersize += (iExpectedNumBlocksPerBuffer * iBlockInfoAlignedSize);
}
else
{
buffersize += (OSCLMEMPOOLRESIZABLEALLOCATOR_DEFAULT_NUMBLOCKPERBUFFER * iBlockInfoAlignedSize);
}
MemPoolBufferInfo* newbuffer = addnewmempoolbuffer(buffersize);
OSCL_ASSERT(newbuffer != NULL);
OSCL_ASSERT(newbuffer->iNextFreeBlock != NULL);
freeblock = (MemPoolBlockInfo*)(newbuffer->iNextFreeBlock);
OSCL_ASSERT(freeblock != NULL);
OSCL_ASSERT(freeblock->iBlockSize >= alignednumbytes);
}
else
{
// Check if another buffer can be created
if (iMemPoolBufferNumLimit > 0 && iMemPoolBufferList.size() >= iMemPoolBufferNumLimit)
{
if (iEnableNullPtrReturn)
{
return NULL;
}
else
{
// Leave with resource limitation
OSCL_LEAVE(OsclErrNoResources);
}
}
// Determine the size of memory pool buffer and create one
uint32 buffersize = oscl_mem_aligned_size(iMemPoolBufferSize) + iBufferInfoAlignedSize;
if (iExpectedNumBlocksPerBuffer > 0)
{
buffersize += (iExpectedNumBlocksPerBuffer * iBlockInfoAlignedSize);
}
else
{
buffersize += (OSCLMEMPOOLRESIZABLEALLOCATOR_DEFAULT_NUMBLOCKPERBUFFER * iBlockInfoAlignedSize);
}
MemPoolBufferInfo* newbuffer = addnewmempoolbuffer(buffersize);
OSCL_ASSERT(newbuffer != NULL);
OSCL_ASSERT(newbuffer->iNextFreeBlock != NULL);
freeblock = (MemPoolBlockInfo*)(newbuffer->iNextFreeBlock);
OSCL_ASSERT(freeblock != NULL);
OSCL_ASSERT(freeblock->iBlockSize >= alignednumbytes);
}
}
// Use the free block and return the buffer pointer
OsclAny* bufptr = allocateblock(*freeblock, alignednumbytes);
if (bufptr)
{
addRef();
++(freeblock->iParentBuffer->iNumOutstanding);
}
return bufptr;
}
OSCL_EXPORT_REF void OsclMemPoolResizableAllocator::deallocate(OsclAny* aPtr)
{
// Check that the returned pointer is from the memory pool
if (validateblock(aPtr) == false)
{
OSCL_LEAVE(OsclErrArgument);
}
// Retrieve the block info header and validate the info
uint8* byteptr = (uint8*)aPtr;
MemPoolBlockInfo* retblock = (MemPoolBlockInfo*)(byteptr - iBlockInfoAlignedSize);
OSCL_ASSERT(retblock != NULL);
OSCL_ASSERT(retblock->iBlockPreFence == OSCLMEMPOOLRESIZABLEALLOCATOR_PREFENCE_PATTERN);
OSCL_ASSERT(retblock->iBlockPostFence == OSCLMEMPOOLRESIZABLEALLOCATOR_POSTFENCE_PATTERN);
// Return the block to the memory pool buffer
deallocateblock(*retblock);
--(retblock->iParentBuffer->iNumOutstanding);
// Check if user needs to be notified when block becomes available
if (iCheckNextAvailable)
{
// Check if user is waiting for certain size
if (iRequestedNextAvailableSize == 0)
{
// No so just make the callback
iCheckNextAvailable = false;
if (iObserver)
{
iObserver->freeblockavailable(iNextAvailableContextData);
}
}
else
{
// Check if the requested size is available now
if (findfreeblock(iRequestedNextAvailableSize + iBlockInfoAlignedSize) != NULL)
{
iCheckNextAvailable = false;
if (iObserver)
{
iObserver->freeblockavailable(iNextAvailableContextData);
}
}
else if (iRequestedNextAvailableSize > iMemPoolBufferSize)
{
// The requested size is bigger than the set buffer size
// Check if there is space to grow the buffer,
if (iMemPoolBufferNumLimit == 0 || iMemPoolBufferList.size() < iMemPoolBufferNumLimit)
{
// Available
iCheckNextAvailable = false;
if (iObserver)
{
iObserver->freeblockavailable(iNextAvailableContextData);
}
}
else
{
// Not available so see if there is a buffer with
// no outstanding buffers which can be destroyed
// in the next allocate() call.
bool emptybufferfound = false;
for (uint32 j = 0; j < iMemPoolBufferList.size(); ++j)
{
if (iMemPoolBufferList[j]->iNumOutstanding == 0)
{
emptybufferfound = true;
break;
}
}
if (emptybufferfound)
{
iCheckNextAvailable = false;
if (iObserver)
{
iObserver->freeblockavailable(iNextAvailableContextData);
}
}
}
}
}
}
if (iCheckFreeMemoryAvailable)
{
if (iRequestedAvailableFreeMemSize == 0)
{
// No so just make the callback
iCheckFreeMemoryAvailable = false;
if (iFreeMemPoolObserver)
{
iFreeMemPoolObserver->freememoryavailable(iFreeMemContextData);
}
}
else
{
// Check if the requested size is available now
if (getAvailableSize() >= iRequestedAvailableFreeMemSize)
{
iCheckFreeMemoryAvailable = false;
if (iFreeMemPoolObserver)
{
iFreeMemPoolObserver->freememoryavailable(iFreeMemContextData);
}
}
}
}
// Decrement the refcount since deallocating succeeded
removeRef();
}
OSCL_EXPORT_REF bool OsclMemPoolResizableAllocator::trim(OsclAny* aPtr, uint32 aBytesToFree)
{
// Amount to free has to be aligned
uint32 alignedbytestofree = oscl_mem_aligned_size(aBytesToFree);
if (alignedbytestofree > aBytesToFree)
{
// Not aligned so decrease amount to free by one alignment size
alignedbytestofree -= 8;
}
OSCL_ASSERT(alignedbytestofree <= aBytesToFree);
// Check that the returned pointer is from the memory pool
if (validateblock(aPtr) == false)
{
OSCL_LEAVE(OsclErrArgument);
// OSCL_UNUSED_RETURN(false); This statement was removed to avoid compiler warning for Unreachable Code
}
// Retrieve the block info header and validate the info
uint8* byteptr = (uint8*)aPtr;
MemPoolBlockInfo* resizeblock = (MemPoolBlockInfo*)(byteptr - iBlockInfoAlignedSize);
OSCL_ASSERT(resizeblock != NULL);
OSCL_ASSERT(resizeblock->iBlockPreFence == OSCLMEMPOOLRESIZABLEALLOCATOR_PREFENCE_PATTERN);
OSCL_ASSERT(resizeblock->iBlockPostFence == OSCLMEMPOOLRESIZABLEALLOCATOR_POSTFENCE_PATTERN);
if ((resizeblock->iBlockSize - iBlockInfoAlignedSize) < alignedbytestofree)
{
// The bytes to free in the resize is bigger than the original buffer size
OSCL_LEAVE(OsclErrArgument);
// OSCL_UNUSED_RETURN(false); This statement was removed to avoid compiler warning for Unreachable Code
}
if (alignedbytestofree < (iBlockInfoAlignedSize + OSCLMEMPOOLRESIZABLEALLOCATOR_MIN_BUFFERSIZE))
{
// The resizing cannot be done since the amount to free doesn't have
// enough space to put in a block info header plus the minimum buffer for the new free block
// So don't do anything and return
return false;
}
// Create and fill in a block info header for the memory being freed back to memory pool
MemPoolBlockInfo* freeblock = (MemPoolBlockInfo*)((uint8*)resizeblock + resizeblock->iBlockSize - alignedbytestofree);
freeblock->iBlockPreFence = OSCLMEMPOOLRESIZABLEALLOCATOR_PREFENCE_PATTERN;
freeblock->iNextFreeBlock = NULL;
freeblock->iPrevFreeBlock = NULL;
freeblock->iBlockSize = alignedbytestofree;
freeblock->iBlockBuffer = (uint8*)freeblock + iBlockInfoAlignedSize;
freeblock->iParentBuffer = resizeblock->iParentBuffer;
freeblock->iBlockPostFence = OSCLMEMPOOLRESIZABLEALLOCATOR_POSTFENCE_PATTERN;
// Return the free block to the memory pool buffer
deallocateblock(*freeblock);
// Adjust the block info for the block being resized
resizeblock->iBlockSize -= alignedbytestofree;
return true;
}
OSCL_EXPORT_REF void OsclMemPoolResizableAllocator::notifyfreeblockavailable(OsclMemPoolResizableAllocatorObserver& aObserver, uint32 aRequestedSize, OsclAny* aContextData)
{
// Save the parameters for the next deallocate() call
iCheckNextAvailable = true;
iObserver = &aObserver;
iRequestedNextAvailableSize = oscl_mem_aligned_size(aRequestedSize);
iNextAvailableContextData = aContextData;
}
OSCL_EXPORT_REF void OsclMemPoolResizableAllocator::CancelFreeChunkAvailableCallback()
{
iCheckNextAvailable = false;
iObserver = NULL;
iRequestedNextAvailableSize = 0;
iNextAvailableContextData = NULL;
}
OSCL_EXPORT_REF void OsclMemPoolResizableAllocator::notifyfreememoryavailable(OsclMemPoolResizableAllocatorMemoryObserver& aObserver, uint32 aRequestedSize, OsclAny* aContextData)
{
// Save the parameters for the next deallocate() call
iCheckFreeMemoryAvailable = true;
iFreeMemPoolObserver = &aObserver;
iRequestedAvailableFreeMemSize = oscl_mem_aligned_size(aRequestedSize);
iFreeMemContextData = aContextData;
}
OSCL_EXPORT_REF void OsclMemPoolResizableAllocator::CancelFreeMemoryAvailableCallback()
{
iCheckFreeMemoryAvailable = false;
iFreeMemPoolObserver = NULL;
iRequestedAvailableFreeMemSize = 0;
iFreeMemContextData = NULL;
}
OSCL_EXPORT_REF void OsclMemPoolResizableAllocator::addRef()
{
// Just increment the ref count
++iRefCount;
}
OSCL_EXPORT_REF void OsclMemPoolResizableAllocator::removeRef()
{
// Decrement the ref count
--iRefCount;
// If ref count reaches 0 then destroy this object automatically
if (iRefCount <= 0)
{
OSCL_DELETE(this);
}
}
OSCL_EXPORT_REF OsclMemPoolResizableAllocator::~OsclMemPoolResizableAllocator()
{
destroyallmempoolbuffers();
}
OsclMemPoolResizableAllocator::MemPoolBufferInfo* OsclMemPoolResizableAllocator::addnewmempoolbuffer(uint32 aBufferAlignedSize)
{
OSCL_ASSERT(aBufferAlignedSize > 0);
OSCL_ASSERT(aBufferAlignedSize == oscl_mem_aligned_size(aBufferAlignedSize));
// Allocate memory for one buffer
uint8* newbuffer = NULL;
if (iMemPoolBufferAllocator)
{
// Use the outside allocator
newbuffer = (uint8*)iMemPoolBufferAllocator->ALLOCATE(aBufferAlignedSize);
}
else
{
// Allocate directly from heap
newbuffer = (uint8*)OSCL_MALLOC(aBufferAlignedSize);
}
if (newbuffer == NULL)
{
OSCL_LEAVE(OsclErrNoMemory);
// OSCL_UNUSED_RETURN(NULL); This statement was removed to avoid compiler warning for Unreachable Code
}
#if OSCL_MEM_FILL_WITH_PATTERN
oscl_memset(newbuffer, 0x55, aBufferAlignedSize);
#endif
// Fill in the buffer info header
MemPoolBufferInfo* newbufferinfo = (MemPoolBufferInfo*)newbuffer;
newbufferinfo->iBufferPreFence = OSCLMEMPOOLRESIZABLEALLOCATOR_PREFENCE_PATTERN;
newbufferinfo->iStartAddr = (OsclAny*)(newbuffer + iBufferInfoAlignedSize);
newbufferinfo->iEndAddr = (OsclAny*)(newbuffer + aBufferAlignedSize - 1);
newbufferinfo->iBufferSize = aBufferAlignedSize;
newbufferinfo->iNumOutstanding = 0;
newbufferinfo->iNextFreeBlock = (MemPoolBlockInfo*)(newbufferinfo->iStartAddr);
newbufferinfo->iAllocatedSz = 0;
newbufferinfo->iBufferPostFence = OSCLMEMPOOLRESIZABLEALLOCATOR_POSTFENCE_PATTERN;
// Put in one free block in the new buffer
MemPoolBlockInfo* freeblockinfo = (MemPoolBlockInfo*)(newbufferinfo->iStartAddr);
freeblockinfo->iBlockPreFence = OSCLMEMPOOLRESIZABLEALLOCATOR_PREFENCE_PATTERN;
freeblockinfo->iNextFreeBlock = NULL;
freeblockinfo->iPrevFreeBlock = NULL;
freeblockinfo->iBlockSize = aBufferAlignedSize - iBufferInfoAlignedSize;
freeblockinfo->iBlockBuffer = (uint8*)freeblockinfo + iBlockInfoAlignedSize;
freeblockinfo->iParentBuffer = newbufferinfo;
freeblockinfo->iBlockPostFence = OSCLMEMPOOLRESIZABLEALLOCATOR_POSTFENCE_PATTERN;
// Add the new buffer to the list
iMemPoolBufferList.push_front(newbufferinfo);
return newbufferinfo;
}
void OsclMemPoolResizableAllocator::destroyallmempoolbuffers()
{
while (iMemPoolBufferList.empty() == false)
{
MemPoolBufferInfo* bufferinfo = iMemPoolBufferList[0];
// Check the buffer
OSCL_ASSERT(bufferinfo != NULL);
OSCL_ASSERT(bufferinfo->iBufferPreFence == OSCLMEMPOOLRESIZABLEALLOCATOR_PREFENCE_PATTERN);
OSCL_ASSERT(bufferinfo->iBufferPostFence == OSCLMEMPOOLRESIZABLEALLOCATOR_POSTFENCE_PATTERN);
OSCL_ASSERT(bufferinfo->iNumOutstanding == 0);
// Free the memory
if (iMemPoolBufferAllocator)
{
iMemPoolBufferAllocator->deallocate((OsclAny*)bufferinfo);
}
else
{
OSCL_FREE((OsclAny*)bufferinfo);
}
iMemPoolBufferList.erase(iMemPoolBufferList.begin());
}
}
OsclMemPoolResizableAllocator::MemPoolBlockInfo* OsclMemPoolResizableAllocator::findfreeblock(uint32 aBlockAlignedSize)
{
OSCL_ASSERT(aBlockAlignedSize > 0);
OSCL_ASSERT(aBlockAlignedSize == oscl_mem_aligned_size(aBlockAlignedSize));
// Go through each mempool buffer and return the first free block that
// is bigger than the specified size
if (aBlockAlignedSize == 0)
{
// Request should be non-zero
OSCL_LEAVE(OsclErrArgument);
// OSCL_UNUSED_RETURN(NULL); This statement was removed to avoid compiler warning for Unreachable Code
}
for (uint32 i = 0; i < iMemPoolBufferList.size(); ++i)
{
MemPoolBufferInfo* bufferinfo = iMemPoolBufferList[i];
MemPoolBlockInfo* blockinfo = bufferinfo->iNextFreeBlock;
while (blockinfo != NULL)
{
if ((blockinfo->iBlockSize/* - iBlockInfoAlignedSize*/) >= aBlockAlignedSize)
{
// This free block fits the request
return blockinfo;
}
// Go to the next free block
blockinfo = blockinfo->iNextFreeBlock;
}
}
return NULL;
}
OsclAny* OsclMemPoolResizableAllocator::allocateblock(MemPoolBlockInfo& aBlockPtr, uint32 aNumAlignedBytes)
{
OSCL_ASSERT(aNumAlignedBytes > 0);
OSCL_ASSERT(aNumAlignedBytes == oscl_mem_aligned_size(aNumAlignedBytes));
if (aNumAlignedBytes == 0)
{
OSCL_LEAVE(OsclErrArgument);
// OSCL_UNUSED_RETURN(NULL); This statement was removed to avoid compiler warning for Unreachable Code
}
// Remove the free block from the double linked list
if (aBlockPtr.iPrevFreeBlock == NULL && aBlockPtr.iNextFreeBlock != NULL)
{
// Removing from the beginning of the free list
aBlockPtr.iNextFreeBlock->iPrevFreeBlock = NULL;
aBlockPtr.iParentBuffer->iNextFreeBlock = aBlockPtr.iNextFreeBlock;
}
else if (aBlockPtr.iPrevFreeBlock != NULL && aBlockPtr.iNextFreeBlock == NULL)
{
// Removing from the end of the free list
aBlockPtr.iPrevFreeBlock->iNextFreeBlock = NULL;
}
else if (aBlockPtr.iPrevFreeBlock == NULL && aBlockPtr.iNextFreeBlock == NULL)
{
// Free list becomes empty so update the parent buffer's link
aBlockPtr.iParentBuffer->iNextFreeBlock = NULL;
}
else
{
// Removing from middle of the free list
aBlockPtr.iPrevFreeBlock->iNextFreeBlock = aBlockPtr.iNextFreeBlock;
aBlockPtr.iNextFreeBlock->iPrevFreeBlock = aBlockPtr.iPrevFreeBlock;
}
aBlockPtr.iNextFreeBlock = NULL;
aBlockPtr.iPrevFreeBlock = NULL;
aBlockPtr.iParentBuffer->iAllocatedSz += aBlockPtr.iBlockSize;
// Resize the block if too large
uint32 extraspace = aBlockPtr.iBlockSize - iBlockInfoAlignedSize - aNumAlignedBytes;
if (extraspace > (iBlockInfoAlignedSize + OSCLMEMPOOLRESIZABLEALLOCATOR_MIN_BUFFERSIZE))
{
trim(aBlockPtr.iBlockBuffer, extraspace);
}
#if OSCL_MEM_FILL_WITH_PATTERN
oscl_memset(aBlockPtr.iBlockBuffer, 0x55, (aBlockPtr.iBlockSize - iBlockInfoAlignedSize));
#endif
return aBlockPtr.iBlockBuffer;
}
void OsclMemPoolResizableAllocator::deallocateblock(MemPoolBlockInfo& aBlockPtr)
{
OSCL_ASSERT(aBlockPtr.iParentBuffer);
MemPoolBufferInfo* bufferinfo = aBlockPtr.iParentBuffer;
MemPoolBlockInfo* rightblockinfo = bufferinfo->iNextFreeBlock;
MemPoolBlockInfo* leftblockinfo = NULL;
// Go through the free block list and find the free block which would
// become the right neighbor of the block being freed
while (rightblockinfo != NULL)
{
if (&aBlockPtr < rightblockinfo)
{
break;
}
leftblockinfo = rightblockinfo;
rightblockinfo = rightblockinfo->iNextFreeBlock;
}
// Check where the newly freed block is in the list
if (leftblockinfo == NULL && rightblockinfo == NULL)
{
// The free block list is empty.
// Trivial case so add to list and return to list without merge
bufferinfo->iNextFreeBlock = &aBlockPtr;
aBlockPtr.iNextFreeBlock = NULL;
aBlockPtr.iPrevFreeBlock = NULL;
aBlockPtr.iParentBuffer->iAllocatedSz -= aBlockPtr.iBlockSize;
return;
}
else if (leftblockinfo != NULL && rightblockinfo == NULL)
{
// Adding to the end of the list
OSCL_ASSERT(leftblockinfo->iNextFreeBlock == NULL);
// Check that the newly freed block doesn't overlap with an existing free block
if (((uint8*)leftblockinfo + leftblockinfo->iBlockSize) > (uint8*)&aBlockPtr)
{
OSCL_LEAVE(OsclErrArgument);
}
leftblockinfo->iNextFreeBlock = &aBlockPtr;
aBlockPtr.iPrevFreeBlock = leftblockinfo;
aBlockPtr.iNextFreeBlock = NULL;
}
else if (leftblockinfo == NULL && rightblockinfo != NULL)
{
// Adding to the beginning of the list
OSCL_ASSERT(rightblockinfo->iPrevFreeBlock == NULL);
// Check that the newly freed block doesn't overlap with an existing free block
if (((uint8*)&aBlockPtr + aBlockPtr.iBlockSize) > (uint8*)rightblockinfo)
{
OSCL_LEAVE(OsclErrArgument);
}
bufferinfo->iNextFreeBlock = &aBlockPtr;
rightblockinfo->iPrevFreeBlock = &aBlockPtr;
aBlockPtr.iPrevFreeBlock = NULL;
aBlockPtr.iNextFreeBlock = rightblockinfo;
}
else
{
// Adding to the middle of the list
OSCL_ASSERT(leftblockinfo->iNextFreeBlock == rightblockinfo);
OSCL_ASSERT(rightblockinfo->iPrevFreeBlock == leftblockinfo);
// Check that the newly freed block doesn't overlap with the existing free blocks
if (((uint8*)&aBlockPtr + aBlockPtr.iBlockSize) > (uint8*)rightblockinfo ||
((uint8*)leftblockinfo + leftblockinfo->iBlockSize) > (uint8*)&aBlockPtr)
{
OSCL_LEAVE(OsclErrArgument);
}
leftblockinfo->iNextFreeBlock = &aBlockPtr;
rightblockinfo->iPrevFreeBlock = &aBlockPtr;
aBlockPtr.iPrevFreeBlock = leftblockinfo;
aBlockPtr.iNextFreeBlock = rightblockinfo;
}
aBlockPtr.iParentBuffer->iAllocatedSz -= aBlockPtr.iBlockSize;
// Merge the newly freed block with neighbors if contiguous
// Check which neighbors are contiguous in memory space
bool rightadj = false;
bool leftadj = false;
if (aBlockPtr.iPrevFreeBlock)
{
MemPoolBlockInfo* leftnb = aBlockPtr.iPrevFreeBlock;
if (((uint8*)leftnb + leftnb->iBlockSize) == (uint8*)&aBlockPtr)
{
leftadj = true;
}
}
if (aBlockPtr.iNextFreeBlock)
{
MemPoolBlockInfo* rightnb = aBlockPtr.iNextFreeBlock;
if (((uint8*)&aBlockPtr + aBlockPtr.iBlockSize) == (uint8*)rightnb)
{
rightadj = true;
}
}
// Do the merge based on the check
if (leftadj == false && rightadj == true)
{
// Merge the right neighbor with the newly freed block
// Update newly freed block's size and remove the right neighbor from the list
MemPoolBlockInfo* midblock = &aBlockPtr;
MemPoolBlockInfo* rightblock = aBlockPtr.iNextFreeBlock;
// Size update
midblock->iBlockSize += rightblock->iBlockSize;
// Right neighbor removal
if (rightblock->iNextFreeBlock)
{
rightblock->iNextFreeBlock->iPrevFreeBlock = midblock;
}
midblock->iNextFreeBlock = rightblock->iNextFreeBlock;
}
else if (leftadj == true && rightadj == false)
{
// Merge the newly freed block with the left neighbor
// Update the left neighbor's block size to include the newly freed block and
// remove the newly freed block from the list
MemPoolBlockInfo* midblock = &aBlockPtr;
MemPoolBlockInfo* leftblock = aBlockPtr.iPrevFreeBlock;
// Size update
leftblock->iBlockSize += midblock->iBlockSize;
// Newly freed block removal
if (midblock->iNextFreeBlock)
{
midblock->iNextFreeBlock->iPrevFreeBlock = leftblock;
}
leftblock->iNextFreeBlock = midblock->iNextFreeBlock;
}
else if (leftadj == true && rightadj == true)
{
// Merge the newly freed block and right neighbor with the left neighbor
// and remove the newly freed block and right neighbor from the list
MemPoolBlockInfo* midblock = &aBlockPtr;
MemPoolBlockInfo* leftblock = aBlockPtr.iPrevFreeBlock;
MemPoolBlockInfo* rightblock = aBlockPtr.iNextFreeBlock;
// Size update
leftblock->iBlockSize += (midblock->iBlockSize + rightblock->iBlockSize);
// Newly freed and right neighbor block removal
if (rightblock->iNextFreeBlock)
{
rightblock->iNextFreeBlock->iPrevFreeBlock = leftblock;
}
leftblock->iNextFreeBlock = rightblock->iNextFreeBlock;
}
}
bool OsclMemPoolResizableAllocator::validateblock(OsclAny* aBlockBufPtr)
{
uint32 i = 0;
if (aBlockBufPtr == NULL)
{
// Invalid pointer
return false;
}
// Check if the pointer falls within one of the memory pool buffer's memory address
for (i = 0; i < iMemPoolBufferList.size(); ++i)
{
MemPoolBufferInfo* bufferinfo = iMemPoolBufferList[i];
if (aBlockBufPtr > bufferinfo->iStartAddr && aBlockBufPtr < bufferinfo->iEndAddr)
{
break;
}
}
if (i >= iMemPoolBufferList.size())
{
// Parent buffer is not part of this memory pool instance
return false;
}
// Retrieve the block info header
MemPoolBlockInfo* chkblock = (MemPoolBlockInfo*)((uint8*)aBlockBufPtr - iBlockInfoAlignedSize);
if (chkblock->iBlockPreFence != OSCLMEMPOOLRESIZABLEALLOCATOR_PREFENCE_PATTERN ||
chkblock->iBlockPostFence != OSCLMEMPOOLRESIZABLEALLOCATOR_POSTFENCE_PATTERN)
{
// Memory fence checking failed
return false;
}
// Check the parent buffer is one in the list
MemPoolBufferInfo* parentbuffer = chkblock->iParentBuffer;
if (parentbuffer == NULL)
{
return false;
}
for (i = 0; i < iMemPoolBufferList.size(); ++i)
{
if (parentbuffer == iMemPoolBufferList[i])
{
break;
}
}
if (i >= iMemPoolBufferList.size())
{
// Parent buffer is not part of this memory pool instance
return false;
}
if (aBlockBufPtr < parentbuffer->iStartAddr || aBlockBufPtr > parentbuffer->iEndAddr)
{
// The address of the buffer is not part of the parent buffer
return false;
}
if ((OsclAny*)((uint8*)chkblock + chkblock->iBlockSize - 1) > (parentbuffer->iEndAddr))
{
// The block size is too big
return false;
}
return true;
}
OSCL_EXPORT_REF uint32 OsclMemPoolResizableAllocator::getBufferSize() const
{
if (iMemPoolBufferNumLimit == 0)
OSCL_LEAVE(OsclErrNotSupported);
uint32 bufferSize = 0;
for (uint32 i = 0; i < iMemPoolBufferList.size(); ++i)
{
MemPoolBufferInfo* bufferinfo = iMemPoolBufferList[i];
bufferSize += getMemPoolBufferSize(bufferinfo);
}
return bufferSize;
}
OSCL_EXPORT_REF uint32 OsclMemPoolResizableAllocator::getAllocatedSize() const
{
//const uint32 expectedNumBlocksPerBuffer = iExpectedNumBlocksPerBuffer > 0 ? iExpectedNumBlocksPerBuffer : OSCLMEMPOOLRESIZABLEALLOCATOR_DEFAULT_NUMBLOCKPERBUFFER;
uint32 allocatedSz = 0;
for (uint32 i = 0; i < iMemPoolBufferList.size(); ++i)
{
MemPoolBufferInfo* bufferinfo = iMemPoolBufferList[i];
allocatedSz += getMemPoolBufferAllocatedSize(bufferinfo);
}
return allocatedSz;
}
OSCL_EXPORT_REF uint32 OsclMemPoolResizableAllocator::getAvailableSize() const
{
if (iMemPoolBufferNumLimit == 0)
OSCL_LEAVE(OsclErrNotSupported);
uint32 availableSize = 0;
for (uint32 i = 0; i < iMemPoolBufferList.size(); ++i)
{
MemPoolBufferInfo* bufferinfo = iMemPoolBufferList[i];
uint32 memPoolBufferAvailableSz = 0;
memPoolBufferAvailableSz = (getMemPoolBufferSize(bufferinfo) - getMemPoolBufferAllocatedSize(bufferinfo));
availableSize += memPoolBufferAvailableSz;
}
return availableSize;
}
OSCL_EXPORT_REF uint32 OsclMemPoolResizableAllocator::getLargestContiguousFreeBlockSize() const
{
uint32 blockSz = 0;
if (iMemPoolBufferNumLimit > 0)
{
for (uint32 i = 0; i < iMemPoolBufferList.size(); ++i)
{
MemPoolBufferInfo* bufferinfo = iMemPoolBufferList[i];
if (bufferinfo)
{
MemPoolBlockInfo* blockinfo = bufferinfo->iNextFreeBlock;
while (blockinfo != NULL)
{
if (blockinfo->iBlockSize > blockSz) blockSz = blockinfo->iBlockSize;
blockinfo = blockinfo->iNextFreeBlock;
}
}
}
}
else
OSCL_LEAVE(OsclErrNotSupported);
if (blockSz > iBlockInfoAlignedSize) blockSz -= iBlockInfoAlignedSize;
else blockSz = 0;
return blockSz;
}
OSCL_EXPORT_REF bool OsclMemPoolResizableAllocator::setMaxSzForNewMemPoolBuffer(uint32 aMaxNewMemPoolBufferSz)
{
bool retval = true;
if (iMemPoolBufferNumLimit > 0)
iMaxNewMemPoolBufferSz = aMaxNewMemPoolBufferSz;
else
retval = false;
return retval;
}
uint32 OsclMemPoolResizableAllocator::getMemPoolBufferSize(MemPoolBufferInfo* aBufferInfo) const
{
uint32 memPoolBufferSz = 0;
if (aBufferInfo)
memPoolBufferSz = aBufferInfo->iBufferSize;
return memPoolBufferSz;
}
uint32 OsclMemPoolResizableAllocator::getMemPoolBufferAllocatedSize(MemPoolBufferInfo* aBufferInfo) const
{
return aBufferInfo->iAllocatedSz;
/*
uint32 allocatedSz = 0;
const uint32 expectedNumBlocksPerBuffer = iExpectedNumBlocksPerBuffer > 0 ? iExpectedNumBlocksPerBuffer : OSCLMEMPOOLRESIZABLEALLOCATOR_DEFAULT_NUMBLOCKPERBUFFER;
if (aBufferInfo)
{
if (aBufferInfo->iNumOutstanding > expectedNumBlocksPerBuffer)
{
allocatedSz = (aBufferInfo->iAllocatedSz - (expectedNumBlocksPerBuffer * iBlockInfoAlignedSize));
}
else
{
allocatedSz = (aBufferInfo->iAllocatedSz - (aBufferInfo->iNumOutstanding * iBlockInfoAlignedSize));
}
}
return allocatedSz;
*/
}
uint32 OsclMemPoolResizableAllocator::memoryPoolBufferMgmtOverhead() const
{
uint32 overheadBytes = iBufferInfoAlignedSize;
if (iExpectedNumBlocksPerBuffer > 0)
{
overheadBytes += (iExpectedNumBlocksPerBuffer * iBlockInfoAlignedSize);
}
else
{
overheadBytes += (OSCLMEMPOOLRESIZABLEALLOCATOR_DEFAULT_NUMBLOCKPERBUFFER * iBlockInfoAlignedSize);
}
return overheadBytes;
}