351 lines
12 KiB
C++
351 lines
12 KiB
C++
/**********************************************************************
|
|
|
|
Filename : HeapRoot.cpp
|
|
Content : Heap root used for bootstrapping and bookkeeping.
|
|
:
|
|
Created : 2009
|
|
Authors : Maxim Shemanarev
|
|
|
|
Copyright : (c) 2008 Scaleform Corp. All Rights Reserved.
|
|
|
|
Licensees may use this file in accordance with the valid Scaleform
|
|
Commercial License Agreement provided with the software.
|
|
|
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
|
|
THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR ANY PURPOSE.
|
|
|
|
**********************************************************************/
|
|
|
|
#include "GDebug.h"
|
|
#include "GMemoryHeapMH.h"
|
|
#include "GHeapRootMH.h"
|
|
#include "GHeapAllocEngineMH.h"
|
|
#include "GHeapDebugInfoMH.h"
|
|
|
|
|
|
GHeapRootMH* GHeapGlobalRootMH = 0;
|
|
GPageTableMH GHeapGlobalPageTableMH;
|
|
GHeapPageMH GHeapGlobalEmptyPageMH;
|
|
|
|
//------------------------------------------------------------------------
|
|
#ifdef GHEAP_TRACE_ALL
|
|
GMemoryHeap::HeapTracer* GHeapRootMH::pTracer = 0;
|
|
#endif
|
|
|
|
//------------------------------------------------------------------------
|
|
void GHeapGetMagicHeaders(UPInt pageStart, GHeapMagicHeadersInfo* headers)
|
|
{
|
|
headers->Header1 = 0;
|
|
headers->Header2 = 0;
|
|
UPInt start = (pageStart + GHeapPageMH::UnitMask) & ~UPInt(GHeapPageMH::UnitMask);
|
|
UPInt end = (pageStart + GHeapPageMH::PageSize) & ~UPInt(GHeapPageMH::UnitMask);
|
|
UPInt bound = (pageStart + GHeapPageMH::PageMask) & ~UPInt(GHeapPageMH::PageMask);
|
|
if (bound-start > GHeapPageMH::UnitSize)
|
|
{
|
|
headers->Header1 = (GHeapMagicHeader*)(bound - sizeof(GHeapMagicHeader));
|
|
}
|
|
if (end-bound > GHeapPageMH::UnitSize)
|
|
{
|
|
headers->Header2 = (GHeapMagicHeader*)bound;
|
|
}
|
|
headers->BitSet = (UInt32*)((bound-start > end-bound)?
|
|
(bound - sizeof(GHeapMagicHeader) - GHeapPageMH::BitSetBytes):
|
|
(bound + sizeof(GHeapMagicHeader)));
|
|
headers->AlignedStart = (UByte*)start;
|
|
headers->AlignedEnd = (UByte*)end;
|
|
headers->Bound = (UByte*)bound;
|
|
headers->Page = 0;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
GHeapRootMH::GHeapRootMH(GSysAlloc* sysAlloc) :
|
|
pSysAlloc(sysAlloc),
|
|
//#ifdef SF_MEMORY_ENABLE_DEBUG_INFO
|
|
// DebugAlloc(&AllocWrapper,
|
|
// 256,
|
|
// Heap_DebugAllocGranularity,
|
|
// Heap_DebugAllocHeaderPageSize),
|
|
//#endif
|
|
RootLock(),
|
|
FreePages(),
|
|
TableCount(0)
|
|
{
|
|
GHeapGlobalEmptyPageMH.pHeap = 0;
|
|
GHeapGlobalEmptyPageMH.Start = 0; // Theoretically there must be a 4K static block,
|
|
// but 0...4K is never available on any system
|
|
|
|
for(int i = 0; i < GPageTableMH::TableSize; ++i)
|
|
{
|
|
GHeapGlobalPageTableMH.Entries[i].FirstPage = &GHeapGlobalEmptyPageMH;
|
|
GHeapGlobalPageTableMH.Entries[i].SizeMask = 0;
|
|
}
|
|
GHeapGlobalRootMH = this;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
GHeapRootMH::~GHeapRootMH()
|
|
{
|
|
FreeTables();
|
|
GHeapGlobalRootMH = 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void GHeapRootMH::FreeTables()
|
|
{
|
|
for(int i = 0; i < GPageTableMH::TableSize; ++i)
|
|
{
|
|
GHeapPageMH* pool = GHeapGlobalPageTableMH.Entries[i].FirstPage;
|
|
if (pool != &GHeapGlobalEmptyPageMH)
|
|
{
|
|
// Took out ASSERT to avoid it happening on shutdown after memory leak report.
|
|
//#ifdef GFC_BUILD_DEBUG
|
|
// UPInt size = GHeapGlobalPageTableMH.Entries[i].SizeMask;
|
|
// GASSERT(size > 1);
|
|
// for (UPInt j = 0; j < size; ++j)
|
|
// {
|
|
// // This assert indicates that the respective PageMH is still use.
|
|
// // Expect memory leaks.
|
|
// //-----------------------
|
|
// //GASSERT(pool[j].pHeap == 0 && pool[j].Start == 0);
|
|
// }
|
|
//#endif
|
|
pSysAlloc->Free(GHeapGlobalPageTableMH.Entries[i].FirstPage,
|
|
(GHeapGlobalPageTableMH.Entries[i].SizeMask+1) * sizeof(GHeapPageMH),
|
|
sizeof(void*));
|
|
}
|
|
GHeapGlobalPageTableMH.Entries[i].FirstPage = &GHeapGlobalEmptyPageMH;
|
|
GHeapGlobalPageTableMH.Entries[i].SizeMask = 0;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool GHeapRootMH::allocPagePool()
|
|
{
|
|
if (TableCount >= GPageTableMH::TableSize)
|
|
{
|
|
// TO DO: Report table overflow
|
|
return false;
|
|
}
|
|
|
|
UPInt tableSize = 128 << (TableCount / 16); // 0...15: 128
|
|
// 16...31: 256
|
|
// 32...48: 512 ...etc
|
|
GHeapPageMH* pool = (GHeapPageMH*)pSysAlloc->Alloc(tableSize * sizeof(GHeapPageMH), sizeof(void*));
|
|
if (pool == 0)
|
|
return false;
|
|
|
|
GHeapGlobalPageTableMH.Entries[TableCount].FirstPage = pool;
|
|
GHeapGlobalPageTableMH.Entries[TableCount].SizeMask = tableSize-1;
|
|
for (UPInt i = 0; i < tableSize; ++i)
|
|
{
|
|
pool->pHeap = 0;
|
|
pool->Start = 0;
|
|
FreePages.PushFront(pool);
|
|
++pool;
|
|
}
|
|
++TableCount;
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
GHeapPageMH* GHeapRootMH::AllocPage(GMemoryHeapMH* heap)
|
|
{
|
|
if (FreePages.IsEmpty())
|
|
if (!allocPagePool())
|
|
return 0;
|
|
|
|
GASSERT(!FreePages.IsEmpty());
|
|
GHeapPageMH* page = FreePages.GetFirst();
|
|
page->Start = (UByte*)pSysAlloc->Alloc(GHeapPageMH::PageSize, sizeof(void*));
|
|
if (page->Start == 0)
|
|
{
|
|
page->pHeap = 0;
|
|
return 0;
|
|
}
|
|
GList<GHeapPageMH>::Remove(page);
|
|
page->pHeap = heap;
|
|
setMagic(page->Start, GHeapMagicHeader::MagicValue);
|
|
return page;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void GHeapRootMH::FreePage(GHeapPageMH* page)
|
|
{
|
|
setMagic(page->Start, 0); // To reduce the number of collisions
|
|
UByte* p = page->Start;
|
|
page->Start = 0;
|
|
page->pHeap = 0;
|
|
pSysAlloc->Free(p, GHeapPageMH::PageSize, sizeof(void*));
|
|
FreePages.PushFront(page);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
UInt32 GHeapRootMH::GetPageIndex(const GHeapPageMH* page) const
|
|
{
|
|
for (unsigned i = 0; i < TableCount; ++i)
|
|
{
|
|
const GPageTableMH::Level0Entry& l1Table = GHeapGlobalPageTableMH.Entries[i];
|
|
GASSERT(l1Table.FirstPage != &GHeapGlobalEmptyPageMH);
|
|
UPInt idx1 = page - l1Table.FirstPage;
|
|
if (idx1 <= UPInt(l1Table.SizeMask))
|
|
{
|
|
return UInt32((idx1 << GPageTableMH::Index1Shift) | i);
|
|
}
|
|
}
|
|
return UInt32(~0U);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void GHeapRootMH::setMagic(UByte* pageStart, unsigned magicValue)
|
|
{
|
|
GHeapMagicHeadersInfo headers;
|
|
GHeapGetMagicHeaders(UPInt(pageStart), &headers);
|
|
if (headers.Header1) headers.Header1->Magic = (UInt16)magicValue;
|
|
if (headers.Header2) headers.Header2->Magic = (UInt16)magicValue;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
GHeapPageMH* GHeapRootMH::ResolveAddress(UPInt addr) const
|
|
{
|
|
const GHeapMagicHeader* header;
|
|
|
|
header = (const GHeapMagicHeader*)(addr & ~UPInt(GHeapPageMH::PageMask));
|
|
if (header->Magic == GHeapMagicHeader::MagicValue)
|
|
{
|
|
const GPageTableMH::Level0Entry& l0e = GHeapGlobalPageTableMH.Entries[header->GetIndex0()];
|
|
GHeapPageMH* page = l0e.FirstPage + (header->GetIndex1() & l0e.SizeMask);
|
|
if ((addr - UPInt(page->Start)) < GHeapPageMH::PageSize)
|
|
{
|
|
return page;
|
|
}
|
|
}
|
|
|
|
// No match on left
|
|
header = (const GHeapMagicHeader*)((addr & ~UPInt(GHeapPageMH::PageMask)) + GHeapPageMH::PageSize - sizeof(GHeapMagicHeader));
|
|
if (header->Magic == GHeapMagicHeader::MagicValue)
|
|
{
|
|
const GPageTableMH::Level0Entry& l0e = GHeapGlobalPageTableMH.Entries[header->GetIndex0()];
|
|
GHeapPageMH* page = l0e.FirstPage + (header->GetIndex1() & l0e.SizeMask);
|
|
if ((addr - UPInt(page->Start)) < GHeapPageMH::PageSize)
|
|
{
|
|
return page;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
GMemoryHeapMH* GHeapRootMH::CreateHeap(const char* name,
|
|
GMemoryHeapMH* parent,
|
|
const GMemoryHeap::HeapDesc& desc)
|
|
{
|
|
// Locked by Root::Lock in MemoryHeap::CreateHeap
|
|
if (GetSysAlloc() == 0)
|
|
{
|
|
GFC_DEBUG_MESSAGE(1, "CreateHeap: Memory System is not initialized");
|
|
GASSERT(0);
|
|
}
|
|
|
|
#ifdef GHEAP_DEBUG_INFO
|
|
UPInt debugStorageSize = sizeof(GHeapDebugStorageMH);
|
|
#else
|
|
UPInt debugStorageSize = 0;
|
|
#endif
|
|
|
|
UPInt selfSize = sizeof(GMemoryHeapMH) +
|
|
sizeof(GHeapAllocEngineMH) +
|
|
debugStorageSize +
|
|
strlen(name) + 1;
|
|
|
|
selfSize = (selfSize + GHeapPageMH::UnitMask) & ~UPInt(GHeapPageMH::UnitMask);
|
|
|
|
UByte* heapBuf = (UByte*)pSysAlloc->Alloc(selfSize, sizeof(void*));
|
|
if (heapBuf == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
GMemoryHeapMH* heap = ::new(heapBuf) GMemoryHeapMH;
|
|
|
|
GHeapAllocEngineMH* engine = ::new (heapBuf + sizeof(GMemoryHeapMH))
|
|
GHeapAllocEngineMH(pSysAlloc,
|
|
heap,
|
|
desc.MinAlign,
|
|
desc.Limit);
|
|
|
|
if (!engine->IsValid())
|
|
{
|
|
pSysAlloc->Free(heapBuf, selfSize, sizeof(void*));
|
|
return 0;
|
|
}
|
|
|
|
heap->SelfSize = selfSize;
|
|
heap->RefCount = 1;
|
|
heap->pAutoRelease = 0;
|
|
heap->Info.Desc = desc;
|
|
heap->Info.pParent = parent;
|
|
heap->Info.pName = (char*)heapBuf +
|
|
sizeof(GMemoryHeapMH) +
|
|
sizeof(GHeapAllocEngineMH) +
|
|
debugStorageSize;
|
|
heap->UseLocks = (desc.Flags & GMemoryHeap::Heap_ThreadUnsafe) == 0;
|
|
heap->TrackDebugInfo = (desc.Flags & GMemoryHeap::Heap_NoDebugInfo) == 0;
|
|
heap->pEngine = engine;
|
|
|
|
#ifdef GHEAP_DEBUG_INFO
|
|
heap->pDebugStorage = ::new(heapBuf + sizeof(GMemoryHeapMH) + sizeof(GHeapAllocEngineMH))
|
|
GHeapDebugStorageMH(GHeapGlobalRootMH->GetSysAlloc(), GHeapGlobalRootMH->GetLock());
|
|
#endif
|
|
memcpy(heap->Info.pName, name, strlen(name) + 1);
|
|
|
|
#ifdef GHEAP_TRACE_ALL
|
|
pTracer->OnCreateHeap(heap);
|
|
#endif
|
|
|
|
return heap;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void GHeapRootMH::DestroyHeap(GMemoryHeapMH* heap)
|
|
{
|
|
// Locked by Root::Lock in MemoryHeap::Release
|
|
#ifdef GHEAP_TRACE_ALL
|
|
pTracer->OnDestroyHeap(heap);
|
|
#endif
|
|
|
|
UPInt heapSize = heap->SelfSize;
|
|
#ifdef GHEAP_DEBUG_INFO
|
|
heap->pDebugStorage->FreeAll();
|
|
#endif
|
|
heap->pEngine->FreeAll();
|
|
heap->~GMemoryHeapMH();
|
|
pSysAlloc->Free(heap, heapSize, sizeof(void*));
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
#ifdef GHEAP_TRACE_ALL
|
|
void GHeapRootMH::OnAlloc(const GMemoryHeapMH* heap, UPInt size, UPInt align, unsigned sid, const void* ptr)
|
|
{
|
|
GLockSafe::Locker lock(&RootLock); // RAGE - bug fix? changed GLock to GLockSafe
|
|
pTracer->OnAlloc(heap, size, align, sid, ptr);
|
|
}
|
|
|
|
void GHeapRootMH::OnRealloc(const GMemoryHeapMH* heap, const void* oldPtr, UPInt newSize, const void* newPtr)
|
|
{
|
|
GLockSafe::Locker lock(&RootLock); // RAGE - bug fix? changed GLock to GLockSafe
|
|
pTracer->OnRealloc(heap, oldPtr, newSize, newPtr);
|
|
}
|
|
|
|
void GHeapRootMH::OnFree(const GMemoryHeapMH* heap, const void* ptr)
|
|
{
|
|
GLockSafe::Locker lock(&RootLock); // RAGE - bug fix? changed GLock to GLockSafe
|
|
pTracer->OnFree(heap, ptr);
|
|
}
|
|
#endif
|
|
|
|
|
|
|