diff -r c1f20ce4abcf -r 3e88ff8f41d5 kernel/eka/common/heap_hybrid.cpp --- a/kernel/eka/common/heap_hybrid.cpp Tue Aug 31 16:34:26 2010 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3319 +0,0 @@ -// Copyright (c) 1994-2009 Nokia Corporation and/or its subsidiary(-ies). -// All rights reserved. -// This component and the accompanying materials are made available -// under the terms of the License "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: -// kernel\eka\common\heap_hybrid.cpp -// -// Uses malloc (aka dlmalloc) written by Doug Lea version 2.8.4 -// - -#include "common.h" -#ifdef __KERNEL_MODE__ -#include -#endif -#include "dla.h" -#ifndef __KERNEL_MODE__ -#include "slab.h" -#include "page_alloc.h" -#endif -#include "heap_hybrid.h" - -// enables btrace code compiling into -#define ENABLE_BTRACE - -// if non zero this causes the iSlabs to be configured only when the chunk size exceeds this level -#define DELAYED_SLAB_THRESHOLD (64*1024) // 64KB seems about right based on trace data -#define SLAB_CONFIG 0xabe // Use slabs of size 48, 40, 32, 24, 20, 16, 12, and 8 bytes - -#ifdef _DEBUG -#define __SIMULATE_ALLOC_FAIL(s) if (CheckForSimulatedAllocFail()) {s} -#define __ALLOC_DEBUG_HEADER(s) (s += EDebugHdrSize) -#define __SET_DEBUG_DATA(p,n,c) (((SDebugCell*)(p))->nestingLevel = (n), ((SDebugCell*)(p))->allocCount = (c)) -#define __GET_USER_DATA_BFR(p) ((p!=0) ? (TUint8*)(p) + EDebugHdrSize : NULL) -#define __GET_DEBUG_DATA_BFR(p) ((p!=0) ? (TUint8*)(p) - EDebugHdrSize : NULL) -#define __ZAP_CELL(p) memset( (TUint8*)p, 0xde, (AllocLen(__GET_USER_DATA_BFR(p))+EDebugHdrSize)) -#define __DEBUG_SAVE(p) TInt dbgNestLevel = ((SDebugCell*)p)->nestingLevel -#define __DEBUG_RESTORE(p) if (p) {((SDebugCell*)p)->nestingLevel = dbgNestLevel;} -#define __DEBUG_HDR_SIZE EDebugHdrSize -#define __REMOVE_DBG_HDR(n) (n*EDebugHdrSize) -#define __GET_AVAIL_BLOCK_SIZE(s) ( (sallocCount = (c);} -#define __INIT_COUNTERS(i) iCellCount=i,iTotalAllocSize=i -#define __INCREMENT_COUNTERS(p) iCellCount++, iTotalAllocSize += AllocLen(p) -#define __DECREMENT_COUNTERS(p) iCellCount--, iTotalAllocSize -= AllocLen(p) -#define __UPDATE_TOTAL_ALLOC(p,s) iTotalAllocSize += (AllocLen(__GET_USER_DATA_BFR(p)) - s) - -#else -#define __SIMULATE_ALLOC_FAIL(s) -#define __ALLOC_DEBUG_HEADER(s) -#define __SET_DEBUG_DATA(p,n,c) -#define __GET_USER_DATA_BFR(p) (p) -#define __GET_DEBUG_DATA_BFR(p) (p) -#define __ZAP_CELL(p) -#define __DEBUG_SAVE(p) -#define __DEBUG_RESTORE(p) -#define __DEBUG_HDR_SIZE 0 -#define __REMOVE_DBG_HDR(n) 0 -#define __GET_AVAIL_BLOCK_SIZE(s) (s) -#define __UPDATE_ALLOC_COUNT(o,n,c) -#define __INIT_COUNTERS(i) iCellCount=i,iTotalAllocSize=i -#define __INCREMENT_COUNTERS(p) -#define __DECREMENT_COUNTERS(p) -#define __UPDATE_TOTAL_ALLOC(p,s) - -#endif - - -#define MEMORY_MONITORED (iFlags & EMonitorMemory) -#define GM (&iGlobalMallocState) -#define IS_FIXED_HEAP (iFlags & EFixedSize) -#define __INIT_COUNTERS(i) iCellCount=i,iTotalAllocSize=i -#define __POWER_OF_2(x) (!((x)&((x)-1))) - -#define __DL_BFR_CHECK(M,P) \ - if ( MEMORY_MONITORED ) \ - if ( !IS_ALIGNED(P) || ((TUint8*)(P)iSeg.iBase) || ((TUint8*)(P)>(M->iSeg.iBase+M->iSeg.iSize))) \ - BTraceContext12(BTrace::EHeap, BTrace::EHeapCorruption, (TUint32)this, (TUint32)P, (TUint32)0), HEAP_PANIC(ETHeapBadCellAddress); \ - else DoCheckInuseChunk(M, MEM2CHUNK(P)) - -#ifndef __KERNEL_MODE__ - -#define __SLAB_BFR_CHECK(S,P,B) \ - if ( MEMORY_MONITORED ) \ - if ( ((TUint32)P & 0x3) || ((TUint8*)P(TUint8*)this)) \ - BTraceContext12(BTrace::EHeap, BTrace::EHeapCorruption, (TUint32)this, (TUint32)P, (TUint32)S), HEAP_PANIC(ETHeapBadCellAddress); \ - else DoCheckSlab(S, EPartialFullSlab, P), BuildPartialSlabBitmap(B,S,P) -#define __PAGE_BFR_CHECK(P) \ - if ( MEMORY_MONITORED ) \ - if ( ((TUint32)P & ((1 << iPageSize)-1)) || ((TUint8*)P(TUint8*)this)) \ - BTraceContext12(BTrace::EHeap, BTrace::EHeapCorruption, (TUint32)this, (TUint32)P, (TUint32)0), HEAP_PANIC(ETHeapBadCellAddress) - -#endif - -#ifdef _MSC_VER -// This is required while we are still using VC6 to compile, so as to avoid warnings that cannot be fixed -// without having to edit the original Doug Lea source. The 4146 warnings are due to the original code having -// a liking for negating unsigned numbers and the 4127 warnings are due to the original code using the RTCHECK -// macro with values that are always defined as 1. It is better to turn these warnings off than to introduce -// diffs between the original Doug Lea implementation and our adaptation of it -#pragma warning( disable : 4146 ) /* unary minus operator applied to unsigned type, result still unsigned */ -#pragma warning( disable : 4127 ) /* conditional expression is constant */ -#endif // _MSC_VER - - -/** -@SYMPatchable -@publishedPartner -@released - -Defines the minimum cell size of a heap. - -The constant can be changed at ROM build time using patchdata OBY keyword. - -@deprecated Patching this constant no longer has any effect. -*/ -#ifdef __X86GCC__ // For X86GCC we dont use the proper data import attribute -#undef IMPORT_D // since the constants are not really imported. GCC doesn't -#define IMPORT_D // allow imports from self. -#endif -IMPORT_D extern const TInt KHeapMinCellSize; - -/** -@SYMPatchable -@publishedPartner -@released - -This constant defines the ratio that determines the amount of hysteresis between heap growing and heap -shrinking. -It is a 32-bit fixed point number where the radix point is defined to be -between bits 7 and 8 (where the LSB is bit 0) i.e. using standard notation, a Q8 or a fx24.8 -fixed point number. For example, for a ratio of 2.0, set KHeapShrinkHysRatio=0x200. - -The heap shrinking hysteresis value is calculated to be: -@code -KHeapShrinkHysRatio*(iGrowBy>>8) -@endcode -where iGrowBy is a page aligned value set by the argument, aGrowBy, to the RHeap constructor. -The default hysteresis value is iGrowBy bytes i.e. KHeapShrinkHysRatio=2.0. - -Memory usage may be improved by reducing the heap shrinking hysteresis -by setting 1.0 < KHeapShrinkHysRatio < 2.0. Heap shrinking hysteresis is disabled/removed -when KHeapShrinkHysRatio <= 1.0. - -The constant can be changed at ROM build time using patchdata OBY keyword. -*/ -IMPORT_D extern const TInt KHeapShrinkHysRatio; - -UEXPORT_C TInt RHeap::AllocLen(const TAny* aCell) const -{ - const MAllocator* m = this; - return m->AllocLen(aCell); -} - -UEXPORT_C TAny* RHeap::Alloc(TInt aSize) -{ - const MAllocator* m = this; - return ((MAllocator*)m)->Alloc(aSize); -} - -UEXPORT_C void RHeap::Free(TAny* aCell) -{ - const MAllocator* m = this; - ((MAllocator*)m)->Free(aCell); -} - -UEXPORT_C TAny* RHeap::ReAlloc(TAny* aCell, TInt aSize, TInt aMode) -{ - const MAllocator* m = this; - return ((MAllocator*)m)->ReAlloc(aCell, aSize, aMode); -} - -UEXPORT_C TInt RHeap::DebugFunction(TInt aFunc, TAny* a1, TAny* a2) -{ - const MAllocator* m = this; - return ((MAllocator*)m)->DebugFunction(aFunc, a1, a2); -} - -UEXPORT_C TInt RHeap::Extension_(TUint aExtensionId, TAny*& a0, TAny* a1) -{ - const MAllocator* m = this; - return ((MAllocator*)m)->Extension_(aExtensionId, a0, a1); -} - -#ifndef __KERNEL_MODE__ - -EXPORT_C TInt RHeap::AllocSize(TInt& aTotalAllocSize) const -{ - const MAllocator* m = this; - return m->AllocSize(aTotalAllocSize); -} - -EXPORT_C TInt RHeap::Available(TInt& aBiggestBlock) const -{ - const MAllocator* m = this; - return m->Available(aBiggestBlock); -} - -EXPORT_C void RHeap::Reset() -{ - const MAllocator* m = this; - ((MAllocator*)m)->Reset(); -} - -EXPORT_C TInt RHeap::Compress() -{ - const MAllocator* m = this; - return ((MAllocator*)m)->Compress(); -} -#endif - -RHybridHeap::RHybridHeap() - { - // This initialisation cannot be done in RHeap() for compatibility reasons - iMaxLength = iChunkHandle = iNestingLevel = 0; - iTop = NULL; - iFailType = ENone; - iTestData = NULL; - } - -void RHybridHeap::operator delete(TAny*, TAny*) -/** -Called if constructor issued by operator new(TUint aSize, TAny* aBase) throws exception. -This is dummy as corresponding new operator does not allocate memory. -*/ -{} - - -#ifndef __KERNEL_MODE__ -void RHybridHeap::Lock() const - /** - @internalComponent -*/ - {((RFastLock&)iLock).Wait();} - - -void RHybridHeap::Unlock() const - /** - @internalComponent -*/ - {((RFastLock&)iLock).Signal();} - - -TInt RHybridHeap::ChunkHandle() const - /** - @internalComponent -*/ -{ - return iChunkHandle; -} - -#else -// -// This method is implemented in kheap.cpp -// -//void RHybridHeap::Lock() const - /** - @internalComponent -*/ -// {;} - - - -// -// This method is implemented in kheap.cpp -// -//void RHybridHeap::Unlock() const - /** - @internalComponent -*/ -// {;} - - -TInt RHybridHeap::ChunkHandle() const - /** - @internalComponent -*/ -{ - return 0; -} -#endif - -RHybridHeap::RHybridHeap(TInt aChunkHandle, TInt aOffset, TInt aMinLength, TInt aMaxLength, TInt aGrowBy, TInt aAlign, TBool aSingleThread, TBool aDLOnly, TBool aUseAdjust) -/** -Constructor for a non fixed heap. Unlike the fixed heap, this heap is quite flexible in terms of its minimum and -maximum lengths and in that it can use the hybrid allocator if all of its requirements are met. -*/ - : iOffset(aOffset), iChunkSize(aMinLength) - { - __ASSERT_ALWAYS(iOffset>=0, HEAP_PANIC(ETHeapNewBadOffset)); - - iChunkHandle = aChunkHandle; - iMinLength = aMinLength; - iMaxLength = aMaxLength; - - // If the user has explicitly specified 0 as the aGrowBy value, set it to 1 so that it will be rounded up to the nearst page size - if (aGrowBy == 0) - aGrowBy = 1; - GET_PAGE_SIZE(iPageSize); - iGrowBy = _ALIGN_UP(aGrowBy, iPageSize); - - Construct(aSingleThread, aDLOnly, aUseAdjust, aAlign); - } - -RHybridHeap::RHybridHeap(TInt aMaxLength, TInt aAlign, TBool aSingleThread) -/** -Constructor for a fixed heap. We have restrictions in that we have fixed minimum and maximum lengths and cannot grow -and we only use DL allocator. -*/ - : iOffset(0), iChunkSize(aMaxLength) - { - iChunkHandle = NULL; - iMinLength = aMaxLength; - iMaxLength = aMaxLength; - iGrowBy = 0; - - Construct(aSingleThread, ETrue, ETrue, aAlign); - } - -TAny* RHybridHeap::operator new(TUint aSize, TAny* aBase) __NO_THROW -{ - __ASSERT_ALWAYS(aSize>=sizeof(RHybridHeap), HEAP_PANIC(ETHeapNewBadSize)); - RHybridHeap* h = (RHybridHeap*)aBase; - h->iBase = ((TUint8*)aBase) + aSize; - return aBase; -} - -void RHybridHeap::Construct(TBool aSingleThread, TBool aDLOnly, TBool aUseAdjust, TInt aAlign) -{ - iAlign = aAlign ? aAlign : RHybridHeap::ECellAlignment; - __ASSERT_ALWAYS((TUint32)iAlign>=sizeof(TAny*) && __POWER_OF_2(iAlign), HEAP_PANIC(ETHeapNewBadAlignment)); - - // This initialisation cannot be done in RHeap() for compatibility reasons - iTop = NULL; - iFailType = ENone; - iNestingLevel = 0; - iTestData = NULL; - - iHighWaterMark = iMinLength; - iAllocCount = 0; - iFlags = aSingleThread ? ESingleThreaded : 0; - iGrowBy = _ALIGN_UP(iGrowBy, iPageSize); - - if ( iMinLength == iMaxLength ) - { - iFlags |= EFixedSize; - aDLOnly = ETrue; - } -#ifndef __KERNEL_MODE__ -#ifdef DELAYED_SLAB_THRESHOLD - iSlabInitThreshold = DELAYED_SLAB_THRESHOLD; -#else - iSlabInitThreshold = 0; -#endif // DELAYED_SLAB_THRESHOLD - iUseAdjust = aUseAdjust; - iDLOnly = aDLOnly; -#else - (void)aUseAdjust; -#endif - // Initialise suballocators - // if DL only is required then it cannot allocate slab or page memory - // so these sub-allocators should be disabled. Otherwise initialise with default values - if ( aDLOnly ) - { - Init(0, 0); - } - else - { - Init(SLAB_CONFIG, 16); - } - -#ifdef ENABLE_BTRACE - - TUint32 traceData[4]; - traceData[0] = iMinLength; - traceData[1] = iMaxLength; - traceData[2] = iGrowBy; - traceData[3] = iAlign; - BTraceContextN(BTrace::ETest1, 90, (TUint32)this, 11, traceData, sizeof(traceData)); -#endif - -} - -#ifndef __KERNEL_MODE__ -TInt RHybridHeap::ConstructLock(TUint32 aMode) -{ - TBool duplicateLock = EFalse; - TInt r = KErrNone; - if (!(iFlags & ESingleThreaded)) - { - duplicateLock = aMode & UserHeap::EChunkHeapSwitchTo; - r = iLock.CreateLocal(duplicateLock ? EOwnerThread : EOwnerProcess); - if( r != KErrNone) - { - iChunkHandle = 0; - return r; - } - } - - if ( aMode & UserHeap::EChunkHeapSwitchTo ) - User::SwitchHeap(this); - - iHandles = &iChunkHandle; - if (!(iFlags & ESingleThreaded)) - { - // now change the thread-relative chunk/semaphore handles into process-relative handles - iHandleCount = 2; - if(duplicateLock) - { - RHandleBase s = iLock; - r = iLock.Duplicate(RThread()); - s.Close(); - } - if (r==KErrNone && (aMode & UserHeap::EChunkHeapDuplicate)) - { - r = ((RChunk*)&iChunkHandle)->Duplicate(RThread()); - if (r!=KErrNone) - iLock.Close(), iChunkHandle=0; - } - } - else - { - iHandleCount = 1; - if (aMode & UserHeap::EChunkHeapDuplicate) - r = ((RChunk*)&iChunkHandle)->Duplicate(RThread(), EOwnerThread); - } - - return r; -} -#endif - -void RHybridHeap::Init(TInt aBitmapSlab, TInt aPagePower) -{ - /*Moved code which does initilization */ - iTop = (TUint8*)this + iMinLength; - iBase = Ceiling(iBase, ECellAlignment); // Align iBase address - - __INIT_COUNTERS(0); - // memset(&mparams,0,sizeof(mparams)); - - InitDlMalloc(iTop - iBase, 0); - -#ifndef __KERNEL_MODE__ - SlabInit(); - iSlabConfigBits = aBitmapSlab; - if ( iChunkSize > iSlabInitThreshold ) - { - iSlabInitThreshold = KMaxTInt32; - SlabConfig(aBitmapSlab); // Delayed slab configuration done - } - if ( aPagePower ) - { - RChunk chunk; - chunk.SetHandle(iChunkHandle); - iMemBase = chunk.Base(); // Store base address for paged allocator - } - - /*10-1K,11-2K,12-4k,13-8K,14-16K,15-32K,16-64K*/ - PagedInit(aPagePower); - -#ifdef ENABLE_BTRACE - TUint32 traceData[3]; - traceData[0] = aBitmapSlab; - traceData[1] = aPagePower; - traceData[2] = GM->iTrimCheck; - BTraceContextN(BTrace::ETest1, 90, (TUint32)this, 0, traceData, sizeof(traceData)); -#endif -#else - (void)aBitmapSlab; - (void)aPagePower; -#endif // __KERNEL_MODE__ - -} - - -TInt RHybridHeap::AllocLen(const TAny* aCell) const -{ - aCell = __GET_DEBUG_DATA_BFR(aCell); - - if (PtrDiff(aCell, this) >= 0) - { - mchunkptr m = MEM2CHUNK(aCell); - return CHUNKSIZE(m) - OVERHEAD_FOR(m) - __DEBUG_HDR_SIZE; - } -#ifndef __KERNEL_MODE__ - if ( aCell ) - { - if (LowBits(aCell, iPageSize) ) - return SlabHeaderSize(slab::SlabFor(aCell)->iHeader) - __DEBUG_HDR_SIZE; - - return PagedSize((void*)aCell) - __DEBUG_HDR_SIZE; - } -#endif - return 0; // NULL pointer situation, should PANIC !! -} - -#ifdef __KERNEL_MODE__ -TAny* RHybridHeap::Alloc(TInt aSize) -{ - __CHECK_THREAD_STATE; - __ASSERT_ALWAYS((TUint)aSize<(KMaxTInt/2),HEAP_PANIC(ETHeapBadAllocatedCellSize)); - __SIMULATE_ALLOC_FAIL(return NULL;) - Lock(); - __ALLOC_DEBUG_HEADER(aSize); - TAny* addr = DlMalloc(aSize); - if ( addr ) - { -// iCellCount++; - __SET_DEBUG_DATA(addr, iNestingLevel, ++iAllocCount); - addr = __GET_USER_DATA_BFR(addr); - __INCREMENT_COUNTERS(addr); - memclr(addr, AllocLen(addr)); - } - Unlock(); -#ifdef ENABLE_BTRACE - if (iFlags & ETraceAllocs) - { - if ( addr ) - { - TUint32 traceData[3]; - traceData[0] = AllocLen(addr); - traceData[1] = aSize - __DEBUG_HDR_SIZE; - traceData[2] = 0; - BTraceContextN(BTrace::EHeap, BTrace::EHeapAlloc, (TUint32)this, (TUint32)addr, traceData, sizeof(traceData)); - } - else - BTraceContext8(BTrace::EHeap, BTrace::EHeapAllocFail, (TUint32)this, (TUint32)(aSize - __DEBUG_HDR_SIZE)); - } -#endif - return addr; -} -#else - -TAny* RHybridHeap::Alloc(TInt aSize) -{ - __ASSERT_ALWAYS((TUint)aSize<(KMaxTInt/2),HEAP_PANIC(ETHeapBadAllocatedCellSize)); - __SIMULATE_ALLOC_FAIL(return NULL;) - - TAny* addr; -#ifdef ENABLE_BTRACE - TInt aSubAllocator=0; -#endif - - Lock(); - - __ALLOC_DEBUG_HEADER(aSize); - - if (aSize < iSlabThreshold) - { - TInt ix = iSizeMap[(aSize+3)>>2]; - HEAP_ASSERT(ix != 0xff); - addr = SlabAllocate(iSlabAlloc[ix]); - if ( !addr ) - { // Slab allocation has failed, try to allocate from DL - addr = DlMalloc(aSize); - } -#ifdef ENABLE_BTRACE - else - aSubAllocator=1; -#endif - }else if((aSize >> iPageThreshold)==0) - { - addr = DlMalloc(aSize); - } - else - { - addr = PagedAllocate(aSize); - if ( !addr ) - { // Page allocation has failed, try to allocate from DL - addr = DlMalloc(aSize); - } -#ifdef ENABLE_BTRACE - else - aSubAllocator=2; -#endif - } - - if ( addr ) - { -// iCellCount++; - __SET_DEBUG_DATA(addr, iNestingLevel, ++iAllocCount); - addr = __GET_USER_DATA_BFR(addr); - __INCREMENT_COUNTERS(addr); - } - Unlock(); - -#ifdef ENABLE_BTRACE - if (iFlags & ETraceAllocs) - { - if ( addr ) - { - TUint32 traceData[3]; - traceData[0] = AllocLen(addr); - traceData[1] = aSize - __DEBUG_HDR_SIZE; - traceData[2] = aSubAllocator; - BTraceContextN(BTrace::EHeap, BTrace::EHeapAlloc, (TUint32)this, (TUint32)addr, traceData, sizeof(traceData)); - } - else - BTraceContext8(BTrace::EHeap, BTrace::EHeapAllocFail, (TUint32)this, (TUint32)(aSize - __DEBUG_HDR_SIZE)); - } -#endif - - return addr; -} -#endif // __KERNEL_MODE__ - -#ifndef __KERNEL_MODE__ -TInt RHybridHeap::Compress() -{ - if ( IS_FIXED_HEAP ) - return 0; - - Lock(); - TInt Reduced = SysTrim(GM, 0); - if (iSparePage) - { - Unmap(iSparePage, iPageSize); - iSparePage = 0; - Reduced += iPageSize; - } - Unlock(); - return Reduced; -} -#endif - -void RHybridHeap::Free(TAny* aPtr) -{ - __CHECK_THREAD_STATE; - if ( !aPtr ) - return; -#ifdef ENABLE_BTRACE - TInt aSubAllocator=0; -#endif - Lock(); - - aPtr = __GET_DEBUG_DATA_BFR(aPtr); - -#ifndef __KERNEL_MODE__ - if (PtrDiff(aPtr, this) >= 0) - { -#endif - __DL_BFR_CHECK(GM, aPtr); - __DECREMENT_COUNTERS(__GET_USER_DATA_BFR(aPtr)); - __ZAP_CELL(aPtr); - DlFree( aPtr); -#ifndef __KERNEL_MODE__ - } - - else if ( LowBits(aPtr, iPageSize) == 0 ) - { -#ifdef ENABLE_BTRACE - aSubAllocator = 2; -#endif - __PAGE_BFR_CHECK(aPtr); - __DECREMENT_COUNTERS(__GET_USER_DATA_BFR(aPtr)); - PagedFree(aPtr); - } - else - { -#ifdef ENABLE_BTRACE - aSubAllocator = 1; -#endif - TUint32 bm[4]; - __SLAB_BFR_CHECK(slab::SlabFor(aPtr),aPtr,bm); - __DECREMENT_COUNTERS(__GET_USER_DATA_BFR(aPtr)); - __ZAP_CELL(aPtr); - SlabFree(aPtr); - } -#endif // __KERNEL_MODE__ -// iCellCount--; - Unlock(); -#ifdef ENABLE_BTRACE - if (iFlags & ETraceAllocs) - { - TUint32 traceData; - traceData = aSubAllocator; - BTraceContextN(BTrace::EHeap, BTrace::EHeapFree, (TUint32)this, (TUint32)__GET_USER_DATA_BFR(aPtr), &traceData, sizeof(traceData)); - } -#endif -} - -#ifndef __KERNEL_MODE__ -void RHybridHeap::Reset() -/** -Frees all allocated cells on this heap. -*/ -{ - Lock(); - if ( !IS_FIXED_HEAP ) - { - if ( GM->iSeg.iSize > (iMinLength - sizeof(*this)) ) - Unmap(GM->iSeg.iBase + (iMinLength - sizeof(*this)), (GM->iSeg.iSize - (iMinLength - sizeof(*this)))); - ResetBitmap(); - if ( !iDLOnly ) - Init(iSlabConfigBits, iPageThreshold); - else - Init(0,0); - } - else Init(0,0); - Unlock(); -} -#endif - -TAny* RHybridHeap::ReAllocImpl(TAny* aPtr, TInt aSize, TInt aMode) -{ - // First handle special case of calling reallocate with NULL aPtr - if (!aPtr) - { - if (( aMode & ENeverMove ) == 0 ) - { - aPtr = Alloc(aSize - __DEBUG_HDR_SIZE); - aPtr = __GET_DEBUG_DATA_BFR(aPtr); - } - return aPtr; - } - - TInt oldsize = AllocLen(__GET_USER_DATA_BFR(aPtr)) + __DEBUG_HDR_SIZE; - - // Insist on geometric growth when reallocating memory, this reduces copying and fragmentation - // generated during arithmetic growth of buffer/array/vector memory - // Experiments have shown that 25% is a good threshold for this policy - if (aSize <= oldsize) - { - if (aSize >= oldsize - (oldsize>>2)) - return aPtr; // don't change if >75% original size - } - else - { - __SIMULATE_ALLOC_FAIL(return NULL;) - if (aSize < oldsize + (oldsize>>2)) - { - aSize = _ALIGN_UP(oldsize + (oldsize>>2), 4); // grow to at least 125% original size - } - } - __DEBUG_SAVE(aPtr); - - TAny* newp; -#ifdef __KERNEL_MODE__ - Lock(); - __DL_BFR_CHECK(GM, aPtr); - newp = DlRealloc(aPtr, aSize, aMode); - Unlock(); - if ( newp ) - { - if ( aSize > oldsize ) - memclr(((TUint8*)newp) + oldsize, (aSize-oldsize)); // Buffer has grown in place, clear extra - __DEBUG_RESTORE(newp); - __UPDATE_ALLOC_COUNT(aPtr, newp, ++iAllocCount); - __UPDATE_TOTAL_ALLOC(newp, oldsize); - } -#else - // Decide how to reallocate based on (a) the current cell location, (b) the mode requested and (c) the new size - if ( PtrDiff(aPtr, this) >= 0 ) - { // current cell in Doug Lea iArena - if ( (aMode & ENeverMove) - || - (!(aMode & EAllowMoveOnShrink) && (aSize < oldsize)) - || - ((aSize >= iSlabThreshold) && ((aSize >> iPageThreshold) == 0)) ) - { - Lock(); - __DL_BFR_CHECK(GM, aPtr); - newp = DlRealloc(aPtr, aSize, aMode); // old and new in DL allocator - Unlock(); - __DEBUG_RESTORE(newp); - __UPDATE_ALLOC_COUNT(aPtr,newp, ++iAllocCount); - __UPDATE_TOTAL_ALLOC(newp, oldsize); - return newp; - } - } - else if (LowBits(aPtr, iPageSize) == 0) - { // current cell in paged iArena - if ( (aMode & ENeverMove) - || - (!(aMode & EAllowMoveOnShrink) && (aSize < oldsize)) - || - ((aSize >> iPageThreshold) != 0) ) - { - Lock(); - __PAGE_BFR_CHECK(aPtr); - newp = PagedReallocate(aPtr, aSize, aMode); // old and new in paged allocator - Unlock(); - __DEBUG_RESTORE(newp); - __UPDATE_ALLOC_COUNT(aPtr,newp, ++iAllocCount); - __UPDATE_TOTAL_ALLOC(newp, oldsize); - return newp; - } - } - else - { // current cell in slab iArena - TUint32 bm[4]; - Lock(); - __SLAB_BFR_CHECK(slab::SlabFor(aPtr), aPtr, bm); - Unlock(); - if ( aSize <= oldsize) - return aPtr; - if (aMode & ENeverMove) - return NULL; // cannot grow in slab iArena - // just use alloc/copy/free... - } - - // fallback to allocate and copy - // shouldn't get here if we cannot move the cell - // __ASSERT(mode == emobile || (mode==efixshrink && size>oldsize)); - - newp = Alloc(aSize - __DEBUG_HDR_SIZE); - newp = __GET_DEBUG_DATA_BFR(newp); - if (newp) - { - memcpy(newp, aPtr, oldsize -//#define DEBUG_REALLOC -#ifdef DEBUG_REALLOC -#include -#endif - -inline void RHybridHeap::InitBins(mstate m) -{ - /* Establish circular links for iSmallBins */ - bindex_t i; - for (i = 0; i < NSMALLBINS; ++i) { - sbinptr bin = SMALLBIN_AT(m,i); - bin->iFd = bin->iBk = bin; - } - } -/* ---------------------------- malloc support --------------------------- */ - -/* allocate a large request from the best fitting chunk in a treebin */ -void* RHybridHeap::TmallocLarge(mstate m, size_t nb) { - tchunkptr v = 0; - size_t rsize = -nb; /* Unsigned negation */ - tchunkptr t; - bindex_t idx; - ComputeTreeIndex(nb, idx); - - if ((t = *TREEBIN_AT(m, idx)) != 0) - { - /* Traverse tree for this bin looking for node with size == nb */ - size_t sizebits = nb << LEFTSHIFT_FOR_TREE_INDEX(idx); - tchunkptr rst = 0; /* The deepest untaken right subtree */ - for (;;) - { - tchunkptr rt; - size_t trem = CHUNKSIZE(t) - nb; - if (trem < rsize) - { - v = t; - if ((rsize = trem) == 0) - break; - } - rt = t->iChild[1]; - t = t->iChild[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; - if (rt != 0 && rt != t) - rst = rt; - if (t == 0) - { - t = rst; /* set t to least subtree holding sizes > nb */ - break; - } - sizebits <<= 1; - } - } - if (t == 0 && v == 0) - { /* set t to root of next non-empty treebin */ - binmap_t leftbits = LEFT_BITS(IDX2BIT(idx)) & m->iTreeMap; - if (leftbits != 0) - { - bindex_t i; - binmap_t leastbit = LEAST_BIT(leftbits); - ComputeBit2idx(leastbit, i); - t = *TREEBIN_AT(m, i); - } - } - while (t != 0) - { /* Find smallest of tree or subtree */ - size_t trem = CHUNKSIZE(t) - nb; - if (trem < rsize) { - rsize = trem; - v = t; - } - t = LEFTMOST_CHILD(t); - } - /* If iDv is a better fit, return 0 so malloc will use it */ - if (v != 0 && rsize < (size_t)(m->iDvSize - nb)) - { - if (RTCHECK(OK_ADDRESS(m, v))) - { /* split */ - mchunkptr r = CHUNK_PLUS_OFFSET(v, nb); - HEAP_ASSERT(CHUNKSIZE(v) == rsize + nb); - if (RTCHECK(OK_NEXT(v, r))) - { - UnlinkLargeChunk(m, v); - if (rsize < MIN_CHUNK_SIZE) - SET_INUSE_AND_PINUSE(m, v, (rsize + nb)); - else - { - SET_SIZE_AND_PINUSE_OF_INUSE_CHUNK(m, v, nb); - SET_SIZE_AND_PINUSE_OF_FREE_CHUNK(r, rsize); - InsertChunk(m, r, rsize); - } - return CHUNK2MEM(v); - } - } - // CORRUPTION_ERROR_ACTION(m); - } - return 0; - } - -/* allocate a small request from the best fitting chunk in a treebin */ -void* RHybridHeap::TmallocSmall(mstate m, size_t nb) -{ - tchunkptr t, v; - size_t rsize; - bindex_t i; - binmap_t leastbit = LEAST_BIT(m->iTreeMap); - ComputeBit2idx(leastbit, i); - - v = t = *TREEBIN_AT(m, i); - rsize = CHUNKSIZE(t) - nb; - - while ((t = LEFTMOST_CHILD(t)) != 0) - { - size_t trem = CHUNKSIZE(t) - nb; - if (trem < rsize) - { - rsize = trem; - v = t; - } - } - - if (RTCHECK(OK_ADDRESS(m, v))) - { - mchunkptr r = CHUNK_PLUS_OFFSET(v, nb); - HEAP_ASSERT(CHUNKSIZE(v) == rsize + nb); - if (RTCHECK(OK_NEXT(v, r))) - { - UnlinkLargeChunk(m, v); - if (rsize < MIN_CHUNK_SIZE) - SET_INUSE_AND_PINUSE(m, v, (rsize + nb)); - else - { - SET_SIZE_AND_PINUSE_OF_INUSE_CHUNK(m, v, nb); - SET_SIZE_AND_PINUSE_OF_FREE_CHUNK(r, rsize); - ReplaceDv(m, r, rsize); - } - return CHUNK2MEM(v); - } - } - // CORRUPTION_ERROR_ACTION(m); - // return 0; - } - -inline void RHybridHeap::InitTop(mstate m, mchunkptr p, size_t psize) -{ - /* Ensure alignment */ - size_t offset = ALIGN_OFFSET(CHUNK2MEM(p)); - p = (mchunkptr)((TUint8*)p + offset); - psize -= offset; - m->iTop = p; - m->iTopSize = psize; - p->iHead = psize | PINUSE_BIT; - /* set size of fake trailing chunk holding overhead space only once */ - mchunkptr chunkPlusOff = CHUNK_PLUS_OFFSET(p, psize); - chunkPlusOff->iHead = TOP_FOOT_SIZE; - m->iTrimCheck = KHeapShrinkHysRatio*(iGrowBy>>8); -} - - -/* Unlink the first chunk from a smallbin */ -inline void RHybridHeap::UnlinkFirstSmallChunk(mstate M,mchunkptr B,mchunkptr P,bindex_t& I) -{ - mchunkptr F = P->iFd; - HEAP_ASSERT(P != B); - HEAP_ASSERT(P != F); - HEAP_ASSERT(CHUNKSIZE(P) == SMALL_INDEX2SIZE(I)); - if (B == F) - CLEAR_SMALLMAP(M, I); - else if (RTCHECK(OK_ADDRESS(M, F))) - { - B->iFd = F; - F->iBk = B; - } - else - { - CORRUPTION_ERROR_ACTION(M); - } -} -/* Link a free chunk into a smallbin */ -inline void RHybridHeap::InsertSmallChunk(mstate M,mchunkptr P, size_t S) -{ - bindex_t I = SMALL_INDEX(S); - mchunkptr B = SMALLBIN_AT(M, I); - mchunkptr F = B; - HEAP_ASSERT(S >= MIN_CHUNK_SIZE); - if (!SMALLMAP_IS_MARKED(M, I)) - MARK_SMALLMAP(M, I); - else if (RTCHECK(OK_ADDRESS(M, B->iFd))) - F = B->iFd; - else - { - CORRUPTION_ERROR_ACTION(M); - } - B->iFd = P; - F->iBk = P; - P->iFd = F; - P->iBk = B; -} - - -inline void RHybridHeap::InsertChunk(mstate M,mchunkptr P,size_t S) -{ - if (IS_SMALL(S)) - InsertSmallChunk(M, P, S); - else - { - tchunkptr TP = (tchunkptr)(P); InsertLargeChunk(M, TP, S); - } -} - -inline void RHybridHeap::UnlinkLargeChunk(mstate M,tchunkptr X) -{ - tchunkptr XP = X->iParent; - tchunkptr R; - if (X->iBk != X) - { - tchunkptr F = X->iFd; - R = X->iBk; - if (RTCHECK(OK_ADDRESS(M, F))) - { - F->iBk = R; - R->iFd = F; - } - else - { - CORRUPTION_ERROR_ACTION(M); - } - } - else - { - tchunkptr* RP; - if (((R = *(RP = &(X->iChild[1]))) != 0) || - ((R = *(RP = &(X->iChild[0]))) != 0)) - { - tchunkptr* CP; - while ((*(CP = &(R->iChild[1])) != 0) || - (*(CP = &(R->iChild[0])) != 0)) - { - R = *(RP = CP); - } - if (RTCHECK(OK_ADDRESS(M, RP))) - *RP = 0; - else - { - CORRUPTION_ERROR_ACTION(M); - } - } - } - if (XP != 0) - { - tbinptr* H = TREEBIN_AT(M, X->iIndex); - if (X == *H) - { - if ((*H = R) == 0) - CLEAR_TREEMAP(M, X->iIndex); - } - else if (RTCHECK(OK_ADDRESS(M, XP))) - { - if (XP->iChild[0] == X) - XP->iChild[0] = R; - else - XP->iChild[1] = R; - } - else - CORRUPTION_ERROR_ACTION(M); - if (R != 0) - { - if (RTCHECK(OK_ADDRESS(M, R))) - { - tchunkptr C0, C1; - R->iParent = XP; - if ((C0 = X->iChild[0]) != 0) - { - if (RTCHECK(OK_ADDRESS(M, C0))) - { - R->iChild[0] = C0; - C0->iParent = R; - } - else - CORRUPTION_ERROR_ACTION(M); - } - if ((C1 = X->iChild[1]) != 0) - { - if (RTCHECK(OK_ADDRESS(M, C1))) - { - R->iChild[1] = C1; - C1->iParent = R; - } - else - CORRUPTION_ERROR_ACTION(M); - } - } - else - CORRUPTION_ERROR_ACTION(M); - } - } -} - -/* Unlink a chunk from a smallbin */ -inline void RHybridHeap::UnlinkSmallChunk(mstate M, mchunkptr P,size_t S) -{ - mchunkptr F = P->iFd; - mchunkptr B = P->iBk; - bindex_t I = SMALL_INDEX(S); - HEAP_ASSERT(P != B); - HEAP_ASSERT(P != F); - HEAP_ASSERT(CHUNKSIZE(P) == SMALL_INDEX2SIZE(I)); - if (F == B) - CLEAR_SMALLMAP(M, I); - else if (RTCHECK((F == SMALLBIN_AT(M,I) || OK_ADDRESS(M, F)) && - (B == SMALLBIN_AT(M,I) || OK_ADDRESS(M, B)))) - { - F->iBk = B; - B->iFd = F; - } - else - { - CORRUPTION_ERROR_ACTION(M); - } -} - -inline void RHybridHeap::UnlinkChunk(mstate M, mchunkptr P, size_t S) -{ - if (IS_SMALL(S)) - UnlinkSmallChunk(M, P, S); - else - { - tchunkptr TP = (tchunkptr)(P); UnlinkLargeChunk(M, TP); - } -} - -// For DL debug functions -void RHybridHeap::DoComputeTreeIndex(size_t S, bindex_t& I) -{ - ComputeTreeIndex(S, I); -} - -inline void RHybridHeap::ComputeTreeIndex(size_t S, bindex_t& I) -{ - size_t X = S >> TREEBIN_SHIFT; - if (X == 0) - I = 0; - else if (X > 0xFFFF) - I = NTREEBINS-1; - else - { - unsigned int Y = (unsigned int)X; - unsigned int N = ((Y - 0x100) >> 16) & 8; - unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4; - N += K; - N += K = (((Y <<= K) - 0x4000) >> 16) & 2; - K = 14 - N + ((Y <<= K) >> 15); - I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)); - } -} - -/* ------------------------- Operations on trees ------------------------- */ - -/* Insert chunk into tree */ -inline void RHybridHeap::InsertLargeChunk(mstate M,tchunkptr X,size_t S) -{ - tbinptr* H; - bindex_t I; - ComputeTreeIndex(S, I); - H = TREEBIN_AT(M, I); - X->iIndex = I; - X->iChild[0] = X->iChild[1] = 0; - if (!TREEMAP_IS_MARKED(M, I)) - { - MARK_TREEMAP(M, I); - *H = X; - X->iParent = (tchunkptr)H; - X->iFd = X->iBk = X; - } - else - { - tchunkptr T = *H; - size_t K = S << LEFTSHIFT_FOR_TREE_INDEX(I); - for (;;) - { - if (CHUNKSIZE(T) != S) { - tchunkptr* C = &(T->iChild[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]); - K <<= 1; - if (*C != 0) - T = *C; - else if (RTCHECK(OK_ADDRESS(M, C))) - { - *C = X; - X->iParent = T; - X->iFd = X->iBk = X; - break; - } - else - { - CORRUPTION_ERROR_ACTION(M); - break; - } - } - else - { - tchunkptr F = T->iFd; - if (RTCHECK(OK_ADDRESS(M, T) && OK_ADDRESS(M, F))) - { - T->iFd = F->iBk = X; - X->iFd = F; - X->iBk = T; - X->iParent = 0; - break; - } - else - { - CORRUPTION_ERROR_ACTION(M); - break; - } - } - } - } -} - -/* -Unlink steps: - -1. If x is a chained node, unlink it from its same-sized iFd/iBk links -and choose its iBk node as its replacement. -2. If x was the last node of its size, but not a leaf node, it must -be replaced with a leaf node (not merely one with an open left or -right), to make sure that lefts and rights of descendents -correspond properly to bit masks. We use the rightmost descendent -of x. We could use any other leaf, but this is easy to locate and -tends to counteract removal of leftmosts elsewhere, and so keeps -paths shorter than minimally guaranteed. This doesn't loop much -because on average a node in a tree is near the bottom. -3. If x is the base of a chain (i.e., has iParent links) relink -x's iParent and children to x's replacement (or null if none). -*/ - -/* Replace iDv node, binning the old one */ -/* Used only when iDvSize known to be small */ -inline void RHybridHeap::ReplaceDv(mstate M, mchunkptr P, size_t S) -{ - size_t DVS = M->iDvSize; - if (DVS != 0) - { - mchunkptr DV = M->iDv; - HEAP_ASSERT(IS_SMALL(DVS)); - InsertSmallChunk(M, DV, DVS); - } - M->iDvSize = S; - M->iDv = P; -} - - -inline void RHybridHeap::ComputeBit2idx(binmap_t X,bindex_t& I) -{ - unsigned int Y = X - 1; - unsigned int K = Y >> (16-4) & 16; - unsigned int N = K; Y >>= K; - N += K = Y >> (8-3) & 8; Y >>= K; - N += K = Y >> (4-2) & 4; Y >>= K; - N += K = Y >> (2-1) & 2; Y >>= K; - N += K = Y >> (1-0) & 1; Y >>= K; - I = (bindex_t)(N + Y); -} - - - -int RHybridHeap::SysTrim(mstate m, size_t pad) -{ - size_t extra = 0; - - if ( IS_INITIALIZED(m) ) - { - pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */ - - if (m->iTopSize > pad) - { - extra = Floor(m->iTopSize - pad, iPageSize); - if ( (m->iSeg.iSize - extra) < (iMinLength - sizeof(*this)) ) - { - if ( m->iSeg.iSize > (iMinLength - sizeof(*this)) ) - extra = Floor(m->iSeg.iSize - (iMinLength - sizeof(*this)), iPageSize); /* do not shrink heap below min length */ - else extra = 0; - } - - if ( extra ) - { - Unmap(m->iSeg.iBase + m->iSeg.iSize - extra, extra); - - m->iSeg.iSize -= extra; - InitTop(m, m->iTop, m->iTopSize - extra); - CHECK_TOP_CHUNK(m, m->iTop); - } - } - - } - - return extra; -} - -/* Get memory from system using MORECORE */ - -void* RHybridHeap::SysAlloc(mstate m, size_t nb) -{ - HEAP_ASSERT(m->iTop); - /* Subtract out existing available iTop space from MORECORE request. */ -// size_t asize = _ALIGN_UP(nb - m->iTopSize + TOP_FOOT_SIZE + SIZE_T_ONE, iGrowBy); - TInt asize = _ALIGN_UP(nb - m->iTopSize + SYS_ALLOC_PADDING, iGrowBy); // From DLA version 2.8.4 - - char* br = (char*)Map(m->iSeg.iBase+m->iSeg.iSize, asize); - if (!br) - return 0; - HEAP_ASSERT(br == (char*)m->iSeg.iBase+m->iSeg.iSize); - - /* Merge with an existing segment */ - m->iSeg.iSize += asize; - InitTop(m, m->iTop, m->iTopSize + asize); - - if (nb < m->iTopSize) - { /* Allocate from new or extended iTop space */ - size_t rsize = m->iTopSize -= nb; - mchunkptr p = m->iTop; - mchunkptr r = m->iTop = CHUNK_PLUS_OFFSET(p, nb); - r->iHead = rsize | PINUSE_BIT; - SET_SIZE_AND_PINUSE_OF_INUSE_CHUNK(m, p, nb); - CHECK_TOP_CHUNK(m, m->iTop); - CHECK_MALLOCED_CHUNK(m, CHUNK2MEM(p), nb); - return CHUNK2MEM(p); - } - - return 0; -} - - -void RHybridHeap::InitDlMalloc(size_t capacity, int /*locked*/) -{ - memset(GM,0,sizeof(malloc_state)); - // The maximum amount that can be allocated can be calculated as:- - // 2^sizeof(size_t) - sizeof(malloc_state) - TOP_FOOT_SIZE - page Size(all accordingly padded) - // If the capacity exceeds this, no allocation will be done. - GM->iSeg.iBase = iBase; - GM->iSeg.iSize = capacity; - InitBins(GM); - InitTop(GM, (mchunkptr)iBase, capacity - TOP_FOOT_SIZE); -} - -void* RHybridHeap::DlMalloc(size_t bytes) -{ - /* - Basic algorithm: - If a small request (< 256 bytes minus per-chunk overhead): - 1. If one exists, use a remainderless chunk in associated smallbin. - (Remainderless means that there are too few excess bytes to - represent as a chunk.) - 2. If it is big enough, use the iDv chunk, which is normally the - chunk adjacent to the one used for the most recent small request. - 3. If one exists, split the smallest available chunk in a bin, - saving remainder in iDv. - 4. If it is big enough, use the iTop chunk. - 5. If available, get memory from system and use it - Otherwise, for a large request: - 1. Find the smallest available binned chunk that fits, and use it - if it is better fitting than iDv chunk, splitting if necessary. - 2. If better fitting than any binned chunk, use the iDv chunk. - 3. If it is big enough, use the iTop chunk. - 4. If request size >= mmap threshold, try to directly mmap this chunk. - 5. If available, get memory from system and use it -*/ - void* mem; - size_t nb; - if (bytes <= MAX_SMALL_REQUEST) - { - bindex_t idx; - binmap_t smallbits; - nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : PAD_REQUEST(bytes); - idx = SMALL_INDEX(nb); - smallbits = GM->iSmallMap >> idx; - - if ((smallbits & 0x3U) != 0) - { /* Remainderless fit to a smallbin. */ - mchunkptr b, p; - idx += ~smallbits & 1; /* Uses next bin if idx empty */ - b = SMALLBIN_AT(GM, idx); - p = b->iFd; - HEAP_ASSERT(CHUNKSIZE(p) == SMALL_INDEX2SIZE(idx)); - UnlinkFirstSmallChunk(GM, b, p, idx); - SET_INUSE_AND_PINUSE(GM, p, SMALL_INDEX2SIZE(idx)); - mem = CHUNK2MEM(p); - CHECK_MALLOCED_CHUNK(GM, mem, nb); - return mem; - } - - else if (nb > GM->iDvSize) - { - if (smallbits != 0) - { /* Use chunk in next nonempty smallbin */ - mchunkptr b, p, r; - size_t rsize; - bindex_t i; - binmap_t leftbits = (smallbits << idx) & LEFT_BITS(IDX2BIT(idx)); - binmap_t leastbit = LEAST_BIT(leftbits); - ComputeBit2idx(leastbit, i); - b = SMALLBIN_AT(GM, i); - p = b->iFd; - HEAP_ASSERT(CHUNKSIZE(p) == SMALL_INDEX2SIZE(i)); - UnlinkFirstSmallChunk(GM, b, p, i); - rsize = SMALL_INDEX2SIZE(i) - nb; - /* Fit here cannot be remainderless if 4byte sizes */ - if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) - SET_INUSE_AND_PINUSE(GM, p, SMALL_INDEX2SIZE(i)); - else - { - SET_SIZE_AND_PINUSE_OF_INUSE_CHUNK(GM, p, nb); - r = CHUNK_PLUS_OFFSET(p, nb); - SET_SIZE_AND_PINUSE_OF_FREE_CHUNK(r, rsize); - ReplaceDv(GM, r, rsize); - } - mem = CHUNK2MEM(p); - CHECK_MALLOCED_CHUNK(GM, mem, nb); - return mem; - } - - else if (GM->iTreeMap != 0 && (mem = TmallocSmall(GM, nb)) != 0) - { - CHECK_MALLOCED_CHUNK(GM, mem, nb); - return mem; - } - } - } - else if (bytes >= MAX_REQUEST) - nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ - else - { - nb = PAD_REQUEST(bytes); - if (GM->iTreeMap != 0 && (mem = TmallocLarge(GM, nb)) != 0) - { - CHECK_MALLOCED_CHUNK(GM, mem, nb); - return mem; - } - } - - if (nb <= GM->iDvSize) - { - size_t rsize = GM->iDvSize - nb; - mchunkptr p = GM->iDv; - if (rsize >= MIN_CHUNK_SIZE) - { /* split iDv */ - mchunkptr r = GM->iDv = CHUNK_PLUS_OFFSET(p, nb); - GM->iDvSize = rsize; - SET_SIZE_AND_PINUSE_OF_FREE_CHUNK(r, rsize); - SET_SIZE_AND_PINUSE_OF_INUSE_CHUNK(GM, p, nb); - } - else - { /* exhaust iDv */ - size_t dvs = GM->iDvSize; - GM->iDvSize = 0; - GM->iDv = 0; - SET_INUSE_AND_PINUSE(GM, p, dvs); - } - mem = CHUNK2MEM(p); - CHECK_MALLOCED_CHUNK(GM, mem, nb); - return mem; - } - - else if (nb < GM->iTopSize) - { /* Split iTop */ - size_t rsize = GM->iTopSize -= nb; - mchunkptr p = GM->iTop; - mchunkptr r = GM->iTop = CHUNK_PLUS_OFFSET(p, nb); - r->iHead = rsize | PINUSE_BIT; - SET_SIZE_AND_PINUSE_OF_INUSE_CHUNK(GM, p, nb); - mem = CHUNK2MEM(p); - CHECK_TOP_CHUNK(GM, GM->iTop); - CHECK_MALLOCED_CHUNK(GM, mem, nb); - return mem; - } - - return SysAlloc(GM, nb); -} - - -void RHybridHeap::DlFree(void* mem) -{ - /* - Consolidate freed chunks with preceeding or succeeding bordering - free chunks, if they exist, and then place in a bin. Intermixed - with special cases for iTop, iDv, mmapped chunks, and usage errors. -*/ - mchunkptr p = MEM2CHUNK(mem); - CHECK_INUSE_CHUNK(GM, p); - if (RTCHECK(OK_ADDRESS(GM, p) && OK_CINUSE(p))) - { - size_t psize = CHUNKSIZE(p); - mchunkptr next = CHUNK_PLUS_OFFSET(p, psize); - if (!PINUSE(p)) - { - size_t prevsize = p->iPrevFoot; - mchunkptr prev = CHUNK_MINUS_OFFSET(p, prevsize); - psize += prevsize; - p = prev; - if (RTCHECK(OK_ADDRESS(GM, prev))) - { /* consolidate backward */ - if (p != GM->iDv) - { - UnlinkChunk(GM, p, prevsize); - } - else if ((next->iHead & INUSE_BITS) == INUSE_BITS) - { - GM->iDvSize = psize; - SET_FREE_WITH_PINUSE(p, psize, next); - return; - } - } - else - { - USAGE_ERROR_ACTION(GM, p); - return; - } - } - - if (RTCHECK(OK_NEXT(p, next) && OK_PINUSE(next))) - { - if (!CINUSE(next)) - { /* consolidate forward */ - if (next == GM->iTop) - { - size_t tsize = GM->iTopSize += psize; - GM->iTop = p; - p->iHead = tsize | PINUSE_BIT; - if (p == GM->iDv) - { - GM->iDv = 0; - GM->iDvSize = 0; - } - if ( !IS_FIXED_HEAP && SHOULD_TRIM(GM, tsize) ) - SysTrim(GM, 0); - return; - } - else if (next == GM->iDv) - { - size_t dsize = GM->iDvSize += psize; - GM->iDv = p; - SET_SIZE_AND_PINUSE_OF_FREE_CHUNK(p, dsize); - return; - } - else - { - size_t nsize = CHUNKSIZE(next); - psize += nsize; - UnlinkChunk(GM, next, nsize); - SET_SIZE_AND_PINUSE_OF_FREE_CHUNK(p, psize); - if (p == GM->iDv) - { - GM->iDvSize = psize; - return; - } - } - } - else - SET_FREE_WITH_PINUSE(p, psize, next); - InsertChunk(GM, p, psize); - CHECK_FREE_CHUNK(GM, p); - return; - } - } -} - - -void* RHybridHeap::DlRealloc(void* oldmem, size_t bytes, TInt mode) -{ - mchunkptr oldp = MEM2CHUNK(oldmem); - size_t oldsize = CHUNKSIZE(oldp); - mchunkptr next = CHUNK_PLUS_OFFSET(oldp, oldsize); - mchunkptr newp = 0; - void* extra = 0; - - /* Try to either shrink or extend into iTop. Else malloc-copy-free */ - - if (RTCHECK(OK_ADDRESS(GM, oldp) && OK_CINUSE(oldp) && - OK_NEXT(oldp, next) && OK_PINUSE(next))) - { - size_t nb = REQUEST2SIZE(bytes); - if (oldsize >= nb) { /* already big enough */ - size_t rsize = oldsize - nb; - newp = oldp; - if (rsize >= MIN_CHUNK_SIZE) - { - mchunkptr remainder = CHUNK_PLUS_OFFSET(newp, nb); - SET_INUSE(GM, newp, nb); -// SET_INUSE(GM, remainder, rsize); - SET_INUSE_AND_PINUSE(GM, remainder, rsize); // corrected in original DLA version V2.8.4 - extra = CHUNK2MEM(remainder); - } - } - else if (next == GM->iTop && oldsize + GM->iTopSize > nb) - { - /* Expand into iTop */ - size_t newsize = oldsize + GM->iTopSize; - size_t newtopsize = newsize - nb; - mchunkptr newtop = CHUNK_PLUS_OFFSET(oldp, nb); - SET_INUSE(GM, oldp, nb); - newtop->iHead = newtopsize |PINUSE_BIT; - GM->iTop = newtop; - GM->iTopSize = newtopsize; - newp = oldp; - } - } - else - { - USAGE_ERROR_ACTION(GM, oldmem); - } - - if (newp != 0) - { - if (extra != 0) - { - DlFree(extra); - } - CHECK_INUSE_CHUNK(GM, newp); - return CHUNK2MEM(newp); - } - else - { - if ( mode & ENeverMove ) - return 0; // cannot move - void* newmem = DlMalloc(bytes); - if (newmem != 0) - { - size_t oc = oldsize - OVERHEAD_FOR(oldp); - memcpy(newmem, oldmem, (oc < bytes)? oc : bytes); - DlFree(oldmem); - } - return newmem; - } - // return 0; -} - -size_t RHybridHeap::DlInfo(struct HeapInfo* i, SWalkInfo* wi) const -{ - TInt max = ((GM->iTopSize-1) & ~CHUNK_ALIGN_MASK) - CHUNK_OVERHEAD; - if ( max < 0 ) - max = 0; - else ++i->iFreeN; // iTop always free - i->iFreeBytes += max; - - Walk(wi, GM->iTop, max, EGoodFreeCell, EDougLeaAllocator); // Introduce DL iTop buffer to the walk function - - for (mchunkptr q = ALIGN_AS_CHUNK(GM->iSeg.iBase); q != GM->iTop; q = NEXT_CHUNK(q)) - { - TInt sz = CHUNKSIZE(q); - if (!CINUSE(q)) - { - if ( sz > max ) - max = sz; - i->iFreeBytes += sz; - ++i->iFreeN; - Walk(wi, CHUNK2MEM(q), sz, EGoodFreeCell, EDougLeaAllocator); // Introduce DL free buffer to the walk function - } - else - { - i->iAllocBytes += sz - CHUNK_OVERHEAD; - ++i->iAllocN; - Walk(wi, CHUNK2MEM(q), (sz- CHUNK_OVERHEAD), EGoodAllocatedCell, EDougLeaAllocator); // Introduce DL allocated buffer to the walk function - } - } - return max; // return largest available chunk size -} - -// -// get statistics about the state of the allocator -// -TInt RHybridHeap::GetInfo(struct HeapInfo* i, SWalkInfo* wi) const -{ - memset(i,0,sizeof(HeapInfo)); - i->iFootprint = iChunkSize; - i->iMaxSize = iMaxLength; -#ifndef __KERNEL_MODE__ - PagedInfo(i, wi); - SlabInfo(i, wi); -#endif - return DlInfo(i,wi); -} - -// -// Methods to commit/decommit memory pages from chunk -// - - -void* RHybridHeap::Map(void* p, TInt sz) -// -// allocate pages in the chunk -// if p is NULL, Find an allocate the required number of pages (which must lie in the lower half) -// otherwise commit the pages specified -// -{ - HEAP_ASSERT(sz > 0); - - if ( iChunkSize + sz > iMaxLength) - return 0; - -#ifdef __KERNEL_MODE__ - - TInt r = ((DChunk*)iChunkHandle)->Adjust(iChunkSize + iOffset + sz); - if (r < 0) - return 0; - - iChunkSize += sz; - -#else - - RChunk chunk; - chunk.SetHandle(iChunkHandle); - if ( p ) - { - TInt r; - if ( iUseAdjust ) - r = chunk.Adjust(iChunkSize + sz); - else - { - HEAP_ASSERT(sz == Ceiling(sz, iPageSize)); - HEAP_ASSERT(p == Floor(p, iPageSize)); - r = chunk.Commit(iOffset + PtrDiff(p, this),sz); - } - if (r < 0) - return 0; - } - else - { - TInt r = chunk.Allocate(sz); - if (r < 0) - return 0; - if (r > iOffset) - { - // can't allow page allocations in DL zone - chunk.Decommit(r, sz); - return 0; - } - p = Offset(this, r - iOffset); - } - iChunkSize += sz; - - if (iChunkSize >= iSlabInitThreshold) - { // set up slab system now that heap is large enough - SlabConfig(iSlabConfigBits); - iSlabInitThreshold = KMaxTInt32; - } - -#endif // __KERNEL_MODE__ - -#ifdef ENABLE_BTRACE - if(iChunkSize > iHighWaterMark) - { - iHighWaterMark = Ceiling(iChunkSize,16*iPageSize); - TUint32 traceData[6]; - traceData[0] = iChunkHandle; - traceData[1] = iMinLength; - traceData[2] = iMaxLength; - traceData[3] = sz; - traceData[4] = iChunkSize; - traceData[5] = iHighWaterMark; - BTraceContextN(BTrace::ETest1, 90, (TUint32)this, 33, traceData, sizeof(traceData)); - } -#endif - - return p; -} - -void RHybridHeap::Unmap(void* p, TInt sz) -{ - HEAP_ASSERT(sz > 0); - -#ifdef __KERNEL_MODE__ - - (void)p; - HEAP_ASSERT(sz == Ceiling(sz, iPageSize)); -#if defined(_DEBUG) - TInt r = -#endif - ((DChunk*)iChunkHandle)->Adjust(iChunkSize + iOffset - sz); - HEAP_ASSERT(r >= 0); - -#else - - RChunk chunk; - chunk.SetHandle(iChunkHandle); - if ( iUseAdjust ) - { - HEAP_ASSERT(sz == Ceiling(sz, iPageSize)); -#if defined(_DEBUG) - TInt r = -#endif - chunk.Adjust(iChunkSize - sz); - HEAP_ASSERT(r >= 0); - } - else - { - HEAP_ASSERT(sz == Ceiling(sz, iPageSize)); - HEAP_ASSERT(p == Floor(p, iPageSize)); -#if defined(_DEBUG) - TInt r = -#endif - chunk.Decommit(PtrDiff(p, Offset(this,-iOffset)), sz); - HEAP_ASSERT(r >= 0); - } -#endif // __KERNEL_MODE__ - - iChunkSize -= sz; -} - - -#ifndef __KERNEL_MODE__ -// -// Slab allocator code -// - -//inline slab* slab::SlabFor(void* p) -slab* slab::SlabFor( const void* p) -{ - return (slab*)(Floor(p, SLABSIZE)); -} - -// -// Remove slab s from its tree/heap (not necessarily the root), preserving the address order -// invariant of the heap -// -void RHybridHeap::TreeRemove(slab* s) -{ - slab** r = s->iParent; - slab* c1 = s->iChild1; - slab* c2 = s->iChild2; - for (;;) - { - if (!c2) - { - *r = c1; - if (c1) - c1->iParent = r; - return; - } - if (!c1) - { - *r = c2; - c2->iParent = r; - return; - } - if (c1 > c2) - { - slab* c3 = c1; - c1 = c2; - c2 = c3; - } - slab* newc2 = c1->iChild2; - *r = c1; - c1->iParent = r; - c1->iChild2 = c2; - c2->iParent = &c1->iChild2; - s = c1; - c1 = s->iChild1; - c2 = newc2; - r = &s->iChild1; - } -} -// -// Insert slab s into the tree/heap rooted at r, preserving the address ordering -// invariant of the heap -// -void RHybridHeap::TreeInsert(slab* s,slab** r) -{ - slab* n = *r; - for (;;) - { - if (!n) - { // tree empty - *r = s; - s->iParent = r; - s->iChild1 = s->iChild2 = 0; - break; - } - if (s < n) - { // insert between iParent and n - *r = s; - s->iParent = r; - s->iChild1 = n; - s->iChild2 = 0; - n->iParent = &s->iChild1; - break; - } - slab* c1 = n->iChild1; - slab* c2 = n->iChild2; - if ((c1 - 1) > (c2 - 1)) - { - r = &n->iChild1; - n = c1; - } - else - { - r = &n->iChild2; - n = c2; - } - } -} - -void* RHybridHeap::AllocNewSlab(slabset& allocator) -// -// Acquire and initialise a new slab, returning a cell from the slab -// The strategy is: -// 1. Use the lowest address free slab, if available. This is done by using the lowest slab -// in the page at the root of the iPartialPage heap (which is address ordered). If the -// is now fully used, remove it from the iPartialPage heap. -// 2. Allocate a new page for iSlabs if no empty iSlabs are available -// -{ - page* p = page::PageFor(iPartialPage); - if (!p) - return AllocNewPage(allocator); - - unsigned h = p->iSlabs[0].iHeader; - unsigned pagemap = SlabHeaderPagemap(h); - HEAP_ASSERT(&p->iSlabs[HIBIT(pagemap)] == iPartialPage); - - unsigned slabix = LOWBIT(pagemap); - p->iSlabs[0].iHeader = h &~ (0x100<iSlabs[slabix]); -} - -/**Defination of this functionis not there in proto code***/ -#if 0 -void RHybridHeap::partial_insert(slab* s) -{ - // slab has had first cell freed and needs to be linked back into iPartial tree - slabset& ss = iSlabAlloc[iSizeMap[s->clz]]; - - HEAP_ASSERT(s->used == slabfull); - s->used = ss.fulluse - s->clz; // full-1 loading - TreeInsert(s,&ss.iPartial); - CHECKTREE(&ss.iPartial); -} -/**Defination of this functionis not there in proto code***/ -#endif - -void* RHybridHeap::AllocNewPage(slabset& allocator) -// -// Acquire and initialise a new page, returning a cell from a new slab -// The iPartialPage tree is empty (otherwise we'd have used a slab from there) -// The iPartialPage link is put in the highest addressed slab in the page, and the -// lowest addressed slab is used to fulfill the allocation request -// -{ - page* p = iSparePage; - if (p) - iSparePage = 0; - else - { - p = static_cast(Map(0, iPageSize)); - if (!p) - return 0; - } - HEAP_ASSERT(p == Floor(p, iPageSize)); - // Store page allocated for slab into paged_bitmap (for RHybridHeap::Reset()) - if (!PagedSetSize(p, iPageSize)) - { - Unmap(p, iPageSize); - return 0; - } - p->iSlabs[0].iHeader = ((1<<3) + (1<<2) + (1<<1))<<8; // set pagemap - p->iSlabs[3].iParent = &iPartialPage; - p->iSlabs[3].iChild1 = p->iSlabs[3].iChild2 = 0; - iPartialPage = &p->iSlabs[3]; - return InitNewSlab(allocator,&p->iSlabs[0]); -} - -void RHybridHeap::FreePage(page* p) -// -// Release an unused page to the OS -// A single page is cached for reuse to reduce thrashing -// the OS allocator. -// -{ - HEAP_ASSERT(Ceiling(p, iPageSize) == p); - if (!iSparePage) - { - iSparePage = p; - return; - } - - // unmapped slab page must be cleared from paged_bitmap, too - PagedZapSize(p, iPageSize); // clear page map - - Unmap(p, iPageSize); -} - -void RHybridHeap::FreeSlab(slab* s) -// -// Release an empty slab to the slab manager -// The strategy is: -// 1. The page containing the slab is checked to see the state of the other iSlabs in the page by -// inspecting the pagemap field in the iHeader of the first slab in the page. -// 2. The pagemap is updated to indicate the new unused slab -// 3. If this is the only unused slab in the page then the slab iHeader is used to add the page to -// the iPartialPage tree/heap -// 4. If all the iSlabs in the page are now unused the page is release back to the OS -// 5. If this slab has a higher address than the one currently used to track this page in -// the iPartialPage heap, the linkage is moved to the new unused slab -// -{ - TreeRemove(s); - CHECKTREE(s->iParent); - HEAP_ASSERT(SlabHeaderUsedm4(s->iHeader) == SlabHeaderSize(s->iHeader)-4); - - page* p = page::PageFor(s); - unsigned h = p->iSlabs[0].iHeader; - int slabix = s - &p->iSlabs[0]; - unsigned pagemap = SlabHeaderPagemap(h); - p->iSlabs[0].iHeader = h | (0x100<iSlabs[HIBIT(pagemap)]; - pagemap ^= (1< sl) - { // replace current link with new one. Address-order tree so position stays the same - slab** r = sl->iParent; - slab* c1 = sl->iChild1; - slab* c2 = sl->iChild2; - s->iParent = r; - s->iChild1 = c1; - s->iChild2 = c2; - *r = s; - if (c1) - c1->iParent = &s->iChild1; - if (c2) - c2->iParent = &s->iChild2; - } - CHECK(if (s < sl) s=sl); - } - HEAP_ASSERT(SlabHeaderPagemap(p->iSlabs[0].iHeader) != 0); - HEAP_ASSERT(HIBIT(SlabHeaderPagemap(p->iSlabs[0].iHeader)) == unsigned(s - &p->iSlabs[0])); -} - - -void RHybridHeap::SlabInit() -{ - iSlabThreshold=0; - iPartialPage = 0; - iFullSlab = 0; - iSparePage = 0; - memset(&iSizeMap[0],0xff,sizeof(iSizeMap)); - memset(&iSlabAlloc[0],0,sizeof(iSlabAlloc)); -} - -void RHybridHeap::SlabConfig(unsigned slabbitmap) -{ - HEAP_ASSERT((slabbitmap & ~EOkBits) == 0); - HEAP_ASSERT(MAXSLABSIZE <= 60); - - unsigned int ix = 0xff; - unsigned int bit = 1<<((MAXSLABSIZE>>2)-1); - for (int sz = MAXSLABSIZE; sz >= 0; sz -= 4, bit >>= 1) - { - if (slabbitmap & bit) - { - if (ix == 0xff) - iSlabThreshold=sz+1; - ix = (sz>>2)-1; - } - iSizeMap[sz>>2] = (TUint8) ix; - } -} - - -void* RHybridHeap::SlabAllocate(slabset& ss) -// -// Allocate a cell from the given slabset -// Strategy: -// 1. Take the partially full slab at the iTop of the heap (lowest address). -// 2. If there is no such slab, allocate from a new slab -// 3. If the slab has a non-empty freelist, pop the cell from the front of the list and update the slab -// 4. Otherwise, if the slab is not full, return the cell at the end of the currently used region of -// the slab, updating the slab -// 5. Otherwise, release the slab from the iPartial tree/heap, marking it as 'floating' and go back to -// step 1 -// -{ - for (;;) - { - slab *s = ss.iPartial; - if (!s) - break; - unsigned h = s->iHeader; - unsigned free = h & 0xff; // extract free cell positioning - if (free) - { - HEAP_ASSERT(((free<<2)-sizeof(slabhdr))%SlabHeaderSize(h) == 0); - void* p = Offset(s,free<<2); - free = *(unsigned char*)p; // get next pos in free list - h += (h&0x3C000)<<6; // update usedm4 - h &= ~0xff; - h |= free; // update freelist - s->iHeader = h; - HEAP_ASSERT(SlabHeaderFree(h) == 0 || ((SlabHeaderFree(h)<<2)-sizeof(slabhdr))%SlabHeaderSize(h) == 0); - HEAP_ASSERT(SlabHeaderUsedm4(h) <= 0x3F8u); - HEAP_ASSERT((SlabHeaderUsedm4(h)+4)%SlabHeaderSize(h) == 0); - return p; - } - unsigned h2 = h + ((h&0x3C000)<<6); -// if (h2 < 0xfc00000) - if (h2 < MAXUSEDM4BITS) - { - HEAP_ASSERT((SlabHeaderUsedm4(h2)+4)%SlabHeaderSize(h2) == 0); - s->iHeader = h2; - return Offset(s,(h>>18) + sizeof(unsigned) + sizeof(slabhdr)); - } - h |= FLOATING_BIT; // mark the slab as full-floating - s->iHeader = h; - TreeRemove(s); - slab* c = iFullSlab; // add to full list - iFullSlab = s; - s->iParent = &iFullSlab; - s->iChild1 = c; - s->iChild2 = 0; - if (c) - c->iParent = &s->iChild1; - - CHECKTREE(&ss.iPartial); - // go back and try the next slab... - } - // no iPartial iSlabs found, so allocate from a new slab - return AllocNewSlab(ss); -} - -void RHybridHeap::SlabFree(void* p) -// -// Free a cell from the slab allocator -// Strategy: -// 1. Find the containing slab (round down to nearest 1KB boundary) -// 2. Push the cell into the slab's freelist, and update the slab usage count -// 3. If this is the last allocated cell, free the slab to the main slab manager -// 4. If the slab was full-floating then insert the slab in it's respective iPartial tree -// -{ - HEAP_ASSERT(LowBits(p,3)==0); - slab* s = slab::SlabFor(p); - CHECKSLAB(s,ESlabAllocator,p); - CHECKSLABBFR(s,p); - - unsigned pos = LowBits(p, SLABSIZE); - unsigned h = s->iHeader; - HEAP_ASSERT(SlabHeaderUsedm4(h) != 0x3fC); // slab is empty already - HEAP_ASSERT((pos-sizeof(slabhdr))%SlabHeaderSize(h) == 0); - *(unsigned char*)p = (unsigned char)h; - h &= ~0xFF; - h |= (pos>>2); - unsigned size = h & 0x3C000; - if (int(h) >= 0) - { - h -= size<<6; - if (int(h)>=0) - { - s->iHeader = h; - return; - } - FreeSlab(s); - return; - } - h -= size<<6; - h &= ~FLOATING_BIT; - s->iHeader = h; - slab** full = s->iParent; // remove from full list - slab* c = s->iChild1; - *full = c; - if (c) - c->iParent = full; - - slabset& ss = iSlabAlloc[iSizeMap[size>>14]]; - TreeInsert(s,&ss.iPartial); - CHECKTREE(&ss.iPartial); -} - -void* RHybridHeap::InitNewSlab(slabset& allocator, slab* s) -// -// initialise an empty slab for this allocator and return the fist cell -// pre-condition: the slabset has no iPartial iSlabs for allocation -// -{ - HEAP_ASSERT(allocator.iPartial==0); - TInt size = 4 + ((&allocator-&iSlabAlloc[0])<<2); // infer size from slab allocator address - unsigned h = s->iHeader & 0xF00; // preserve pagemap only - h |= (size<<12); // set size - h |= (size-4)<<18; // set usedminus4 to one object minus 4 - s->iHeader = h; - allocator.iPartial = s; - s->iParent = &allocator.iPartial; - s->iChild1 = s->iChild2 = 0; - return Offset(s,sizeof(slabhdr)); -} - -const unsigned char slab_bitcount[16] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4}; - -const unsigned char slab_ext_frag[16] = -{ - 0, - 16 + (1008 % 4), - 16 + (1008 % 8), - 16 + (1008 % 12), - 16 + (1008 % 16), - 16 + (1008 % 20), - 16 + (1008 % 24), - 16 + (1008 % 28), - 16 + (1008 % 32), - 16 + (1008 % 36), - 16 + (1008 % 40), - 16 + (1008 % 44), - 16 + (1008 % 48), - 16 + (1008 % 52), - 16 + (1008 % 56), - 16 + (1008 % 60) -}; - -void RHybridHeap::TreeWalk(slab* const* root, void (*f)(slab*, struct HeapInfo*, SWalkInfo*), struct HeapInfo* i, SWalkInfo* wi) -{ - // iterative walk around the tree at root - - slab* s = *root; - if (!s) - return; - - for (;;) - { - slab* c; - while ((c = s->iChild1) != 0) - s = c; // walk down left side to end - for (;;) - { - f(s, i, wi); - c = s->iChild2; - if (c) - { // one step down right side, now try and walk down left - s = c; - break; - } - for (;;) - { // loop to walk up right side - slab** pp = s->iParent; - if (pp == root) - return; - s = slab::SlabFor(pp); - if (pp == &s->iChild1) - break; - } - } - } -} - -void RHybridHeap::SlabEmptyInfo(slab* s, struct HeapInfo* i, SWalkInfo* wi) -{ - Walk(wi, s, SLABSIZE, EGoodFreeCell, EEmptySlab); // Introduce an empty slab to the walk function - int nslab = slab_bitcount[SlabHeaderPagemap(page::PageFor(s)->iSlabs[0].iHeader)]; - i->iFreeN += nslab; - i->iFreeBytes += nslab << SLABSHIFT; -} - -void RHybridHeap::SlabPartialInfo(slab* s, struct HeapInfo* i, SWalkInfo* wi) -{ - Walk(wi, s, SLABSIZE, EGoodAllocatedCell, EPartialFullSlab); // Introduce a full slab to the walk function - unsigned h = s->iHeader; - unsigned used = SlabHeaderUsedm4(h)+4; - unsigned size = SlabHeaderSize(h); - unsigned free = 1024 - slab_ext_frag[size>>2] - used; - i->iFreeN += (free/size); - i->iFreeBytes += free; - i->iAllocN += (used/size); - i->iAllocBytes += used; -} - -void RHybridHeap::SlabFullInfo(slab* s, struct HeapInfo* i, SWalkInfo* wi) -{ - Walk(wi, s, SLABSIZE, EGoodAllocatedCell, EFullSlab); // Introduce a full slab to the walk function - unsigned h = s->iHeader; - unsigned used = SlabHeaderUsedm4(h)+4; - unsigned size = SlabHeaderSize(h); - HEAP_ASSERT(1024 - slab_ext_frag[size>>2] - used == 0); - i->iAllocN += (used/size); - i->iAllocBytes += used; -} - -void RHybridHeap::SlabInfo(struct HeapInfo* i, SWalkInfo* wi) const -{ - if (iSparePage) - { - i->iFreeBytes += iPageSize; - i->iFreeN = 4; - Walk(wi, iSparePage, iPageSize, EGoodFreeCell, ESlabSpare); // Introduce Slab spare page to the walk function - } - TreeWalk(&iFullSlab, &SlabFullInfo, i, wi); - for (int ix = 0; ix < (MAXSLABSIZE>>2); ++ix) - TreeWalk(&iSlabAlloc[ix].iPartial, &SlabPartialInfo, i, wi); - TreeWalk(&iPartialPage, &SlabEmptyInfo, i, wi); -} - - -// -// Bitmap class implementation for large page allocator -// -inline unsigned char* paged_bitmap::Addr() const {return iBase;} -inline unsigned paged_bitmap::Size() const {return iNbits;} -// - -void paged_bitmap::Init(unsigned char* p, unsigned size, unsigned bit) -{ - iBase = p; - iNbits=size; - int bytes=Ceiling(size,8)>>3; - memset(p,bit?0xff:0,bytes); -} - -inline void paged_bitmap::Set(unsigned ix, unsigned bit) -{ - if (bit) - iBase[ix>>3] |= (1<<(ix&7)); - else - iBase[ix>>3] &= ~(1<<(ix&7)); -} - -inline unsigned paged_bitmap::operator[](unsigned ix) const -{ - return 1U&(iBase[ix>>3] >> (ix&7)); -} - -void paged_bitmap::Setn(unsigned ix, unsigned len, unsigned bit) -{ - int l=len; - while (--l>=0) - Set(ix++,bit); -} - -void paged_bitmap::Set(unsigned ix, unsigned len, unsigned val) -{ - int l=len; - while (--l>=0) - { - Set(ix++,val&1); - val>>=1; - } -} - -unsigned paged_bitmap::Bits(unsigned ix, unsigned len) const -{ - int l=len; - unsigned val=0; - unsigned bit=0; - while (--l>=0) - val |= (*this)[ix++]< iNbits) - return false; - for (;;) - { - if ((*this)[ix] != bit) - return false; - if (++ix==i2) - return true; - } -} - -int paged_bitmap::Find(unsigned start, unsigned bit) const -{ - if (start 0) - { - if (aPagePower < MINPAGEPOWER) - aPagePower = MINPAGEPOWER; - } - else aPagePower = 31; - - iPageThreshold = aPagePower; - /*------------------------------------------------------------- - * Initialize page bitmap - *-------------------------------------------------------------*/ - iPageMap.Init((unsigned char*)&iBitMapBuffer, MAXSMALLPAGEBITS, 0); -} - -void* RHybridHeap::PagedAllocate(unsigned size) -{ - TInt nbytes = Ceiling(size, iPageSize); - void* p = Map(0, nbytes); - if (!p) - return 0; - if (!PagedSetSize(p, nbytes)) - { - Unmap(p, nbytes); - return 0; - } - return p; -} - -void* RHybridHeap::PagedReallocate(void* p, unsigned size, TInt mode) -{ - - HEAP_ASSERT(Ceiling(p, iPageSize) == p); - unsigned nbytes = Ceiling(size, iPageSize); - - unsigned osize = PagedSize(p); - if ( nbytes == 0 ) // Special case to handle shrinking below min page threshold - nbytes = Min((1 << MINPAGEPOWER), osize); - - if (osize == nbytes) - return p; - - if (nbytes < osize) - { // shrink in place, unmap final pages and rewrite the pagemap - Unmap(Offset(p, nbytes), osize-nbytes); - // zap old code and then write new code (will not fail) - PagedZapSize(p, osize); - - TBool check = PagedSetSize(p, nbytes); - __ASSERT_ALWAYS(check, HEAP_PANIC(ETHeapBadCellAddress)); - - return p; - } - - // nbytes > osize - // try and extend current region first - - void* newp = Map(Offset(p, osize), nbytes-osize); - if (newp) - { // In place growth. Possibility that pagemap may have to grow AND then fails - if (!PagedSetSize(p, nbytes)) - { // must release extra mapping - Unmap(Offset(p, osize), nbytes-osize); - return 0; - } - // if successful, the new length code will have overwritten the old one (it is at least as long) - return p; - } - - // fallback to allocate/copy/free - if (mode & ENeverMove) - return 0; // not allowed to move cell - - newp = PagedAllocate(nbytes); - if (!newp) - return 0; - memcpy(newp, p, osize); - PagedFree(p); - return newp; -} - -void RHybridHeap::PagedFree(void* p) -{ - HEAP_ASSERT(Ceiling(p, iPageSize) == p); - - - unsigned size = PagedSize(p); - - PagedZapSize(p, size); // clear page map - Unmap(p, size); -} - -void RHybridHeap::PagedInfo(struct HeapInfo* i, SWalkInfo* wi) const -{ - for (int ix = 0;(ix = iPageMap.Find(ix,1)) >= 0;) - { - int npage = PagedDecode(ix); - // Introduce paged buffer to the walk function - TAny* bfr = Bitmap2addr(ix); - int len = npage << PAGESHIFT; - if ( len > iPageSize ) - { // If buffer is not larger than one page it must be a slab page mapped into bitmap - i->iAllocBytes += len; - ++i->iAllocN; - Walk(wi, bfr, len, EGoodAllocatedCell, EPageAllocator); - } - ix += (npage<<1); - } -} - -void RHybridHeap::ResetBitmap() -/*--------------------------------------------------------- - * Go through paged_bitmap and unmap all buffers to system - * This method is called from RHybridHeap::Reset() to unmap all page - * allocated - and slab pages which are stored in bitmap, too - *---------------------------------------------------------*/ -{ - unsigned iNbits = iPageMap.Size(); - if ( iNbits ) - { - for (int ix = 0;(ix = iPageMap.Find(ix,1)) >= 0;) - { - int npage = PagedDecode(ix); - void* p = Bitmap2addr(ix); - unsigned size = PagedSize(p); - PagedZapSize(p, size); // clear page map - Unmap(p, size); - ix += (npage<<1); - } - if ( (TInt)iNbits > MAXSMALLPAGEBITS ) - { - // unmap page reserved for enlarged bitmap - Unmap(iPageMap.Addr(), (iNbits >> 3) ); - } - } -} - -TBool RHybridHeap::CheckBitmap(void* aBfr, TInt aSize, TUint32& aDummy, TInt& aNPages) -/*--------------------------------------------------------- - * If aBfr = NULL - * Go through paged_bitmap and unmap all buffers to system - * and assure that by reading the first word of each page of aBfr - * that aBfr is still accessible - * else - * Assure that specified buffer is mapped with correct length in - * page map - *---------------------------------------------------------*/ -{ - TBool ret; - if ( aBfr ) - { - __ASSERT_ALWAYS((Ceiling(aBfr, iPageSize) == aBfr), HEAP_PANIC(ETHeapBadCellAddress)); - ret = ( aSize == (TInt)PagedSize(aBfr)); - } - else - { - ret = ETrue; - unsigned iNbits = iPageMap.Size(); - if ( iNbits ) - { - TInt npage; - aNPages = 0; - for (int ix = 0;(ix = iPageMap.Find(ix,1)) >= 0;) - { - npage = PagedDecode(ix); - aNPages += npage; - void* p = Bitmap2addr(ix); - __ASSERT_ALWAYS((Ceiling(p, iPageSize) == p), HEAP_PANIC(ETHeapBadCellAddress)); - unsigned s = PagedSize(p); - __ASSERT_ALWAYS((Ceiling(s, iPageSize) == s), HEAP_PANIC(ETHeapBadCellAddress)); - while ( s ) - { - aDummy += *(TUint32*)((TUint8*)p + (s-iPageSize)); - s -= iPageSize; - } - ix += (npage<<1); - } - if ( (TInt)iNbits > MAXSMALLPAGEBITS ) - { - // add enlarged bitmap page(s) to total page count - npage = (iNbits >> 3); - __ASSERT_ALWAYS((Ceiling(npage, iPageSize) == npage), HEAP_PANIC(ETHeapBadCellAddress)); - aNPages += (npage / iPageSize); - } - } - } - - return ret; -} - - -// The paged allocations are tracked in a bitmap which has 2 bits per page -// this allows us to store allocations as small as 4KB -// The presence and size of an allocation is encoded as follows: -// let N = number of pages in the allocation, then -// 10 : N = 1 // 4KB -// 110n : N = 2 + n // 8-12KB -// 1110nnnn : N = nnnn // 16-60KB -// 1111n[18] : N = n[18] // 64KB-1GB - -const struct etab { unsigned char offset, len, codelen, code;} encode_table[] = -{ - {1,2,2,0x1}, - {2,4,3,0x3}, - {0,8,4,0x7}, - {0,22,4,0xf} -}; - -// Return code length for specified allocation Size(assumed to be aligned to pages) -inline unsigned paged_codelen(unsigned size, unsigned pagesz) -{ - HEAP_ASSERT(size == Ceiling(size, pagesz)); - - if (size == pagesz) - return 2; - else if (size < 4*pagesz) - return 4; - else if (size < 16*pagesz) - return 8; - else - return 22; -} - -inline const etab& paged_coding(unsigned npage) -{ - if (npage < 4) - return encode_table[npage>>1]; - else if (npage < 16) - return encode_table[2]; - else - return encode_table[3]; -} - -bool RHybridHeap::PagedEncode(unsigned pos, unsigned npage) -{ - const etab& e = paged_coding(npage); - if (pos + e.len > iPageMap.Size()) - { - // need to grow the page bitmap to fit the cell length into the map - // if we outgrow original bitmap buffer in RHybridHeap metadata, then just get enough pages to cover the full space: - // * initial 68 byte bitmap mapped (68*8*4kB):2 = 1,1MB - // * 4KB can Map(4096*8*4kB):2 = 64MB - unsigned maxsize = Ceiling(iMaxLength, iPageSize); - unsigned mapbits = maxsize >> (PAGESHIFT-1); - maxsize = Ceiling(mapbits>>3, iPageSize); - void* newb = Map(0, maxsize); - if (!newb) - return false; - - unsigned char* oldb = iPageMap.Addr(); - iPageMap.Init((unsigned char*)newb, (maxsize<<3), 0); - memcpy(newb, oldb, Ceiling(MAXSMALLPAGEBITS,8)>>3); - } - // encode the allocation block size into the bitmap, starting at the bit for the start page - unsigned bits = e.code; - bits |= (npage - e.offset) << e.codelen; - iPageMap.Set(pos, e.len, bits); - return true; -} - -unsigned RHybridHeap::PagedDecode(unsigned pos) const -{ - __ASSERT_ALWAYS(pos + 2 <= iPageMap.Size(), HEAP_PANIC(ETHeapBadCellAddress)); - - unsigned bits = iPageMap.Bits(pos,2); - __ASSERT_ALWAYS(bits & 1, HEAP_PANIC(ETHeapBadCellAddress)); - bits >>= 1; - if (bits == 0) - return 1; - __ASSERT_ALWAYS(pos + 4 <= iPageMap.Size(), HEAP_PANIC(ETHeapBadCellAddress)); - bits = iPageMap.Bits(pos+2,2); - if ((bits & 1) == 0) - return 2 + (bits>>1); - else if ((bits>>1) == 0) - { - __ASSERT_ALWAYS(pos + 8 <= iPageMap.Size(), HEAP_PANIC(ETHeapBadCellAddress)); - return iPageMap.Bits(pos+4, 4); - } - else - { - __ASSERT_ALWAYS(pos + 22 <= iPageMap.Size(), HEAP_PANIC(ETHeapBadCellAddress)); - return iPageMap.Bits(pos+4, 18); - } -} - -inline void RHybridHeap::PagedZapSize(void* p, unsigned size) -{iPageMap.Setn(PtrDiff(p, iMemBase) >> (PAGESHIFT-1), paged_codelen(size, iPageSize) ,0);} - -inline unsigned RHybridHeap::PagedSize(void* p) const - { return PagedDecode(PtrDiff(p, iMemBase) >> (PAGESHIFT-1)) << PAGESHIFT; } - -inline bool RHybridHeap::PagedSetSize(void* p, unsigned size) -{ return PagedEncode(PtrDiff(p, iMemBase) >> (PAGESHIFT-1), size >> PAGESHIFT); } - -inline void* RHybridHeap::Bitmap2addr(unsigned pos) const - { return iMemBase + (1 << (PAGESHIFT-1))*pos; } - - -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// -/** -Constructor where minimum and maximum length of the heap can be defined. -It defaults the chunk heap to be created to have use a new local chunk, -to have a grow by value of KMinHeapGrowBy, to be unaligned, not to be -single threaded and not to have any mode flags set. - -@param aMinLength The minimum length of the heap to be created. -@param aMaxLength The maximum length to which the heap to be created can grow. - If the supplied value is less than a page size, then it - is discarded and the page size is used instead. -*/ -EXPORT_C TChunkHeapCreateInfo::TChunkHeapCreateInfo(TInt aMinLength, TInt aMaxLength) : - iVersionNumber(EVersion0), iMinLength(aMinLength), iMaxLength(aMaxLength), -iAlign(0), iGrowBy(1), iSingleThread(EFalse), -iOffset(0), iPaging(EUnspecified), iMode(0), iName(NULL) -{ -} - - -/** -Sets the chunk heap to create a new chunk with the specified name. - -This overriddes any previous call to TChunkHeapCreateInfo::SetNewChunkHeap() or -TChunkHeapCreateInfo::SetExistingChunkHeap() for this TChunkHeapCreateInfo object. - -@param aName The name to be given to the chunk heap to be created -If NULL, the function constructs a local chunk to host the heap. -If not NULL, a pointer to a descriptor containing the name to be -assigned to the global chunk hosting the heap. -*/ -EXPORT_C void TChunkHeapCreateInfo::SetCreateChunk(const TDesC* aName) -{ - iName = (TDesC*)aName; - iChunk.SetHandle(KNullHandle); -} - - -/** -Sets the chunk heap to be created to use the chunk specified. - -This overriddes any previous call to TChunkHeapCreateInfo::SetNewChunkHeap() or -TChunkHeapCreateInfo::SetExistingChunkHeap() for this TChunkHeapCreateInfo object. - -@param aChunk A handle to the chunk to use for the heap. -*/ -EXPORT_C void TChunkHeapCreateInfo::SetUseChunk(const RChunk aChunk) -{ - iName = NULL; - iChunk = aChunk; -} - -EXPORT_C RHeap* UserHeap::FixedHeap(TAny* aBase, TInt aMaxLength, TInt aAlign, TBool aSingleThread) -/** -Creates a fixed length heap at a specified location. - -On successful return from this function, the heap is ready to use. This assumes that -the memory pointed to by aBase is mapped and able to be used. You must ensure that you -pass in a large enough value for aMaxLength. Passing in a value that is too small to -hold the metadata for the heap (~1 KB) will result in the size being rounded up and the -heap thereby running over the end of the memory assigned to it. But then if you were to -pass in such as small value then you would not be able to do any allocations from the -heap anyway. Moral of the story: Use a sensible value for aMaxLength! - -@param aBase A pointer to the location where the heap is to be constructed. -@param aMaxLength The maximum length in bytes to which the heap can grow. If the - supplied value is too small to hold the heap's metadata, it - will be increased. -@param aAlign From Symbian^4 onwards, this value is ignored but EABI 8 - byte alignment is guaranteed for all allocations 8 bytes or - more in size. 4 byte allocations will be aligned to a 4 - byte boundary. Best to pass in zero. -@param aSingleThread EFalse if the heap is to be accessed from multiple threads. - This will cause internal locks to be created, guaranteeing - thread safety. - -@return A pointer to the new heap, or NULL if the heap could not be created. - -@panic USER 56 if aMaxLength is negative. -*/ -{ - __ASSERT_ALWAYS( aMaxLength>=0, ::Panic(ETHeapMaxLengthNegative)); - if ( aMaxLength < (TInt)sizeof(RHybridHeap) ) - aMaxLength = sizeof(RHybridHeap); - - RHybridHeap* h = new(aBase) RHybridHeap(aMaxLength, aAlign, aSingleThread); - - if (!aSingleThread) - { - TInt r = h->iLock.CreateLocal(); - if (r!=KErrNone) - return NULL; // No need to delete the RHybridHeap instance as the new above is only a placement new - h->iHandles = (TInt*)&h->iLock; - h->iHandleCount = 1; - } - return h; -} - -/** -Creates a chunk heap of the type specified by the parameter aCreateInfo. - -@param aCreateInfo A reference to a TChunkHeapCreateInfo object specifying the -type of chunk heap to create. - -@return A pointer to the new heap or NULL if the heap could not be created. - -@panic USER 41 if the heap's specified minimum length is greater than the specified maximum length. -@panic USER 55 if the heap's specified minimum length is negative. -@panic USER 172 if the heap's specified alignment is not a power of 2 or is less than the size of a TAny*. -*/ -EXPORT_C RHeap* UserHeap::ChunkHeap(const TChunkHeapCreateInfo& aCreateInfo) -{ - // aCreateInfo must have been configured to use a new chunk or an exiting chunk. - __ASSERT_ALWAYS(!(aCreateInfo.iMode & (TUint32)~EChunkHeapMask), ::Panic(EHeapCreateInvalidMode)); - RHeap* h = NULL; - - if (aCreateInfo.iChunk.Handle() == KNullHandle) - { - // A new chunk is to be created for this heap. - - __ASSERT_ALWAYS(aCreateInfo.iMinLength >= 0, ::Panic(ETHeapMinLengthNegative)); - __ASSERT_ALWAYS(aCreateInfo.iMaxLength >= aCreateInfo.iMinLength, ::Panic(ETHeapCreateMaxLessThanMin)); - - TInt maxLength = aCreateInfo.iMaxLength; - TInt page_size; - GET_PAGE_SIZE(page_size); - - if (maxLength < page_size) - maxLength = page_size; - - TChunkCreateInfo chunkInfo; -#if USE_HYBRID_HEAP - if ( aCreateInfo.iOffset ) - chunkInfo.SetNormal(0, maxLength); // Create DL only heap - else - { - maxLength = 2*maxLength; - chunkInfo.SetDisconnected(0, 0, maxLength); // Create hybrid heap - } -#else - chunkInfo.SetNormal(0, maxLength); // Create DL only heap -#endif - chunkInfo.SetOwner((aCreateInfo.iSingleThread)? EOwnerThread : EOwnerProcess); - if (aCreateInfo.iName) - chunkInfo.SetGlobal(*aCreateInfo.iName); - // Set the paging attributes of the chunk. - if (aCreateInfo.iPaging == TChunkHeapCreateInfo::EPaged) - chunkInfo.SetPaging(TChunkCreateInfo::EPaged); - if (aCreateInfo.iPaging == TChunkHeapCreateInfo::EUnpaged) - chunkInfo.SetPaging(TChunkCreateInfo::EUnpaged); - // Create the chunk. - RChunk chunk; - if (chunk.Create(chunkInfo) != KErrNone) - return NULL; - // Create the heap using the new chunk. - TUint mode = aCreateInfo.iMode | EChunkHeapDuplicate; // Must duplicate the handle. - h = OffsetChunkHeap(chunk, aCreateInfo.iMinLength, aCreateInfo.iOffset, - aCreateInfo.iGrowBy, maxLength, aCreateInfo.iAlign, - aCreateInfo.iSingleThread, mode); - chunk.Close(); - } - else - { - h = OffsetChunkHeap(aCreateInfo.iChunk, aCreateInfo.iMinLength, aCreateInfo.iOffset, - aCreateInfo.iGrowBy, aCreateInfo.iMaxLength, aCreateInfo.iAlign, - aCreateInfo.iSingleThread, aCreateInfo.iMode); - } - return h; -} - - - -EXPORT_C RHeap* UserHeap::ChunkHeap(const TDesC* aName, TInt aMinLength, TInt aMaxLength, TInt aGrowBy, TInt aAlign, TBool aSingleThread) -/** -Creates a heap in a local or global chunk. - -The chunk hosting the heap can be local or global. - -A local chunk is one which is private to the process creating it and is not -intended for access by other user processes. A global chunk is one which is -visible to all processes. - -The hosting chunk is local, if the pointer aName is NULL, otherwise the -hosting chunk is global and the descriptor *aName is assumed to contain -the name to be assigned to it. - -Ownership of the host chunk is vested in the current process. - -A minimum and a maximum size for the heap can be specified. On successful -return from this function, the size of the heap is at least aMinLength. -If subsequent requests for allocation of memory from the heap cannot be -satisfied by compressing the heap, the size of the heap is extended in -increments of aGrowBy until the request can be satisfied. Attempts to extend -the heap causes the size of the host chunk to be adjusted. - -Note that the size of the heap cannot be adjusted by more than aMaxLength. - -@param aName If NULL, the function constructs a local chunk to host - the heap. If not NULL, a pointer to a descriptor containing - the name to be assigned to the global chunk hosting the heap. -@param aMinLength The minimum length of the heap in bytes. This will be - rounded up to the nearest page size by the allocator. -@param aMaxLength The maximum length in bytes to which the heap can grow. This - will be rounded up to the nearest page size by the allocator. -@param aGrowBy The number of bytes by which the heap will grow when more - memory is required. This will be rounded up to the nearest - page size by the allocator. If a value is not explicitly - specified, the page size is taken by default. -@param aAlign From Symbian^4 onwards, this value is ignored but EABI 8 - byte alignment is guaranteed for all allocations 8 bytes or - more in size. 4 byte allocations will be aligned to a 4 - byte boundary. Best to pass in zero. -@param aSingleThread EFalse if the heap is to be accessed from multiple threads. - This will cause internal locks to be created, guaranteeing - thread safety. - -@return A pointer to the new heap or NULL if the heap could not be created. - -@panic USER 41 if aMaxLength is < aMinLength. -@panic USER 55 if aMinLength is negative. -@panic USER 56 if aMaxLength is negative. -*/ - { - TInt page_size; - GET_PAGE_SIZE(page_size); - TInt minLength = _ALIGN_UP(aMinLength, page_size); - TInt maxLength = Max(aMaxLength, minLength); - - TChunkHeapCreateInfo createInfo(minLength, maxLength); - createInfo.SetCreateChunk(aName); - createInfo.SetGrowBy(aGrowBy); - createInfo.SetAlignment(aAlign); - createInfo.SetSingleThread(aSingleThread); - - return ChunkHeap(createInfo); - } - -EXPORT_C RHeap* UserHeap::ChunkHeap(RChunk aChunk, TInt aMinLength, TInt aGrowBy, TInt aMaxLength, TInt aAlign, TBool aSingleThread, TUint32 aMode) -/** -Creates a heap in an existing chunk. - -This function is intended to be used to create a heap in a user writable code -chunk as created by a call to RChunk::CreateLocalCode(). This type of heap can -be used to hold code fragments from a JIT compiler. - -@param aChunk The chunk that will host the heap. -@param aMinLength The minimum length of the heap in bytes. This will be - rounded up to the nearest page size by the allocator. -@param aGrowBy The number of bytes by which the heap will grow when more - memory is required. This will be rounded up to the nearest - page size by the allocator. If a value is not explicitly - specified, the page size is taken by default. -@param aMaxLength The maximum length in bytes to which the heap can grow. This - will be rounded up to the nearest page size by the allocator. - If 0 is passed in, the maximum lengt of the chunk is used. -@param aAlign From Symbian^4 onwards, this value is ignored but EABI 8 - byte alignment is guaranteed for all allocations 8 bytes or - more in size. 4 byte allocations will be aligned to a 4 - byte boundary. Best to pass in zero. -@param aSingleThread EFalse if the heap is to be accessed from multiple threads. - This will cause internal locks to be created, guaranteeing - thread safety. -@param aMode Flags controlling the heap creation. See RAllocator::TFlags. - -@return A pointer to the new heap or NULL if the heap could not be created. - -@see UserHeap::OffsetChunkHeap() -*/ - { - return OffsetChunkHeap(aChunk, aMinLength, 0, aGrowBy, aMaxLength, aAlign, aSingleThread, aMode); - } - -EXPORT_C RHeap* UserHeap::OffsetChunkHeap(RChunk aChunk, TInt aMinLength, TInt aOffset, TInt aGrowBy, TInt aMaxLength, TInt aAlign, TBool aSingleThread, TUint32 aMode) -/** -Creates a heap in an existing chunk, offset from the beginning of the chunk. - -This function is intended to be used to create a heap using a chunk which has -some of its memory already used, at the start of that that chunk. The maximum -length to which the heap can grow is the maximum size of the chunk, minus the -data at the start of the chunk. - -The offset at which to create the heap is passed in as the aOffset parameter. -Legacy heap implementations always respected the aOffset value, however more -modern heap implementations are more sophisticated and cannot necessarily respect -this value. Therefore, if possible, you should always use an aOffset of 0 unless -you have a very explicit requirement for using a non zero value. Using a non zero -value will result in a less efficient heap algorithm being used in order to respect -the offset. - -Another issue to consider when using this function is the type of the chunk passed -in. In order for the most efficient heap algorithms to be used, the chunk passed -in should always be a disconnected chunk. Passing in a non disconnected chunk will -again result in a less efficient heap algorithm being used. - -Finally, another requirement for the most efficient heap algorithms to be used is -for the heap to be able to expand. Therefore, unless you have a specific reason to -do so, always specify aMaxLength > aMinLength. - -So, if possible, use aOffset == zero, aMaxLength > aMinLength and a disconnected -chunk for best results! - -@param aChunk The chunk that will host the heap. -@param aMinLength The minimum length of the heap in bytes. This will be - rounded up to the nearest page size by the allocator. -@param aOffset The offset in bytes from the start of the chunk at which to - create the heap. If used (and it shouldn't really be!) - then it will be rounded up to a multiple of 8, to respect - EABI 8 byte alignment requirements. -@param aGrowBy The number of bytes by which the heap will grow when more - memory is required. This will be rounded up to the nearest - page size by the allocator. If a value is not explicitly - specified, the page size is taken by default. -@param aMaxLength The maximum length in bytes to which the heap can grow. This - will be rounded up to the nearest page size by the allocator. - If 0 is passed in, the maximum length of the chunk is used. -@param aAlign From Symbian^4 onwards, this value is ignored but EABI 8 - byte alignment is guaranteed for all allocations 8 bytes or - more in size. 4 byte allocations will be aligned to a 4 - byte boundary. Best to pass in zero. -@param aSingleThread EFalse if the heap is to be accessed from multiple threads. - This will cause internal locks to be created, guaranteeing - thread safety. -@param aMode Flags controlling the heap creation. See RAllocator::TFlags. - -@return A pointer to the new heap or NULL if the heap could not be created. - -@panic USER 41 if aMaxLength is < aMinLength. -@panic USER 55 if aMinLength is negative. -@panic USER 56 if aMaxLength is negative. -@panic USER 168 if aOffset is negative. -*/ - { - TBool dlOnly = EFalse; - TInt pageSize; - GET_PAGE_SIZE(pageSize); - TInt align = RHybridHeap::ECellAlignment; // Always use EABI 8 byte alignment - - __ASSERT_ALWAYS(aMinLength>=0, ::Panic(ETHeapMinLengthNegative)); - __ASSERT_ALWAYS(aMaxLength>=0, ::Panic(ETHeapMaxLengthNegative)); - - if ( aMaxLength > 0 ) - __ASSERT_ALWAYS(aMaxLength>=aMinLength, ::Panic(ETHeapCreateMaxLessThanMin)); - - // Stick to EABI alignment for the start offset, if any - aOffset = _ALIGN_UP(aOffset, align); - - // Using an aOffset > 0 means that we can't use the hybrid allocator and have to revert to Doug Lea only - if (aOffset > 0) - dlOnly = ETrue; - - // Ensure that the minimum length is enough to hold the RHybridHeap object itself - TInt minCell = _ALIGN_UP(Max((TInt)RHybridHeap::EAllocCellSize, (TInt)RHybridHeap::EFreeCellSize), align); - TInt hybridHeapSize = (sizeof(RHybridHeap) + minCell); - if (aMinLength < hybridHeapSize) - aMinLength = hybridHeapSize; - - // Round the minimum length up to a multiple of the page size, taking into account that the - // offset takes up a part of the chunk's memory - aMinLength = _ALIGN_UP((aMinLength + aOffset), pageSize); - - // If aMaxLength is 0 then use the entire chunk - TInt chunkSize = aChunk.MaxSize(); - if (aMaxLength == 0) - { - aMaxLength = chunkSize; - } - // Otherwise round the maximum length up to a multiple of the page size, taking into account that - // the offset takes up a part of the chunk's memory. We also clip the maximum length to the chunk - // size, so the user may get a little less than requested if the chunk size is not large enough - else - { - aMaxLength = _ALIGN_UP((aMaxLength + aOffset), pageSize); - if (aMaxLength > chunkSize) - aMaxLength = chunkSize; - } - - // If the rounded up values don't make sense then a crazy aMinLength or aOffset must have been passed - // in, so fail the heap creation - if (aMinLength > aMaxLength) - return NULL; - - // Adding the offset into the minimum and maximum length was only necessary for ensuring a good fit of - // the heap into the chunk. Re-adjust them now back to non offset relative sizes - aMinLength -= aOffset; - aMaxLength -= aOffset; - - // If we are still creating the hybrid allocator (call parameter - // aOffset is 0 and aMaxLength > aMinLength), we must reduce heap - // aMaxLength size to the value aMaxLength/2 and set the aOffset to point in the middle of chunk. - TInt offset = aOffset; - TInt maxLength = aMaxLength; - if (!dlOnly && (aMaxLength > aMinLength)) - maxLength = offset = _ALIGN_UP(aMaxLength >> 1, pageSize); - - // Try to use commit to map aMinLength physical memory for the heap, taking into account the offset. If - // the operation fails, suppose that the chunk is not a disconnected heap and try to map physical memory - // with adjust. In this case, we also can't use the hybrid allocator and have to revert to Doug Lea only - TBool useAdjust = EFalse; - TInt r = aChunk.Commit(offset, aMinLength); - if (r == KErrGeneral) - { - dlOnly = useAdjust = ETrue; - r = aChunk.Adjust(aMinLength); - if (r != KErrNone) - return NULL; - } - else if (r == KErrNone) - { - // We have a disconnected chunk reset aOffset and aMaxlength - aOffset = offset; - aMaxLength = maxLength; - } - - else - return NULL; - - // Parameters have been mostly verified and we know whether to use the hybrid allocator or Doug Lea only. The - // constructor for the hybrid heap will automatically drop back to Doug Lea if it determines that aMinLength - // == aMaxLength, so no need to worry about that requirement here. The user specified alignment is not used but - // is passed in so that it can be sanity checked in case the user is doing something totally crazy with it - RHybridHeap* h = new (aChunk.Base() + aOffset) RHybridHeap(aChunk.Handle(), aOffset, aMinLength, aMaxLength, - aGrowBy, aAlign, aSingleThread, dlOnly, useAdjust); - - if (h->ConstructLock(aMode) != KErrNone) - return NULL; - - // Return the heap address - return h; - } - -#define UserTestDebugMaskBit(bit) (TBool)(UserSvr::DebugMask(bit>>5) & (1<<(bit&31))) - -_LIT(KLitDollarHeap,"$HEAP"); -EXPORT_C TInt UserHeap::CreateThreadHeap(SStdEpocThreadCreateInfo& aInfo, RHeap*& aHeap, TInt aAlign, TBool aSingleThread) -/** -@internalComponent -*/ -// -// Create a user-side heap -// -{ - TInt page_size; - GET_PAGE_SIZE(page_size); - TInt minLength = _ALIGN_UP(aInfo.iHeapInitialSize, page_size); - TInt maxLength = Max(aInfo.iHeapMaxSize, minLength); - if (UserTestDebugMaskBit(96)) // 96 == KUSERHEAPTRACE in nk_trace.h - aInfo.iFlags |= ETraceHeapAllocs; - // Create the thread's heap chunk. - RChunk c; - TChunkCreateInfo createInfo; - - createInfo.SetThreadHeap(0, maxLength, KLitDollarHeap()); // Initialise with no memory committed. -#if USE_HYBRID_HEAP - // - // Create disconnected chunk for hybrid heap with double max length value - // - maxLength = 2*maxLength; - createInfo.SetDisconnected(0, 0, maxLength); -#endif - // Set the paging policy of the heap chunk based on the thread's paging policy. - TUint pagingflags = aInfo.iFlags & EThreadCreateFlagPagingMask; - switch (pagingflags) - { - case EThreadCreateFlagPaged: - createInfo.SetPaging(TChunkCreateInfo::EPaged); - break; - case EThreadCreateFlagUnpaged: - createInfo.SetPaging(TChunkCreateInfo::EUnpaged); - break; - case EThreadCreateFlagPagingUnspec: - // Leave the chunk paging policy unspecified so the process's - // paging policy is used. - break; - } - - TInt r = c.Create(createInfo); - if (r!=KErrNone) - return r; - - aHeap = ChunkHeap(c, minLength, page_size, maxLength, aAlign, aSingleThread, EChunkHeapSwitchTo|EChunkHeapDuplicate); - c.Close(); - - if ( !aHeap ) - return KErrNoMemory; - - if (aInfo.iFlags & ETraceHeapAllocs) - { - aHeap->iFlags |= RHeap::ETraceAllocs; - BTraceContext8(BTrace::EHeap, BTrace::EHeapCreate,(TUint32)aHeap, RHybridHeap::EAllocCellSize); - TInt chunkId = ((RHandleBase&)((RHybridHeap*)aHeap)->iChunkHandle).BTraceId(); - BTraceContext8(BTrace::EHeap, BTrace::EHeapChunkCreate, (TUint32)aHeap, chunkId); - } - if (aInfo.iFlags & EMonitorHeapMemory) - aHeap->iFlags |= RHeap::EMonitorMemory; - - return KErrNone; -} - -#endif // __KERNEL_MODE__