mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-16 06:05:58 -06:00
249 lines
4.7 KiB
C++
249 lines
4.7 KiB
C++
#include "Core.h"
|
|
|
|
NAMESPACE_UPP
|
|
|
|
#ifdef UPP_HEAP
|
|
|
|
#include "HeapImp.h"
|
|
|
|
#define LLOG(x) // LOG((void *)this << ' ' << x)
|
|
|
|
Heap::DLink Heap::big[1];
|
|
Heap Heap::aux;
|
|
StaticMutex Heap::mutex;
|
|
|
|
void Heap::Init()
|
|
{
|
|
if(initialized)
|
|
return;
|
|
LLOG("Init heap " << (void *)this);
|
|
for(int i = 0; i < NKLASS; i++) {
|
|
empty[i] = NULL;
|
|
full[i]->LinkSelf();
|
|
work[i]->LinkSelf();
|
|
work[i]->freelist = NULL;
|
|
work[i]->klass = i;
|
|
cachen[i] = 3500 / Ksz(i);
|
|
}
|
|
ASSERT(sizeof(Header) == 16);
|
|
ASSERT(sizeof(DLink) <= 16);
|
|
ASSERT(sizeof(BigHdr) + sizeof(Header) < BIGHDRSZ);
|
|
GlobalLInit();
|
|
for(int i = 0; i < LBINS; i++)
|
|
freebin[i]->LinkSelf();
|
|
large->LinkSelf();
|
|
lcount = 0;
|
|
if(this != &aux && !aux.work[0]->next) {
|
|
Mutex::Lock __(mutex);
|
|
aux.Init();
|
|
}
|
|
initialized = true;
|
|
PROFILEMT(mutex);
|
|
}
|
|
|
|
void Heap::RemoteFree(void *ptr)
|
|
{
|
|
LLOG("RemoteFree " << ptr);
|
|
Mutex::Lock __(mutex); // TODO: Go lockless!
|
|
FreeLink *f = (FreeLink *)ptr;
|
|
f->next = remote_free;
|
|
remote_free = f;
|
|
}
|
|
|
|
void Heap::FreeRemoteRaw()
|
|
{
|
|
while(remote_free) {
|
|
FreeLink *f = remote_free;
|
|
remote_free = remote_free->next;
|
|
LLOG("FreeRemote " << (void *)f);
|
|
FreeDirect(f);
|
|
}
|
|
}
|
|
|
|
void Heap::FreeRemote()
|
|
{
|
|
LLOG("FreeRemote");
|
|
Mutex::Lock __(mutex); // TODO: Go lockless!
|
|
FreeRemoteRaw();
|
|
}
|
|
|
|
void Heap::Shutdown()
|
|
{
|
|
LLOG("Shutdown");
|
|
Mutex::Lock __(mutex);
|
|
Init();
|
|
FreeRemoteRaw();
|
|
for(int i = 0; i < NKLASS; i++) {
|
|
LLOG("Free cache " << i);
|
|
FreeLink *l = cache[i];
|
|
while(l) {
|
|
FreeLink *h = l;
|
|
l = l->next;
|
|
FreeDirect(h);
|
|
}
|
|
while(full[i]->next != full[i]) {
|
|
Page *p = full[i]->next;
|
|
p->Unlink();
|
|
p->heap = &aux;
|
|
p->Link(aux.full[i]);
|
|
LLOG("Orphan full " << (void *)p);
|
|
}
|
|
while(work[i]->next != work[i]) {
|
|
Page *p = work[i]->next;
|
|
p->Unlink();
|
|
p->heap = &aux;
|
|
p->Link(p->freelist ? aux.work[i] : aux.full[i]);
|
|
LLOG("Orphan work " << (void *)p);
|
|
}
|
|
if(empty[i]) {
|
|
ASSERT(empty[i]->freelist);
|
|
ASSERT(empty[i]->active == 0);
|
|
empty[i]->heap = &aux;
|
|
empty[i]->next = aux.empty[i];
|
|
aux.empty[i] = empty[i];
|
|
LLOG("Orphan empty " << (void *)empty[i]);
|
|
}
|
|
}
|
|
while(large != large->next) {
|
|
Header *bh = (Header *)((byte *)large->next + LARGEHDRSZ);
|
|
LLOG("Orphan large block " << (void *)large->next << " size: " << bh->size);
|
|
if(bh->size == MAXBLOCK && bh->free)
|
|
MoveToEmpty(large->next, bh);
|
|
else
|
|
MoveLarge(&aux, large->next);
|
|
}
|
|
memset(this, 0, sizeof(Heap));
|
|
}
|
|
|
|
void Heap::Assert(bool b)
|
|
{
|
|
if(!b)
|
|
Panic("Heap is corrupted!");
|
|
}
|
|
|
|
void Heap::DblCheck(Page *p)
|
|
{
|
|
Page *l = p;
|
|
do {
|
|
Assert(l->next->prev == l && l->prev->next == l);
|
|
l = l->next;
|
|
}
|
|
while(p != l);
|
|
}
|
|
|
|
int Heap::CheckPageFree(FreeLink *l, int k)
|
|
{
|
|
int n = 0;
|
|
while(l) {
|
|
DbgFreeCheckK(l, k);
|
|
l = l->next;
|
|
n++;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
void Heap::Check() {
|
|
Mutex::Lock __(mutex);
|
|
Init();
|
|
if(!work[0]->next)
|
|
Init();
|
|
for(int i = 0; i < NKLASS; i++) {
|
|
DblCheck(work[i]);
|
|
DblCheck(full[i]);
|
|
Page *p = work[i]->next;
|
|
while(p != work[i]) {
|
|
Assert(p->heap == this);
|
|
Assert(CheckPageFree(p->freelist, p->klass) == p->Count() - p->active);
|
|
p = p->next;
|
|
}
|
|
p = full[i]->next;
|
|
while(p != full[i]) {
|
|
Assert(p->heap == this);
|
|
Assert(p->klass == i);
|
|
Assert(p->active == p->Count());
|
|
p = p->next;
|
|
}
|
|
p = empty[i];
|
|
if(p) {
|
|
for(;;) {
|
|
Assert(p->heap == this);
|
|
Assert(p->active == 0);
|
|
Assert(p->klass == i);
|
|
Assert(CheckPageFree(p->freelist, i) == p->Count());
|
|
if(this != &aux)
|
|
break;
|
|
p = p->next;
|
|
if(!p)
|
|
break;
|
|
}
|
|
}
|
|
FreeLink *l = cache[i];
|
|
while(l) {
|
|
DbgFreeCheckK(l, i);
|
|
l = l->next;
|
|
}
|
|
}
|
|
DLink *l = large->next;
|
|
while(l != large) {
|
|
Header *bh = (Header *)((byte *)l + LARGEHDRSZ);
|
|
while(bh->size) {
|
|
Assert((byte *)bh >= (byte *)l + LARGEHDRSZ && (byte *)bh < (byte *)l + 65536);
|
|
if(bh->free)
|
|
DbgFreeCheck(bh->GetBlock() + 1, bh->size - sizeof(DLink));
|
|
bh = bh->Next();
|
|
}
|
|
l = l->next;
|
|
}
|
|
if(this != &aux)
|
|
aux.Check();
|
|
}
|
|
|
|
void Heap::AssertLeaks(bool b)
|
|
{
|
|
if(!b)
|
|
Panic("Memory leaks detected! (final check)");
|
|
}
|
|
|
|
void Heap::AuxFinalCheck()
|
|
{
|
|
Mutex::Lock __(mutex);
|
|
aux.Init();
|
|
aux.FreeRemoteRaw();
|
|
aux.Check();
|
|
if(!aux.work[0]->next)
|
|
aux.Init();
|
|
for(int i = 0; i < NKLASS; i++) {
|
|
Assert(!aux.cache[i]);
|
|
DblCheck(aux.work[i]);
|
|
DblCheck(aux.full[i]);
|
|
AssertLeaks(aux.work[i] == aux.work[i]->next);
|
|
AssertLeaks(aux.full[i] == aux.full[i]->next);
|
|
Page *p = aux.empty[i];
|
|
if(p) {
|
|
for(;;) {
|
|
Assert(p->heap == &aux);
|
|
Assert(p->active == 0);
|
|
Assert(CheckPageFree(p->freelist, p->klass) == p->Count());
|
|
p = p->next;
|
|
if(!p)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
AssertLeaks(aux.large == aux.large->next);
|
|
AssertLeaks(big == big->next);
|
|
}
|
|
|
|
void MemoryFreeThread()
|
|
{
|
|
heap.Shutdown();
|
|
}
|
|
|
|
void MemoryCheck()
|
|
{
|
|
heap.Check();
|
|
}
|
|
|
|
#endif
|
|
|
|
END_UPP_NAMESPACE
|