mpx/commonframework/common/src/mpxheapmanager.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:23:05 +0100
branchRCL_3
changeset 56 63223d4fd956
parent 55 6c1dfe4da5dd
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201033 Kit: 201035

/*
* 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