diff -r 000000000000 -r a2952bb97e68 mpx/commonframework/common/src/mpxheapmanager.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mpx/commonframework/common/src/mpxheapmanager.cpp Thu Dec 17 08:55:47 2009 +0200 @@ -0,0 +1,662 @@ +/* +* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Implementation of heap manager +* +*/ + +#include +#include +#include +#include +#include +#include "mpxheapmanager.h" + +_LIT(KMPXChunkNameV2,"MPX_DATA"); +_LIT(KMPXMutexNameV2,"MPX_MUTEX"); + +const TInt KMPXBytesPerMB = 0x100000; // bytes of 1 MB, 1024*1024 +#ifdef __WINS__ +const TInt KMPXMaxHeapDefaultSize=8; // MB +#else +const TInt KMPXMaxHeapDefaultSize=15; // MB +#endif +const TInt KMPXMinHeapSize=0x10000; // 64KB +const TInt KMPXChunkAdjustSize=0x40000; // 256KB +#ifdef __ENABLE_MPX_GARBAGE_COLLECTOR +const TInt KMPXGarbageCollectorClientThreshold=1000;//GC +#endif // __ENABLE_MPX_GARBAGE_COLLECTOR + +#define MPX_NEXT_CELL(p) ((RMPXHeapCell*)(((TUint8*)p)+p->iLen)) +#define MPX_NEXT_FREE_CELL(ch,p) (p->iNextOffset?Ptr(ch, p->iNextOffset):NULL) + +// ----------------------------------------------------------------------------- +// RMPXHeapManager::TClientContext::TClientContext +// ----------------------------------------------------------------------------- +// +RMPXHeapManager::TClientContext::TClientContext() + : iTid(KNullThreadId),iCount(0),iBase(0) + { + } + +// ----------------------------------------------------------------------------- +// RMPXHeapManager::TClientContext::TClientContext +// ----------------------------------------------------------------------------- +// +RMPXHeapManager::TClientContext::TClientContext( + TUint8* aBase, + TInt aChunkHandle, + TInt aMutexHandle) : + iTid(RThread().Id()), + iCount(0), + iBase(aBase) + { + iChunk.SetHandle(aChunkHandle); + iMutex.SetHandle(aMutexHandle); + } + +// ----------------------------------------------------------------------------- +// RMPXHeapManager::RMPXHeapManager +// ----------------------------------------------------------------------------- +// +RMPXHeapManager::RMPXHeapManager(const RChunk& aChunk) + : iEndOffset(aChunk.Size()),iCounter(0) +#ifdef __ENABLE_MPX_GARBAGE_COLLECTOR + ,iDeadContextIndex(KErrNotFound) +#endif // __ENABLE_MPX_GARBAGE_COLLECTOR + { + TInt hmSize = sizeof(RMPXHeapManager); + MPX_ASSERT_WORD_ALIGNED(aChunk.Base()+hmSize); + // + // Create first free heap cell + // + RMPXHeapCell* cell=new(aChunk.Base()+hmSize)RMPXHeapCell(0,iEndOffset-hmSize); + // set up free cell list header + iFree.iNextOffset = hmSize; + iFree.iLen = 0; + iUsedMemory = hmSize; + } + +// ----------------------------------------------------------------------------- +// RMPXHeapManager::HeapMemoryInfoL +// ----------------------------------------------------------------------------- +// +void RMPXHeapManager::HeapMemoryInfoL(TInt& aTotal, TInt& aUsed) + { + TUint h = RMPXHeapManager::ClientHandle(); + if (!h) + { + User::Leave(KErrNotFound); + } + RMPXHeapManager& hm = RMPXHeapManager::HeapManager(h); + aUsed = hm.iUsedMemory; + HAL::Get(HALData::EMemoryRAMFree, aTotal); + aTotal +=aUsed; + if (aTotal > hm.iMaxMemory) + { + aTotal = hm.iMaxMemory; + } + } + +// ----------------------------------------------------------------------------- +// RMPXHeapManager::ClientHandle +// ----------------------------------------------------------------------------- +// +TUint RMPXHeapManager::ClientHandle() + { + // First, try to retrieve the handle from TLS. Although slow, this seems to be + // faster than openeing the chunk, though probably requires further testing. It + // will be in TLS if another client from this thread has previously called this + // method + // + TUint h=(TUint)Dll::Tls(); + RMPXHeapManager* m=NULL; + if (h==0) + { + // Must be first client in this thread, so try opening the chunk, but + // first, create a "critical section" on this code to protect against multiple + // threads creating the chunk/heap at the same time + // + RMutex mx; + TInt n=2; + TInt r=KErrNotFound; + while(r!=KErrNone && n--) + { + r=mx.OpenGlobal(KMPXMutexNameV2,EOwnerThread); + if (r==KErrNotFound) + { + r=mx.CreateGlobal(KMPXMutexNameV2,EOwnerThread); + } + }; + if (r==KErrNone) + { + mx.Wait(); + // + TUint8* base=NULL; + RChunk c; + r=c.OpenGlobal(KMPXChunkNameV2,EFalse,EOwnerThread); + if (r==KErrNotFound) + { + // Chunk not there, so this must be the first client in the system and so + // the chunk must be created + // + // Get maximum size of memory from CenRep key + CRepository* rep(NULL); + // Set to default size + TInt maxMemory = KMPXMaxHeapDefaultSize; + TRAP_IGNORE(rep = CRepository::NewL( KCRUidMPXSettings )); + if (rep) + { + rep->Get( KMPXMaxGlobalHeapSize, maxMemory ); + delete rep; + } + // size in bytes + maxMemory *= KMPXBytesPerMB; + r=c.CreateGlobal(KMPXChunkNameV2,0,maxMemory,EOwnerThread); + if (r==KErrNone && c.Adjust(KMPXMinHeapSize)==KErrNone) + { + base=c.Base(); + // + // Add the Heap Manager and the bottom of the chunk. Thereafter will come the + // data + // + MPX_ASSERT(sizeof(RMPXHeapManager)iMaxMemory = maxMemory; + } + } + if (r==KErrNone) + { + if (!base) + { + base=c.Base(); + } + m=reinterpret_cast(base); + TClientContext cc(base,c.Handle(),mx.Handle()); + TInt index=m->ClientIndex(cc); + h=ClientHandle(base,index); + Dll::SetTls((TAny*)h); + mx.Signal(); + } + else + { + c.Close(); + mx.Signal(); + mx.Close(); + m=NULL; + } + } + } + MPX_ASSERT(h); + return h; + } + +// ----------------------------------------------------------------------------- +// RMPXHeapManager::Find +// ----------------------------------------------------------------------------- +// +TInt RMPXHeapManager::Find(const TThreadId& aTid) + { + TInt r=KErrNotFound; + for (TInt i=0;i=0); // Run out of space (num threads > ENumClients) + iClients[i]=aContext; + } + return i; + } + +// ----------------------------------------------------------------------------- +// RMPXHeapManager::AddRef +// ----------------------------------------------------------------------------- +// +void RMPXHeapManager::AddRef(TUint aClientHandle) + { + MPX_ASSERT(aClientHandle); + TClientContext& c=iClients[ClientIndex(aClientHandle)]; + ++c.iCount; +#ifdef __ENABLE_MPX_GARBAGE_COLLECTOR + if (iCounter%KMPXGarbageCollectorClientThreshold==0 && iCounter) + {//GC + RunGc(aClientHandle); + } +#endif // __ENABLE_MPX_GARBAGE_COLLECTOR + } + +// ----------------------------------------------------------------------------- +// RMPXHeapManager::Release() +// ----------------------------------------------------------------------------- +// +void RMPXHeapManager::Release(TUint aClientHandle) + { + MPX_ASSERT(aClientHandle); + TClientContext& c=iClients[ClientIndex(aClientHandle)]; + if (--c.iCount==0) + { +#ifdef __ENABLE_MPX_GARBAGE_COLLECTOR + c.iData.Close(aClientHandle); //GC +#endif // __ENABLE_MPX_GARBAGE_COLLECTOR + c.iTid=KNullThreadId; // Mark as free + c.iMutex.Close(); + c.iChunk.Close(); + Dll::SetTls(NULL); + } + } + +// ----------------------------------------------------------------------------- +// RMPXHeapManager::HeapCell +// ----------------------------------------------------------------------------- +// +RMPXHeapCell* RMPXHeapManager::HeapCell( + TUint aClientHandle, + TInt aSize) + { + aSize = Align4(aSize + EHeapCellHeaderSize); + RMPXHeapCell* lastFree(NULL); + RMPXHeapCell* cell = DoHeapCell(aClientHandle, aSize, lastFree); + if (!cell) + { // try to grow chunk + TInt r = TryToGrowHeap(aClientHandle, aSize, lastFree); + if (KErrNone == r) + { + cell = DoHeapCell(aClientHandle, aSize, lastFree); + } + } + if (cell) + { + iUsedMemory += (cell->iLen); + } + return cell; + } + +// ----------------------------------------------------------------------------- +// RMPXHeapManager::HeapCell +// ----------------------------------------------------------------------------- +// +RMPXHeapCell* RMPXHeapManager::DoHeapCell( + TUint aClientHandle, + TInt aSize, + RMPXHeapCell*& aLastFree) + { + // + // Start with the first cell and walk the heap until a free cell is found which + // is big enough + // + RMPXHeapCell* prevCell = &iFree; + RMPXHeapCell* cell = MPX_NEXT_FREE_CELL(aClientHandle, prevCell); + TInt tempOffset(0); + for (; cell; prevCell=cell, cell=MPX_NEXT_FREE_CELL(aClientHandle, cell)) + { // scan the free list + //cell is valid and free + if (cell->iLen >= aSize) + { // size is big enough + if (cell->iLen-aSize < EMinCellSize) // leftover must larger enough to split + { // it isn't, so take it all + aSize = cell->iLen; + tempOffset = cell->iNextOffset; + MPX_ASSERT(tempOffset <= iEndOffset - EMinCellSize); + } + else + { // Take amount and create a new free cell + tempOffset = Offset(aClientHandle, cell) + aSize; + MPX_ASSERT(tempOffset <= iEndOffset - EMinCellSize); + // create a new cell + new(Ptr(aClientHandle,tempOffset)) + RMPXHeapCell(cell->iNextOffset, cell->iLen - aSize); + } + prevCell->iNextOffset = tempOffset; + cell->iLen = aSize; + return cell; + } + } + aLastFree = prevCell; + return NULL; + } + +TInt RMPXHeapManager::TryToGrowHeap( + TUint aClientHandle, + TInt aSize, + RMPXHeapCell* aLastFree) + { + TBool atEnd = IsLastCell(aClientHandle, aLastFree); + TInt grow=Max((TInt)KMPXChunkAdjustSize, aSize); + // Grow the chunk + TClientContext& cc=iClients[ClientIndex(aClientHandle)]; + TInt r(KErrNoMemory); + if (iEndOffset+grow <= iMaxMemory) + { + r=cc.iChunk.Adjust(iEndOffset+grow); + if (KErrNone == r) + { + if (atEnd) + { + aLastFree->iLen += grow; + } + else + { // create a new free cell + new (Ptr(aClientHandle, iEndOffset))RMPXHeapCell(0, grow); + aLastFree->iNextOffset = iEndOffset; + } + iEndOffset=cc.iChunk.Size(); + } + else + { + MPX_DEBUG2("RMPXHeapManager::HeapCell chunk failed to grow %d", r); + } + } + return r; + } + +// ----------------------------------------------------------------------------- +// RMPXHeapManager::ClientCount +// ----------------------------------------------------------------------------- +// +TInt RMPXHeapManager::ClientCount() const + { + TInt c=0; + for (TInt i=0;i(aClientHandle,offset); + MPX_ASSERT(ptr); + MPX_ASSERT_WORD_ALIGNED(ptr); + } + return ptr; + } + +// ----------------------------------------------------------------------------- +// Copy a buffer on the chunk +// ----------------------------------------------------------------------------- +// +TInt RMPXHeapManager::Copy(TUint aClientHandle,TAny* aSrc,TInt aSize) + { + TInt r=KErrNoMemory; + TAny* ptr=Alloc(aClientHandle,aSize); + if (ptr) + { + (void)Mem::Copy(ptr,aSrc,aSize); + r=RMPXHeapManager::Offset(aClientHandle,ptr); + } + return r; + } + +// ----------------------------------------------------------------------------- +// Frees a pointer on the chunk +// ----------------------------------------------------------------------------- +// +TInt RMPXHeapManager::Free(TUint aClientHandle,TAny* aPtr) + { + TInt cellOffset=RMPXHeapManager::Offset(aClientHandle, aPtr)-sizeof(RMPXHeapCell); + RMPXHeapCell* cell = Ptr(aClientHandle,cellOffset); + RMPXHeapCell* prevCell = &iFree; + RMPXHeapCell* tempCell = MPX_NEXT_FREE_CELL(aClientHandle, prevCell); + RMPXHeapCell* nextCell(NULL); + TInt size = cell->iLen; + // find the position in the list for the new free cell + for (; tempCell && tempCelliLen += tempCell->iLen; + cell->iNextOffset = tempCell->iNextOffset; + } + else + { + cell->iNextOffset = RMPXHeapManager::Offset(aClientHandle, tempCell); + } + } + else + { + cell->iNextOffset = 0; // No following free cell + } + + nextCell = MPX_NEXT_CELL(prevCell); + if (nextCell==cell) // Is it adjacent + { + prevCell->iLen += cell->iLen; + prevCell->iNextOffset = cell->iNextOffset; + cell = prevCell; + } + else + { + prevCell->iNextOffset = RMPXHeapManager::Offset(aClientHandle, cell); + } + + iUsedMemory -= size; + MPX_ASSERT(iUsedMemory > 0); + // Shrink chunk + // How big is the chunk now + // + // TInt gap=last->iBufOffset+last->iBufLen-iEndOffset; + //if (gap>2*KMPXChunkAdjustSize) + // { + // We assume that a spare capacity of KMPXChunkAdjustSize bytes + // is all we need, so if we have more we can shrink the chunk + // + // TClientContext& cc=iClients[ClientIndex(aClientHandle)]; + // cc.iChunk.Adjust(iEndOffset-KMPXChunkAdjustSize); + // iEndOffset=cc.iChunk.Size(); + // } + + return KErrNone; + } + +// ----------------------------------------------------------------------------- +// RMPXHeapManager::Lock +// ----------------------------------------------------------------------------- +// +void RMPXHeapManager::Lock(TUint aClientHandle) + { + MPX_ASSERT(aClientHandle); + TClientContext& cc=iClients[ClientIndex(aClientHandle)]; + cc.iMutex.Wait(); + } + +// ----------------------------------------------------------------------------- +// RMPXHeapManager::Unlock +// ----------------------------------------------------------------------------- +// +void RMPXHeapManager::Unlock(TUint aClientHandle) + { + MPX_ASSERT(aClientHandle); + TClientContext& cc=iClients[ClientIndex(aClientHandle)]; + cc.iMutex.Signal(); + } + +// ----------------------------------------------------------------------------- +// RMPXHeapManager::Counter +// ----------------------------------------------------------------------------- +// +TInt RMPXHeapManager::IncrementCounter() + { + MPX_DEBUG3("RMPXHeapManager::Counter 0x%08x, count %d", this, iCounter); + return ++iCounter; + } + +// ----------------------------------------------------------------------------- +// RMPXHeapManager::IsLastCell +// ----------------------------------------------------------------------------- +// +TBool RMPXHeapManager::IsLastCell( + TUint aClientHandle, + RMPXHeapCell* aCell) + { + return (RMPXHeapManager::Offset(aClientHandle, aCell) + aCell->iLen) == iEndOffset; + } + +#ifdef __ENABLE_MPX_GARBAGE_COLLECTOR +// ----------------------------------------------------------------------------- +// Cleans up any objects associated with threads that have died +// ----------------------------------------------------------------------------- +// +void RMPXHeapManager::RunGc(TUint aClientHandle) + {//GC + MPX_FUNC("RMPXHeapManager::RunGc"); + RThread t; + for (TInt i=0;i=0) + { + RMPXDataObject* d= + (RMPXDataObject*)c.iData.DataItem(aClientHandle,j); + TUint* dataHandle=d->Object(aClientHandle); + MMPXData* data=MMPXData::Data(aClientHandle, *dataHandle); + iDeadContextIndex=i; + data->Release(aClientHandle); + iDeadContextIndex=KErrNotFound; + j = c.iData.Count()-1; + } + + // Now remove context, now that we've cleaned up its data + // + c.iTid=KNullThreadId; // Mark as free + c.iData.Close(aClientHandle); + } + } + } + } + } + +// ----------------------------------------------------------------------------- +// RMPXHeapManager::AddData +// ----------------------------------------------------------------------------- +// +TInt RMPXHeapManager::AddData(TUint aClientHandle,TUint aDataHandle) + { //GC + TAny* ptr =Alloc(aClientHandle, sizeof(RMPXDataObject)); + TInt r(KErrNone); + if (ptr) + { + RMPXDataObject* obj=new(ptr)RMPXDataObject(); + obj->CopyObject(aClientHandle,aDataHandle); + TClientContext& c=iClients[ClientIndex(aClientHandle)]; + c.iData.Append(aClientHandle,*obj); + } + else + { + r = KErrNoMemory; + } + return r; + } + +// ----------------------------------------------------------------------------- +// RMPXHeapManager::RemoveData +// ----------------------------------------------------------------------------- +// +TInt RMPXHeapManager::RemoveData( + TUint aClientHandle, + TUint aDataHandle, + TBool aRemoveAll) + {//GC + RMPXDataObject obj; + obj.CopyObject(aClientHandle,aDataHandle); + TInt clientIndex = ClientIndex(aClientHandle); + TInt ret(KErrNotFound); + + if (aRemoveAll && iDeadContextIndex==KErrNotFound) + { // remove all and GC is not running + for (TInt i=0;i