--- /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 <hal.h>
+#include <centralrepository.h>
+#include <mpxprivatecrkeys.h>
+#include <mpxdata.h>
+#include <mpxlog.h>
+#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<RMPXHeapCell>(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)<KMPXMinHeapSize);
+ m=new(base)RMPXHeapManager(c); // Add on chunk
+ m->iMaxMemory = maxMemory;
+ }
+ }
+ if (r==KErrNone)
+ {
+ if (!base)
+ {
+ base=c.Base();
+ }
+ m=reinterpret_cast<RMPXHeapManager*>(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<ENumClients;++i)
+ {
+ if (iClients[i].iTid==aTid)
+ {
+ r=i;
+ break;
+ }
+ }
+ return r;
+ }
+
+// -----------------------------------------------------------------------------
+// RMPXHeapManager::ClientIndex
+// -----------------------------------------------------------------------------
+//
+TInt RMPXHeapManager::ClientIndex(const TClientContext& aContext)
+ {
+ TInt i=Find(aContext.iTid);
+ if (i==KErrNotFound)
+ {
+ i=Find(KNullThreadId); // First unused slot
+ MPX_ASSERT(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<TAny>(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<RMPXHeapCell>(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<ENumClients;++i)
+ {
+ if (iClients[i].iTid.Id()!=KNullThreadId)
+ {
+ ++c;
+ }
+ }
+ return c;
+ }
+
+// -----------------------------------------------------------------------------
+// Allocate a buffer on the chunk
+// -----------------------------------------------------------------------------
+//
+TAny* RMPXHeapManager::Alloc(TUint aClientHandle,TInt aSize)
+ {
+ TAny* ptr=NULL;
+ TInt size=Align4(aSize);
+ RMPXHeapCell* cell=HeapCell(aClientHandle,size);
+ if (cell)
+ {
+ TInt offset = RMPXHeapManager::Offset(aClientHandle, cell) + EHeapCellHeaderSize;
+ ptr=Ptr<TAny>(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<RMPXHeapCell>(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 && tempCell<cell;
+ prevCell=tempCell, tempCell= MPX_NEXT_FREE_CELL(aClientHandle, tempCell))
+ {
+ }
+ if (tempCell)
+ { // Is there a following free cell?
+ nextCell = MPX_NEXT_CELL(cell);
+ if (nextCell == tempCell)
+ { //Yes, merge two cells
+ cell->iLen += 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<ENumClients;++i)
+ {
+ if (i!=ClientIndex(aClientHandle)) // No point in looking at current thread!
+ {
+ TClientContext& c=iClients[i];
+ TThreadId tid=c.iTid;
+ if (tid.Id()!=KNullThreadId)
+ {
+ TInt r=t.Open(tid,EOwnerThread);
+ if (r==KErrNone) // Thread still exists, so that's OK
+ {
+ t.Close();
+ }
+ else
+ {
+ // Cannot open the thread, but we still have an entry for it. This (may)
+ // mean that there data stranded on the heap which we must remove
+ //
+ // Remove data associated with this thread
+ //
+ TInt j = c.iData.Count()-1;
+ while (j>=0)
+ {
+ RMPXDataObject<TUint>* d=
+ (RMPXDataObject<TUint>*)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<TUint>));
+ TInt r(KErrNone);
+ if (ptr)
+ {
+ RMPXDataObject<TUint>* obj=new(ptr)RMPXDataObject<TUint>();
+ 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<TUint> 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<ENumClients;++i)
+ {
+ TClientContext& c=iClients[i];
+ TThreadId tid=c.iTid;
+ if (tid.Id()!=KNullThreadId)
+ { // Remove datahandle
+ ret = c.iData.Find(aClientHandle, obj);
+ if (KErrNotFound != ret)
+ {
+ c.iData.Remove(aClientHandle, ret);
+ ret = KErrNone;
+ }
+ }
+ }
+ }
+ else
+ {
+ // Remove data from current context
+ TClientContext& cc = (iDeadContextIndex==KErrNotFound ?
+ iClients[clientIndex] : iClients[iDeadContextIndex]);
+ ret = cc.iData.Find(aClientHandle,obj);
+ if (KErrNotFound != ret)
+ {
+ cc.iData.Remove(aClientHandle,ret);
+ ret=KErrNone;
+ }
+ }
+ obj.Close(aClientHandle);
+ return ret;
+ }
+#endif // __ENABLE_MPX_GARBAGE_COLLECTOR
+
+// End of file