diff -r 000000000000 -r a41df078684a kernel/eka/common/heap.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/eka/common/heap.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,1704 @@ +// 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: +// e32\common\heap.cpp +// +// + +#include "common.h" +#ifdef __KERNEL_MODE__ +#include +#endif + +#ifdef _DEBUG +#define __SIMULATE_ALLOC_FAIL(s) if (CheckForSimulatedAllocFail()) {s} +#define __CHECK_CELL(p) CheckCell(p) +#define __ZAP_CELL(p) memset( ((TUint8*)p) + RHeap::EAllocCellSize, 0xde, p->len - RHeap::EAllocCellSize) +#define __DEBUG_SAVE(p) TInt dbgNestLevel = ((SDebugCell*)p)->nestingLevel +#define __DEBUG_RESTORE(p) ((SDebugCell*)(((TUint8*)p)-EAllocCellSize))->nestingLevel = dbgNestLevel +#else +#define __SIMULATE_ALLOC_FAIL(s) +#define __CHECK_CELL(p) +#define __ZAP_CELL(p) +#define __DEBUG_SAVE(p) +#define __DEBUG_RESTORE(p) +#endif + +#define __NEXT_CELL(p) ((SCell*)(((TUint8*)p)+p->len)) + +#define __POWER_OF_2(x) ((TUint32)((x)^((x)-1))>=(TUint32)(x)) + +#define __MEMORY_MONITOR_CHECK_CELL(p) \ + { \ + TLinAddr m = TLinAddr(iAlign-1); \ + SCell* c = (SCell*)(((TUint8*)p)-EAllocCellSize); \ + if((c->len & m) || (c->leniTop)) \ + BTraceContext12(BTrace::EHeap, BTrace::EHeapCorruption, (TUint32)this, (TUint32)p, (TUint32)c->len-EAllocCellSize); \ + } + +/** +@SYMPatchable +@publishedPartner +@released + +Defines the minimum cell size of a heap. + +The constant can be changed at ROM build time using patchdata OBY keyword. +*/ +#ifdef __X86GCC__ // For X86GCC we dont use the proper data import attribute +#undef IMPORT_D // since the constant is 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; + +#pragma warning( disable : 4705 ) // statement has no effect +UEXPORT_C RHeap::RHeap(TInt aMaxLength, TInt aAlign, TBool aSingleThread) +/** +@internalComponent +*/ +// +// Constructor for fixed size heap +// + : iMinLength(aMaxLength), iMaxLength(aMaxLength), iOffset(0), iGrowBy(0), iChunkHandle(0), + iNestingLevel(0), iAllocCount(0), iFailType(ENone), iTestData(NULL) + { + iAlign = aAlign ? aAlign : ECellAlignment; + iPageSize = 0; + iFlags = aSingleThread ? (ESingleThreaded|EFixedSize) : EFixedSize; + Initialise(); + } +#pragma warning( default : 4705 ) + + + + +UEXPORT_C RHeap::RHeap(TInt aChunkHandle, TInt aOffset, TInt aMinLength, TInt aMaxLength, TInt aGrowBy, TInt aAlign, TBool aSingleThread) +/** +@internalComponent +*/ +// +// Constructor for chunk heaps. +// + : iOffset(aOffset), iChunkHandle(aChunkHandle), + iNestingLevel(0), iAllocCount(0), iFailType(ENone), iTestData(NULL) + { + TInt sz = iBase - ((TUint8*)this - iOffset); + GET_PAGE_SIZE(iPageSize); + __ASSERT_ALWAYS(iOffset>=0, HEAP_PANIC(ETHeapNewBadOffset)); + iMinLength = Max(aMinLength, sz + EAllocCellSize); + iMinLength = _ALIGN_UP(iMinLength, iPageSize); + iMaxLength = Max(aMaxLength, iMinLength); + iMaxLength = _ALIGN_UP(iMaxLength, iPageSize); + iGrowBy = _ALIGN_UP(aGrowBy, iPageSize); + iFlags = aSingleThread ? ESingleThreaded : 0; + iAlign = aAlign ? aAlign : ECellAlignment; + Initialise(); + } + + + + +UEXPORT_C TAny* RHeap::operator new(TUint aSize, TAny* aBase) __NO_THROW +/** +@internalComponent +*/ + { + __ASSERT_ALWAYS(aSize>=sizeof(RHeap), HEAP_PANIC(ETHeapNewBadSize)); + RHeap* h = (RHeap*)aBase; + h->iAlign = 0x80000000; // garbage value + h->iBase = ((TUint8*)aBase) + aSize; + return aBase; + } + +void RHeap::Initialise() +// +// Initialise the heap. +// + { + + __ASSERT_ALWAYS((TUint32)iAlign>=sizeof(TAny*) && __POWER_OF_2(iAlign), HEAP_PANIC(ETHeapNewBadAlignment)); + iCellCount = 0; + iTotalAllocSize = 0; + iBase = (TUint8*)Align(iBase + EAllocCellSize); + iBase -= EAllocCellSize; + TInt b = iBase - ((TUint8*)this - iOffset); + TInt len = _ALIGN_DOWN(iMinLength - b, iAlign); + iTop = iBase + len; + iMinLength = iTop - ((TUint8*)this - iOffset); + iMinCell = Align(KHeapMinCellSize + Max((TInt)EAllocCellSize, (TInt)EFreeCellSize)); +#ifdef _DEBUG + memset(iBase, 0xa5, len); +#endif + SCell* pM=(SCell*)iBase; // First free cell + iFree.next=pM; // Free list points to first free cell + iFree.len=0; // Stop free from joining this with a free block + pM->next=NULL; // Terminate the free list + pM->len=len; // Set the size of the free cell + } + +#ifdef _DEBUG +void RHeap::CheckCell(const SCell* aCell) const + { + TLinAddr m = TLinAddr(iAlign - 1); + + __ASSERT_DEBUG(!(aCell->len & m), HEAP_PANIC(ETHeapBadCellAddress)); + __ASSERT_DEBUG(aCell->len >= iMinCell, HEAP_PANIC(ETHeapBadCellAddress)); + __ASSERT_DEBUG((TUint8*)aCell>=iBase, HEAP_PANIC(ETHeapBadCellAddress)); + __ASSERT_DEBUG((TUint8*)__NEXT_CELL(aCell)<=iTop, HEAP_PANIC(ETHeapBadCellAddress)); + } +#endif + +UEXPORT_C RHeap::SCell* RHeap::GetAddress(const TAny* aCell) const +// +// As much as possible, check a cell address and backspace it +// to point at the cell header. +// + { + + TLinAddr m = TLinAddr(iAlign - 1); + __ASSERT_ALWAYS(!(TLinAddr(aCell)&m), HEAP_PANIC(ETHeapBadCellAddress)); + + SCell* pC = (SCell*)(((TUint8*)aCell)-EAllocCellSize); + __CHECK_CELL(pC); + + return pC; + } + + + + +UEXPORT_C TInt RHeap::AllocLen(const TAny* aCell) const +/** +Gets the length of the available space in the specified allocated cell. + +@param aCell A pointer to the allocated cell. + +@return The length of the available space in the allocated cell. + +@panic USER 42 if aCell does not point to a valid cell. +*/ + { + + SCell* pC = GetAddress(aCell); + return pC->len - EAllocCellSize; + } + + + + + +#if !defined(__HEAP_MACHINE_CODED__) || defined(_DEBUG) +RHeap::SCell* RHeap::DoAlloc(TInt aSize, SCell*& aLastFree) +// +// Allocate without growing. aSize includes cell header and alignment. +// Lock already held. +// + { + SCell* pP = &iFree; + SCell* pC = pP->next; + for (; pC; pP=pC, pC=pC->next) // Scan the free list + { + __CHECK_CELL(pC); + SCell* pE; + if (pC->len >= aSize) // Block size bigger than request + { + if (pC->len - aSize < iMinCell) // Leftover must be large enough to hold an SCell + { + aSize = pC->len; // It isn't, so take it all + pE = pC->next; // Set the next field + } + else + { + pE = (SCell*)(((TUint8*)pC)+aSize); // Take amount required + pE->len = pC->len - aSize; // Initialize new free cell + pE->next = pC->next; + } + pP->next = pE; // Update previous pointer + pC->len = aSize; // Set control size word +#if defined(_DEBUG) + ((SDebugCell*)pC)->nestingLevel = iNestingLevel; + ((SDebugCell*)pC)->allocCount = ++iAllocCount; +#endif + return pC; + } + } + aLastFree = pP; + return NULL; + } +#endif + + + + +UEXPORT_C TAny* RHeap::Alloc(TInt aSize) +/** +Allocates a cell of the specified size from the heap. + +If there is insufficient memory available on the heap from which to allocate +a cell of the required size, the function returns NULL. + +The cell is aligned according to the alignment value specified at construction, +or the default alignment value, if an explict value was not specified. + +The resulting size of the allocated cell may be rounded up to a +value greater than aSize, but is guaranteed to be not less than aSize. + +@param aSize The +size of the cell to be allocated from the heap + +@return A pointer to the allocated cell. NULL if there is insufficient memory + available. + +@panic USER 47 if the maximum unsigned value of aSize is greater than or equal + to the value of KMaxTInt/2; for example, calling Alloc(-1) raises + this panic. + +@see KMaxTInt +*/ + { + + __CHECK_THREAD_STATE; + __ASSERT_ALWAYS((TUint)aSize<(KMaxTInt/2),HEAP_PANIC(ETHeapBadAllocatedCellSize)); + __SIMULATE_ALLOC_FAIL(return NULL;) + + TInt origSize = aSize; + aSize = Max(Align(aSize + EAllocCellSize), iMinCell); + SCell* pL = NULL; + Lock(); + SCell* pC = (SCell*)DoAlloc(aSize, pL); + if (!pC && !(iFlags & EFixedSize)) + { + // try to grow chunk heap + TInt r = TryToGrowHeap(aSize, pL); + if (r==KErrNone) + pC = DoAlloc(aSize, pL); + } + if (pC) + ++iCellCount, iTotalAllocSize += (pC->len - EAllocCellSize); + Unlock(); + if (pC) + { + TAny* result=((TUint8*)pC) + EAllocCellSize; + if (iFlags & ETraceAllocs) + { + TUint32 traceData[2]; + traceData[0] = AllocLen(result); + traceData[1] = origSize; + BTraceContextN(BTrace::EHeap, BTrace::EHeapAlloc, (TUint32)this, (TUint32)result, traceData, sizeof(traceData)); + } +#ifdef __KERNEL_MODE__ + memclr(result, pC->len - EAllocCellSize); +#endif + return result; + } + if (iFlags & ETraceAllocs) + BTraceContext8(BTrace::EHeap, BTrace::EHeapAllocFail, (TUint32)this, (TUint32)origSize); + return NULL; + } + + + + +TInt RHeap::TryToGrowHeap(TInt aSize, SCell* aLastFree) + { + TBool at_end = IsLastCell(aLastFree); + TInt extra = at_end ? aSize - aLastFree->len : aSize; + extra = (extra + iGrowBy - 1) / iGrowBy; + extra *= iGrowBy; + TInt cur_len = _ALIGN_UP(iTop - ((TUint8*)this - iOffset), iPageSize); + TInt new_len = cur_len + extra; + TInt r = KErrNoMemory; + if (new_len <= iMaxLength) + { + r = SetBrk(new_len); + if (r == KErrNone) + { + if (at_end) + aLastFree->len += extra; + else + { + SCell* pC = (SCell*)iTop; + pC->len = extra; + pC->next = NULL; + aLastFree->next = pC; + } + iTop += extra; + } + } + return r; + } + + + + +#ifndef __KERNEL_MODE__ +EXPORT_C TInt RHeap::Compress() +/** +Compresses the heap. + +The function frees excess committed space from the top +of the heap. The size of the heap is never reduced below the minimum size +specified during creation of the heap. + +@return The space reclaimed. If no space can be reclaimed, then this value + is zero. +*/ + { + + if (iFlags & EFixedSize) + return 0; + TInt r = 0; + Lock(); + SCell* pC = &iFree; + for (; pC->next; pC=pC->next) {} + if (pC!=&iFree) + { + __CHECK_CELL(pC); + if (IsLastCell(pC)) + r = Reduce(pC); + } + Unlock(); + return r; + } +#endif + + + + +#if !defined(__HEAP_MACHINE_CODED__) || defined(_DEBUG) +void RHeap::DoFree(SCell* pC) + { + __ZAP_CELL(pC); + + SCell* pP = &iFree; + SCell* pE = pP->next; + for (; pE && pEnext) {} + if (pE) // Is there a following free cell? + { + SCell* pN = __NEXT_CELL(pC); + __ASSERT_ALWAYS(pN<=pE, HEAP_PANIC(ETHeapFreeBadNextCell)); // Following cell overlaps + if (pN==pE) // Is it adjacent + { + pC->len += pE->len; // Yes - coalesce adjacent free cells + pC->next = pE->next; + } + else // pNnext = pE; // Otherwise just point to it + } + else + pC->next = NULL; // No following free cell + SCell* pN = __NEXT_CELL(pP); // pN=pP=&iFree if no preceding free cell + __ASSERT_ALWAYS(pN<=pC, HEAP_PANIC(ETHeapFreeBadPrevCell)); // Previous cell overlaps + if (pN==pC) // Is it adjacent + { + pP->len += pC->len; // Yes - coalesce adjacent free cells + pP->next = pC->next; + pC = pP; // for size reduction check + } + else // pNnext = pC; // point previous cell to the one being freed + pN = __NEXT_CELL(pC); // End of amalgamated free cell + if ((TUint8*)pN==iTop && !(iFlags & EFixedSize) && + pC->len >= KHeapShrinkHysRatio*(iGrowBy>>8)) + Reduce(pC); + } +#endif + + + + +UEXPORT_C void RHeap::Free(TAny* aCell) +/** +Frees the specified cell and returns it to the heap. + +@param aCell A pointer to a valid cell; this pointer can also be NULL, + in which case the function does nothing and just returns. + +@panic USER 42 if aCell points to an invalid cell. +*/ + { + __CHECK_THREAD_STATE; + if (!aCell) + return; + Lock(); + if (iFlags & EMonitorMemory) + __MEMORY_MONITOR_CHECK_CELL(aCell); + SCell* pC = GetAddress(aCell); + --iCellCount; + iTotalAllocSize -= (pC->len - EAllocCellSize); + DoFree(pC); + if (iFlags & ETraceAllocs) + BTraceContext8(BTrace::EHeap, BTrace::EHeapFree, (TUint32)this, (TUint32)aCell); + Unlock(); + } + + + + +TInt RHeap::Reduce(SCell* aCell) + { + TInt reduce=0; + TInt offset=((TUint8*)aCell)-((TUint8*)this - iOffset); + if (offset>=iMinLength) + reduce = aCell->len; // length of entire free cell + else + reduce = offset + aCell->len - iMinLength; // length of free cell past minimum heap size + reduce = _ALIGN_DOWN(reduce, iPageSize); // round down to page multiple + if (reduce<=0) + return 0; // can't reduce this heap + TInt new_cell_len = aCell->len - reduce; // length of last free cell after reduction + if (new_cell_len == 0) + { + // the free cell can be entirely eliminated + SCell* pP = &iFree; + for (; pP->next!=aCell; pP=pP->next) {} + pP->next = NULL; + } + else + { + if (new_cell_len < iMinCell) + { + // max reduction would leave a cell too small + reduce -= iPageSize; + new_cell_len += iPageSize; + } + aCell->len = new_cell_len; // reduce the cell length + } + iTop -= reduce; + TInt new_len = _ALIGN_UP(iTop - ((TUint8*)this - iOffset), iPageSize); + TInt r = SetBrk(new_len); + __ASSERT_ALWAYS(r==KErrNone, HEAP_PANIC(ETHeapReduceFailed)); + return reduce; + } + + + + +#ifndef __KERNEL_MODE__ +EXPORT_C void RHeap::Reset() +/** +Frees all allocated cells on this heap. +*/ + { + + Lock(); + if (!(iFlags & EFixedSize)) + { + TInt r = SetBrk(iMinLength); + __ASSERT_ALWAYS(r==KErrNone, HEAP_PANIC(ETHeapResetFailed)); + } + Initialise(); + Unlock(); + } +#endif + + + + +inline void RHeap::FindFollowingFreeCell(SCell* aCell, SCell*& aPrev, SCell*& aNext) +// +// Find the free cell that immediately follows aCell, if one exists +// If found, aNext is set to point to it, else it is set to NULL. +// aPrev is set to the free cell before aCell or the dummy free cell where there are no free cells before aCell. +// Called with lock enabled. +// + { + aPrev = &iFree; + aNext = aPrev->next; + for (; aNext && aNextnext) {} + + if (aNext) // If there is a following free cell, check its directly after aCell. + { + SCell* pNextCell = __NEXT_CELL(aCell); // end of this cell + __ASSERT_ALWAYS(pNextCell<=aNext, (Unlock(), HEAP_PANIC(ETHeapReAllocBadNextCell))); // Following free cell overlaps + if (pNextCell!=aNext) + aNext=NULL; + } + } + + + + +TInt RHeap::TryToGrowCell(SCell* aCell,SCell* aPrev, SCell* aNext, TInt aSize) +// +// Try to grow the heap cell 'aCell' in place, to size 'aSize'. +// Requires the free cell immediately after aCell (aNext), and the free cell prior to +// that (aPrev), to be provided. (As found by FindFollowingFreeCell) +// + + { + TInt extra = aSize - aCell->len; + if (aNext && (aNext->len>=extra)) // Is there a following free cell big enough? + { + if (aNext->len - extra >= iMinCell) // take part of free cell ? + { + SCell* pX = (SCell*)((TUint8*)aNext + extra); // remainder of free cell + pX->next = aNext->next; // remainder->next = original free cell->next + pX->len = aNext->len - extra; // remainder length = original free cell length - extra + aPrev->next = pX; // put remainder into free chain + } + else + { + extra = aNext->len; // Take whole free cell + aPrev->next = aNext->next; // remove from free chain + } +#ifdef __KERNEL_MODE__ + memclr(((TUint8*)aCell) + aCell->len, extra); +#endif + aCell->len += extra; // update reallocated cell length + iTotalAllocSize += extra; + return KErrNone; + } + return KErrGeneral; // No space to grow cell + } + + + + +// UEXPORT_C TAny* RHeap::ReAlloc(TAny* aCell, TInt aSize, TInt aMode) +/** +Increases or decreases the size of an existing cell in the heap. + +If the cell is being decreased in size, then it is guaranteed not to move, +and the function returns the pointer originally passed in aCell. Note that the +length of the cell will be the same if the difference between the old size +and the new size is smaller than the minimum cell size. + +If the cell is being increased in size, i.e. aSize is bigger than its +current size, then the function tries to grow the cell in place. +If successful, then the function returns the pointer originally +passed in aCell. If unsuccessful, then: + +1. if the cell cannot be moved, i.e. aMode has the ENeverMove bit set, then + the function returns NULL. +2. if the cell can be moved, i.e. aMode does not have the ENeverMove bit set, + then the function tries to allocate a new replacement cell, and, if + successful, returns a pointer to the new cell; if unsuccessful, it + returns NULL. + +Note that in debug mode, the function returns NULL if the cell cannot be grown +in place, regardless of whether the ENeverMove bit is set. + +If the reallocated cell is at a different location from the original cell, then +the content of the original cell is copied to the reallocated cell. + +If the supplied pointer, aCell is NULL, then the function attempts to allocate +a new cell, but only if the cell can be moved, i.e. aMode does not have +the ENeverMove bit set. + +Note the following general points: + +1. If reallocation fails, the content of the original cell is preserved. + +2. The resulting size of the re-allocated cell may be rounded up to a value + greater than aSize, but is guaranteed to be not less than aSize. + +@param aCell A pointer to the cell to be reallocated. This may be NULL. + +@param aSize The new size of the cell. This may be bigger or smaller than the + size of the original cell. + +@param aMode Flags controlling the reallocation. The only bit which has any + effect on this function is that defined by the enumeration + ENeverMove of the enum RAllocator::TReAllocMode. + If this is set, then any successful reallocation guarantees not + to have changed the start address of the cell. + By default, this parameter is zero. + +@return A pointer to the reallocated cell. This may be the same as the original + pointer supplied through aCell. NULL if there is insufficient memory to + reallocate the cell, or to grow it in place. + +@panic USER 42, if aCell is not NULL, and does not point to a valid cell. +@panic USER 47, if the maximum unsigned value of aSize is greater + than or equal to KMaxTInt/2. For example, + calling ReAlloc(someptr,-1) raises this panic. + +@see RAllocator::TReAllocMode +*/ +UEXPORT_C TAny* RHeap::ReAlloc(TAny* aCell, TInt aSize, TInt aMode) + { + if (aCell && iFlags&EMonitorMemory) + __MEMORY_MONITOR_CHECK_CELL(aCell); + TAny* retval = ReAllocImpl(aCell, aSize, aMode); + if (iFlags & ETraceAllocs) + { + if (retval) + { + TUint32 traceData[3]; + traceData[0] = AllocLen(retval); + traceData[1] = aSize; + traceData[2] = (TUint32)aCell; + BTraceContextN(BTrace::EHeap, BTrace::EHeapReAlloc,(TUint32)this, (TUint32)retval,traceData, sizeof(traceData)); + } + else + BTraceContext12(BTrace::EHeap, BTrace::EHeapReAllocFail, (TUint32)this, (TUint32)aCell, (TUint32)aSize); + } + return retval; + } +inline TAny* RHeap::ReAllocImpl(TAny* aCell, TInt aSize, TInt aMode) + { + __CHECK_THREAD_STATE; + if (!aCell) + return (aMode & ENeverMove) ? NULL : Alloc(aSize); + __ASSERT_ALWAYS((TUint)aSize<(KMaxTInt/2),HEAP_PANIC(ETHeapBadAllocatedCellSize)); + Lock(); + SCell* pC = GetAddress(aCell); + TInt old_len = pC->len; + __DEBUG_SAVE(pC); + aSize = Max(Align(aSize + EAllocCellSize), iMinCell); + if (aSize > old_len) // Trying to grow cell + { + __SIMULATE_ALLOC_FAIL({ Unlock(); return NULL;}) + + // Try to grow cell in place, without reallocation + SCell* pPrev; + SCell* pNext; + FindFollowingFreeCell(pC,pPrev, pNext); + TInt r = TryToGrowCell(pC, pPrev, pNext, aSize); + + if (r==KErrNone) + { + Unlock(); + return aCell; + } + + if (!(aMode & ENeverMove)) + // If moving allowed, try re-alloc. + // If we need to extend heap,and cell is at the end, try and grow in place + { + SCell* pLastFree; + SCell* pNewCell = (SCell*)DoAlloc(aSize, pLastFree); + if (!pNewCell && !(iFlags & EFixedSize)) + // if we need to extend the heap to alloc + { + if (IsLastCell(pC) || (pNext && IsLastCell(pNext))) + // if last used Cell, try and extend heap and then cell + { + TInt r = TryToGrowHeap(aSize - old_len, pLastFree); + if (r==KErrNone) + { + r = TryToGrowCell(pC, pPrev, pPrev->next, aSize); + Unlock(); + __ASSERT_DEBUG(r == KErrNone, HEAP_PANIC(ETHeapCellDidntGrow)); + return aCell; + } + } + else + // try to grow chunk heap and Alloc on it + { + TInt r = TryToGrowHeap(aSize, pLastFree); + if (r==KErrNone) + pNewCell = DoAlloc(aSize, pLastFree); + } + } + + if (pNewCell) + // if we created a new cell, adjust tellies, copy the contents and delete old cell. + { + iCellCount++; + iTotalAllocSize += (pNewCell->len - EAllocCellSize); + + Unlock(); + TUint8* raw = ((TUint8*) pNewCell); + + memcpy(raw + EAllocCellSize, aCell, old_len - EAllocCellSize); +#ifdef __KERNEL_MODE__ + memclr(raw + old_len, pNewCell->len - old_len); +#endif + Free(aCell); + __DEBUG_RESTORE(raw + EAllocCellSize); + return raw + EAllocCellSize; + } + } + else + // No moving, but still posible to extend the heap (if heap extendable) + { + if (!(iFlags & EFixedSize) && (IsLastCell(pC) || (pNext && IsLastCell(pNext)))) + { + SCell* pLastFree = pNext ? pNext : pPrev; + TInt r = TryToGrowHeap(aSize - old_len, pLastFree); + if (r==KErrNone) + { + r = TryToGrowCell(pC, pPrev, pPrev->next, aSize); + Unlock(); + __ASSERT_DEBUG(r==KErrNone, HEAP_PANIC(ETHeapCellDidntGrow)); + return aCell; + } + } + } + Unlock(); + return NULL; + } + if (old_len - aSize >= iMinCell) + { + // cell shrinking, remainder big enough to form a new free cell + SCell* pX = (SCell*)((TUint8*)pC + aSize); // pointer to new free cell + pC->len = aSize; // update cell size + pX->len = old_len - aSize; // size of remainder + iTotalAllocSize -= pX->len; + DoFree(pX); // link new free cell into chain, shrink heap if necessary + } + Unlock(); + return aCell; + } + + + + +#ifndef __KERNEL_MODE__ + +EXPORT_C TInt RHeap::Available(TInt& aBiggestBlock) const +/** +Gets the total free space currently available on the heap and the space +available in the largest free block. + +The space available represents the total space which can be allocated. + +Note that compressing the heap may reduce the total free space available and +the space available in the largest free block. + +@param aBiggestBlock On return, contains the space available + in the largest free block on the heap. + +@return The total free space currently available on the heap. +*/ + { + + TInt total = 0; + TInt max = 0; + Lock(); + SCell* pC = iFree.next; + for (; pC; pC=pC->next) + { + TInt l = pC->len - EAllocCellSize; + if (l > max) + max = l; + total += l; + } + Unlock(); + aBiggestBlock = max; + return total; + } + + + + +EXPORT_C TInt RHeap::AllocSize(TInt& aTotalAllocSize) const +/** +Gets the number of cells allocated on this heap, and the total space +allocated to them. + +@param aTotalAllocSize On return, contains the total space allocated + to the cells. + +@return The number of cells allocated on this heap. +*/ + { + Lock(); + TInt c = iCellCount; + aTotalAllocSize = iTotalAllocSize; + Unlock(); + return c; + } + + + + +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, aMaxLength bytes are committed by the chunk. +The heap cannot be extended. + +@param aBase A pointer to the location where the heap is to be constructed. +@param aMaxLength The length of the heap. If the supplied value is less + than KMinHeapSize, it is discarded and the value KMinHeapSize + is used instead. +@param aAlign The alignment of heap cells. +@param aSingleThread Indicates whether single threaded or not. + +@return A pointer to the new heap, or NULL if the heap could not be created. + +@panic USER 56 if aMaxLength is negative. +@panic USER 172 if aAlign is not a power of 2 or is less than the size of a TAny*. +*/ +// +// Force construction of the fixed memory. +// + { + + __ASSERT_ALWAYS(aMaxLength>=0, ::Panic(ETHeapMaxLengthNegative)); + if (aMaxLengthiLock.CreateLocal(); + if (r!=KErrNone) + return NULL; + h->iHandles = (TInt*)&h->iLock; + h->iHandleCount = 1; + } + return h; + } + + +/** +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 KMinHeapSize, then it + is discarded and the value KMinHeapSize 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; + } + + +/** +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; + if (maxLength < KMinHeapSize) + maxLength = KMinHeapSize; + + TChunkCreateInfo chunkInfo; + chunkInfo.SetNormal(0, maxLength); + 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. +@param aMaxLength The maximum length to which the heap can grow. + If the supplied value is less than KMinHeapSize, then it + is discarded and the value KMinHeapSize used instead. +@param aGrowBy The increments to the size of the host chunk. If a value is + not explicitly specified, the value KMinHeapGrowBy is taken + by default +@param aAlign The alignment of heap cells. +@param aSingleThread Indicates whether single threaded or not. + +@return A pointer to the new heap or NULL if the heap could not be created. + +@panic USER 41 if aMinLength is greater than the supplied value of aMaxLength. +@panic USER 55 if aMinLength is negative. +@panic USER 172 if aAlign is not a power of 2 or is less than the size of a TAny*. +*/ +// +// Allocate a Chunk of the requested size and force construction. +// + { + TChunkHeapCreateInfo createInfo(aMinLength, aMaxLength); + 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. + +The maximum length to which the heap can grow is the same as +the maximum size of the chunk. + +@param aChunk The chunk that will host the heap. +@param aMinLength The minimum length of the heap. +@param aGrowBy The increments to the size of the host chunk. +@param aMaxLength The maximum length to which the heap can grow. +@param aAlign The alignment of heap cells. +@param aSingleThread Indicates whether single threaded or not. +@param aMode Flags controlling the heap creation. This should be set + from one or more of the values in TChunkHeapCreateMode. + +@return A pointer to the new heap or NULL if the heap could not be created. + +@panic USER 172 if aAlign is not a power of 2 or is less than the size of a TAny*. +*/ +// +// Construct a heap in an already existing chunk +// + { + + 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 where a fixed amount of +additional data must be stored at a known location. The additional data can be +placed at the base address of the chunk, allowing it to be located without +depending on the internals of the heap structure. + +The maximum length to which the heap can grow is the maximum size of the chunk, +minus the offset. + +@param aChunk The chunk that will host the heap. +@param aMinLength The minimum length of the heap. +@param aOffset The offset from the start of the chunk, to the start of the heap. +@param aGrowBy The increments to the size of the host chunk. +@param aMaxLength The maximum length to which the heap can grow. +@param aAlign The alignment of heap cells. +@param aSingleThread Indicates whether single threaded or not. +@param aMode Flags controlling the heap creation. This should be set + from one or more of the values in TChunkHeapCreateMode. + +@return A pointer to the new heap or NULL if the heap could not be created. + +@panic USER 172 if aAlign is not a power of 2 or is less than the size of a TAny*. +*/ +// +// Construct a heap in an already existing chunk +// + { + + TInt page_size; + UserHal::PageSizeInBytes(page_size); + if (!aAlign) + aAlign = RHeap::ECellAlignment; + TInt maxLength = aChunk.MaxSize(); + TInt round_up = Max(aAlign, page_size); + TInt min_cell = _ALIGN_UP(Max((TInt)RHeap::EAllocCellSize, (TInt)RHeap::EFreeCellSize), aAlign); + aOffset = _ALIGN_UP(aOffset, 8); + if (aMaxLength && aMaxLength+aOffset=0, ::Panic(ETHeapMinLengthNegative)); + __ASSERT_ALWAYS(maxLength>=aMinLength, ::Panic(ETHeapCreateMaxLessThanMin)); + aMinLength = _ALIGN_UP(Max(aMinLength, (TInt)sizeof(RHeap) + min_cell) + aOffset, round_up); + TInt r=aChunk.Adjust(aMinLength); + if (r!=KErrNone) + return NULL; + + RHeap* h = new (aChunk.Base() + aOffset) RHeap(aChunk.Handle(), aOffset, aMinLength, maxLength, aGrowBy, aAlign, aSingleThread); + + TBool duplicateLock = EFalse; + if (!aSingleThread) + { + duplicateLock = aMode & EChunkHeapSwitchTo; + if(h->iLock.CreateLocal(duplicateLock ? EOwnerThread : EOwnerProcess)!=KErrNone) + { + h->iChunkHandle = 0; + return NULL; + } + } + + if (aMode & EChunkHeapSwitchTo) + User::SwitchHeap(h); + + h->iHandles = &h->iChunkHandle; + if (!aSingleThread) + { + // now change the thread-relative chunk/semaphore handles into process-relative handles + h->iHandleCount = 2; + if(duplicateLock) + { + RHandleBase s = h->iLock; + r = h->iLock.Duplicate(RThread()); + s.Close(); + } + if (r==KErrNone && (aMode & EChunkHeapDuplicate)) + { + r = ((RChunk*)&h->iChunkHandle)->Duplicate(RThread()); + if (r!=KErrNone) + h->iLock.Close(), h->iChunkHandle=0; + } + } + else + { + h->iHandleCount = 1; + if (aMode & EChunkHeapDuplicate) + r = ((RChunk*)&h->iChunkHandle)->Duplicate(RThread(), EOwnerThread); + } + + // return the heap address + return (r==KErrNone) ? h : NULL; + } + + + +#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; + UserHal::PageSizeInBytes(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. + + // Set the heap chunk to be paged or unpaged based on the thread's settings. + // aInfo.iFlags will have already been set by the kernel based on the paging policies. + TBool paged = (aInfo.iFlags & EThreadCreateFlagPagingMask) == EThreadCreateFlagPaged; + TChunkCreateInfo::TChunkPagingAtt paging = + (paged)? TChunkCreateInfo::EPaged : TChunkCreateInfo::EUnpaged; + createInfo.SetPaging(paging); + + 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, RHeap::EAllocCellSize); + TInt handle = aHeap->ChunkHandle(); + TInt chunkId = ((RHandleBase&)handle).BTraceId(); + BTraceContext8(BTrace::EHeap, BTrace::EHeapChunkCreate, (TUint32)aHeap, chunkId); + } + if (aInfo.iFlags & EMonitorHeapMemory) + aHeap->iFlags |= RHeap::EMonitorMemory; + return KErrNone; + } + +#endif // __KERNEL_MODE__ + +void RHeap::WalkCheckCell(TAny* aPtr, TCellType aType, TAny* aCell, TInt aLen) + { + (void)aCell; + SHeapCellInfo& info = *(SHeapCellInfo*)aPtr; + switch(aType) + { + case EGoodAllocatedCell: + { + ++info.iTotalAlloc; + info.iTotalAllocSize += (aLen-EAllocCellSize); +#if defined(_DEBUG) + RHeap& h = *info.iHeap; + if ( ((SDebugCell*)aCell)->nestingLevel == h.iNestingLevel ) + { + if (++info.iLevelAlloc==1) + info.iStranded = (SDebugCell*)aCell; +#ifdef __KERNEL_MODE__ + if (KDebugNum(KSERVER) || KDebugNum(KTESTFAST)) + { +// __KTRACE_OPT(KSERVER,Kern::Printf("LEAKED KERNEL HEAP CELL @ %08x : len=%d", aCell, aLen)); + Kern::Printf("LEAKED KERNEL HEAP CELL @ %08x : len=%d", aCell, aLen); + TLinAddr base = ((TLinAddr)aCell)&~0x0f; + TLinAddr end = ((TLinAddr)aCell)+(TLinAddr)aLen; + while(baseiRate, fail->iBurst); + } +#endif + break; + + case RAllocator::ECheckFailure: + // iRand will be incremented for each EFailNext, EBurstFailNext, + // EDeterministic and EBurstDeterministic failure. + r = iRand; + break; + + case RAllocator::ECopyDebugInfo: + { + TInt nestingLevel = ((SDebugCell*)a1)[-1].nestingLevel; + ((SDebugCell*)a2)[-1].nestingLevel = nestingLevel; + break; + } + case RHeap::EWalk: + Walk((TWalkFunc)a1, a2); + break; + default: + return KErrNotSupported; + } + return r; + } + + + + +void RHeap::Walk(TWalkFunc aFunc, TAny* aPtr) +// +// Walk the heap calling the info function. +// + { + + Lock(); + SCell* pC = (SCell*)iBase; // allocated cells + SCell* pF = &iFree; // free cells + FOREVER + { + pF = pF->next; // next free cell + if (!pF) + pF = (SCell*)iTop; // to make size checking work + else if ( (TUint8*)pF>=iTop || (pF->next && pF->next<=pF) ) + { + if (iFlags & ETraceAllocs) + BTraceContext12(BTrace::EHeap, BTrace::EHeapCorruption, (TUint32)this, (TUint32)pF+EFreeCellSize, 0); + // free cell pointer off the end or going backwards + Unlock(); + (*aFunc)(aPtr, EBadFreeCellAddress, pF, 0); + return; + } + else + { + TInt l = pF->len; + if (llen; + if (l pF) + { + if (iFlags & ETraceAllocs) + BTraceContext12(BTrace::EHeap, BTrace::EHeapCorruption, (TUint32)this, (TUint32)pC+EAllocCellSize, l-EAllocCellSize); + // cell overlaps next free cell + Unlock(); + (*aFunc)(aPtr, EBadAllocatedCellAddress, pC, l); + return; + } + pC = pN; + } + if ((TUint8*)pF == iTop) + break; // reached end of heap + pC = __NEXT_CELL(pF); // step to next allocated cell + (*aFunc)(aPtr, EGoodFreeCell, pF, pF->len); + } + Unlock(); + } + +TInt RHeap::DoCheckHeap(SCheckInfo* aInfo) + { + (void)aInfo; + SHeapCellInfo info; + memclr(&info, sizeof(info)); + info.iHeap = this; + Walk(&WalkCheckCell, &info); +#if defined(_DEBUG) + if (!aInfo) + return KErrNone; + TInt expected = aInfo->iCount; + TInt actual = aInfo->iAll ? info.iTotalAlloc : info.iLevelAlloc; + if (actual!=expected && !iTestData) + { +#ifdef __KERNEL_MODE__ + Kern::Fault("KERN-ALLOC COUNT", (expected<<16)|actual ); +#else + User::Panic(_L("ALLOC COUNT"), (expected<<16)|actual ); +#endif + } +#endif + return KErrNone; + } + +#ifdef _DEBUG +void RHeap::DoMarkStart() + { + if (iNestingLevel==0) + iAllocCount=0; + iNestingLevel++; + } + +TUint32 RHeap::DoMarkEnd(TInt aExpected) + { + if (iNestingLevel==0) + return 0; + SHeapCellInfo info; + SHeapCellInfo* p = iTestData ? (SHeapCellInfo*)iTestData : &info; + memclr(p, sizeof(info)); + p->iHeap = this; + Walk(&WalkCheckCell, p); + if (p->iLevelAlloc != aExpected && !iTestData) + return (TUint32)(p->iStranded + 1); + if (--iNestingLevel == 0) + iAllocCount = 0; + return 0; + } + +void ResetAllocCellLevels(TAny* aPtr, RHeap::TCellType aType, TAny* aCell, TInt aLen) + { + (void)aPtr; + (void)aLen; + RHeap::SDebugCell* cell = (RHeap::SDebugCell*)aCell; + if (aType == RHeap::EGoodAllocatedCell) + { + cell->nestingLevel = 0; + } + } + +void RHeap::DoSetAllocFail(TAllocFail aType, TInt aRate) + {// Default to a burst mode of 1, as aType may be a burst type. + DoSetAllocFail(aType, aRate, 1); + } + +// Don't change as the ETHeapBadDebugFailParameter check below and the API +// documentation rely on this being 16 for RHeap. +LOCAL_D const TInt KBurstFailRateShift = 16; +LOCAL_D const TInt KBurstFailRateMask = (1 << KBurstFailRateShift) - 1; + +void RHeap::DoSetAllocFail(TAllocFail aType, TInt aRate, TUint aBurst) + { + if (aType==EReset) + { + // reset levels of all allocated cells to 0 + // this should prevent subsequent tests failing unnecessarily + iFailed = EFalse; // Reset for ECheckFailure relies on this. + Walk(&ResetAllocCellLevels, NULL); + // reset heap allocation mark as well + iNestingLevel=0; + iAllocCount=0; + aType=ENone; + } + + switch (aType) + { + case EBurstRandom: + case EBurstTrueRandom: + case EBurstDeterministic: + case EBurstFailNext: + // If the fail type is a burst type then iFailRate is split in 2: + // the 16 lsbs are the fail rate and the 16 msbs are the burst length. + if (TUint(aRate) > (TUint)KMaxTUint16 || aBurst > KMaxTUint16) + HEAP_PANIC(ETHeapBadDebugFailParameter); + + iFailed = EFalse; + iFailType = aType; + iFailRate = (aRate == 0) ? 1 : aRate; + iFailAllocCount = -iFailRate; + iFailRate = iFailRate | (aBurst << KBurstFailRateShift); + break; + + default: + iFailed = EFalse; + iFailType = aType; + iFailRate = (aRate == 0) ? 1 : aRate; // A rate of <1 is meaningless + iFailAllocCount = 0; + break; + } + + // Set up iRand for either: + // - random seed value, or + // - a count of the number of failures so far. + iRand = 0; +#ifndef __KERNEL_MODE__ + switch (iFailType) + { + case ETrueRandom: + case EBurstTrueRandom: + { + TTime time; + time.HomeTime(); + TInt64 seed = time.Int64(); + iRand = Math::Rand(seed); + break; + } + case ERandom: + case EBurstRandom: + { + TInt64 seed = 12345; + iRand = Math::Rand(seed); + break; + } + default: + break; + } +#endif + } + +TBool RHeap::CheckForSimulatedAllocFail() +// +// Check to see if the user has requested simulated alloc failure, and if so possibly +// Return ETrue indicating a failure. +// + { + // For burst mode failures iFailRate is shared + TUint16 rate = (TUint16)(iFailRate & KBurstFailRateMask); + TUint16 burst = (TUint16)(iFailRate >> KBurstFailRateShift); + TBool r = EFalse; + switch (iFailType) + { +#ifndef __KERNEL_MODE__ + case ERandom: + case ETrueRandom: + if (++iFailAllocCount>=iFailRate) + { + iFailAllocCount=0; + if (!iFailed) // haven't failed yet after iFailRate allocations so fail now + return(ETrue); + iFailed=EFalse; + } + else + { + if (!iFailed) + { + TInt64 seed=iRand; + iRand=Math::Rand(seed); + if (iRand%iFailRate==0) + { + iFailed=ETrue; + return(ETrue); + } + } + } + break; + + case EBurstRandom: + case EBurstTrueRandom: + if (++iFailAllocCount < 0) + { + // We haven't started failing yet so should we now? + TInt64 seed = iRand; + iRand = Math::Rand(seed); + if (iRand % rate == 0) + {// Fail now. Reset iFailAllocCount so we fail burst times + iFailAllocCount = 0; + r = ETrue; + } + } + else + { + if (iFailAllocCount < burst) + {// Keep failing for burst times + r = ETrue; + } + else + {// We've now failed burst times so start again. + iFailAllocCount = -(rate - 1); + } + } + break; +#endif + case EDeterministic: + if (++iFailAllocCount%iFailRate==0) + { + r=ETrue; + iRand++; // Keep count of how many times we have failed + } + break; + + case EBurstDeterministic: + // This will fail burst number of times, every rate attempts. + if (++iFailAllocCount >= 0) + { + if (iFailAllocCount == burst - 1) + {// This is the burst time we have failed so make it the last by + // reseting counts so we next fail after rate attempts. + iFailAllocCount = -rate; + } + r = ETrue; + iRand++; // Keep count of how many times we have failed + } + break; + + case EFailNext: + if ((++iFailAllocCount%iFailRate)==0) + { + iFailType=ENone; + r=ETrue; + iRand++; // Keep count of how many times we have failed + } + break; + + case EBurstFailNext: + if (++iFailAllocCount >= 0) + { + if (iFailAllocCount == burst - 1) + {// This is the burst time we have failed so make it the last. + iFailType = ENone; + } + r = ETrue; + iRand++; // Keep count of how many times we have failed + } + break; + default: + break; + } + return r; + } +#endif // ifdef _DEBUG + +UEXPORT_C TInt RHeap::Extension_(TUint aExtensionId, TAny*& a0, TAny* a1) + { + return RAllocator::Extension_(aExtensionId, a0, a1); + } + +#if defined(__HEAP_MACHINE_CODED__) && !defined(_DEBUG) +GLDEF_C void RHeap_PanicBadAllocatedCellSize() + { + HEAP_PANIC(ETHeapBadAllocatedCellSize); + } + +GLDEF_C void RHeap_PanicBadNextCell() + { + HEAP_PANIC(ETHeapFreeBadNextCell); + } + +GLDEF_C void RHeap_PanicBadPrevCell() + { + HEAP_PANIC(ETHeapFreeBadPrevCell); + } + +GLDEF_C void RHeap_PanicBadCellAddress() + { + HEAP_PANIC(ETHeapBadCellAddress); + } +#endif + + + + +