mpx/commonframework/common/src/mpxheapmanager.cpp
changeset 0 a2952bb97e68
child 24 6c1dfe4da5dd
--- /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