mirror of
https://github.com/NixOS/nix.git
synced 2025-11-15 23:12:44 +01:00
Checkpoint
This commit is contained in:
parent
4237414f4d
commit
7c716b4c49
27 changed files with 596 additions and 440 deletions
|
|
@ -18,6 +18,15 @@ GC::GC()
|
|||
|
||||
backSentinel->prev = frontSentinel;
|
||||
backSentinel->next = nullptr;
|
||||
|
||||
frontRootSentinel = (Root<Object> *) malloc(sizeof(Root<Object>));
|
||||
backRootSentinel = (Root<Object> *) malloc(sizeof(Root<Object>));
|
||||
|
||||
frontRootSentinel->prev = nullptr;
|
||||
frontRootSentinel->next = backRootSentinel;
|
||||
|
||||
backRootSentinel->prev = frontRootSentinel;
|
||||
backRootSentinel->next = nullptr;
|
||||
}
|
||||
|
||||
GC::~GC()
|
||||
|
|
@ -26,9 +35,18 @@ GC::~GC()
|
|||
for (Ptr<Object> * p = frontSentinel->next; p != backSentinel; p = p->next)
|
||||
n++;
|
||||
if (n)
|
||||
warn("%d GC roots still exist on exit", n);
|
||||
warn("%d GC root pointers still exist on exit", n);
|
||||
|
||||
n = 0;
|
||||
for (Root<Object> * p = frontRootSentinel->next; p != backRootSentinel; p = p->next)
|
||||
n++;
|
||||
if (n)
|
||||
warn("%d GC root objects still exist on exit", n);
|
||||
|
||||
assert(!frontSentinel->prev);
|
||||
assert(!backSentinel->next);
|
||||
assert(!frontRootSentinel->prev);
|
||||
assert(!backRootSentinel->next);
|
||||
}
|
||||
|
||||
void GC::gc()
|
||||
|
|
@ -37,112 +55,120 @@ void GC::gc()
|
|||
|
||||
std::stack<Object *> stack;
|
||||
|
||||
for (Ptr<Object> * p = frontSentinel->next; p != backSentinel; p = p->next) {
|
||||
if (!p->value) continue;
|
||||
// FIXME: ensure this gets inlined.
|
||||
auto push = [&](Object * p) { if (p) { assertObject(p); stack.push(p); } };
|
||||
|
||||
stack.push(p->value);
|
||||
auto pushPointers = [&](Object * obj) {
|
||||
switch (obj->type) {
|
||||
|
||||
case tFree:
|
||||
printError("reached a freed object at %x", obj);
|
||||
abort();
|
||||
|
||||
case tBindings: {
|
||||
auto obj2 = (Bindings *) obj;
|
||||
for (auto i = obj2->attrs; i < obj2->attrs + obj2->size_; ++i)
|
||||
push(i->value);
|
||||
break;
|
||||
}
|
||||
|
||||
case tValueList: {
|
||||
auto obj2 = (PtrList<Object> *) obj;
|
||||
for (auto i = obj2->elems; i < obj2->elems + obj2->size(); ++i)
|
||||
push(*i);
|
||||
break;
|
||||
}
|
||||
|
||||
case tEnv: {
|
||||
auto obj2 = (Env *) obj;
|
||||
push(obj2->up);
|
||||
if (obj2->type != Env::HasWithExpr)
|
||||
for (auto i = obj2->values; i < obj2->values + obj2->size; ++i)
|
||||
push(*i);
|
||||
break;
|
||||
}
|
||||
|
||||
case tInt:
|
||||
case tBool:
|
||||
case tNull:
|
||||
case tList0:
|
||||
case tFloat:
|
||||
break;
|
||||
|
||||
case tString:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case tPath:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case tAttrs:
|
||||
push(((Value *) obj)->attrs);
|
||||
break;
|
||||
|
||||
case tList1:
|
||||
push(((Value *) obj)->smallList[0]);
|
||||
break;
|
||||
|
||||
case tList2:
|
||||
push(((Value *) obj)->smallList[0]);
|
||||
push(((Value *) obj)->smallList[1]);
|
||||
break;
|
||||
|
||||
case tListN:
|
||||
push(((Value *) obj)->bigList);
|
||||
break;
|
||||
|
||||
case tThunk:
|
||||
case tBlackhole:
|
||||
push(((Value *) obj)->thunk.env);
|
||||
break;
|
||||
|
||||
case tApp:
|
||||
case tPrimOpApp:
|
||||
push(((Value *) obj)->app.left);
|
||||
push(((Value *) obj)->app.right);
|
||||
break;
|
||||
|
||||
case tLambda:
|
||||
push(((Value *) obj)->lambda.env);
|
||||
break;
|
||||
|
||||
case tPrimOp:
|
||||
// FIXME: GC primops?
|
||||
break;
|
||||
|
||||
default:
|
||||
printError("don't know how to traverse object at %x (tag %d)", obj, obj->type);
|
||||
abort();
|
||||
}
|
||||
};
|
||||
|
||||
auto processStack = [&]() {
|
||||
while (!stack.empty()) {
|
||||
auto obj = stack.top();
|
||||
stack.pop();
|
||||
|
||||
// FIXME: ensure this gets inlined.
|
||||
auto push = [&](Object * p) { if (p) { /* FIXME */ assert(isObject(p)); stack.push(p); } };
|
||||
|
||||
//printError("MARK %x", obj);
|
||||
|
||||
if (!obj->isMarked()) {
|
||||
marked++;
|
||||
obj->mark();
|
||||
switch (obj->type) {
|
||||
|
||||
case tFree:
|
||||
printError("reached a freed object at %x", obj);
|
||||
abort();
|
||||
|
||||
case tBindings: {
|
||||
auto obj2 = (Bindings *) obj;
|
||||
for (auto i = obj2->attrs; i < obj2->attrs + obj2->size_; ++i)
|
||||
push(i->value);
|
||||
break;
|
||||
}
|
||||
|
||||
case tValueList: {
|
||||
auto obj2 = (PtrList<Object> *) obj;
|
||||
for (auto i = obj2->elems; i < obj2->elems + obj2->size(); ++i)
|
||||
push(*i);
|
||||
break;
|
||||
}
|
||||
|
||||
case tEnv: {
|
||||
auto obj2 = (Env *) obj;
|
||||
push(obj2->up);
|
||||
if (obj2->type != Env::HasWithExpr)
|
||||
for (auto i = obj2->values; i < obj2->values + obj2->size; ++i)
|
||||
push(*i);
|
||||
break;
|
||||
}
|
||||
|
||||
case tInt:
|
||||
case tBool:
|
||||
case tNull:
|
||||
case tList0:
|
||||
case tFloat:
|
||||
break;
|
||||
|
||||
case tString:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case tPath:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case tAttrs:
|
||||
push(((Value *) obj)->attrs);
|
||||
break;
|
||||
|
||||
case tList1:
|
||||
push(((Value *) obj)->smallList[0]);
|
||||
break;
|
||||
|
||||
case tList2:
|
||||
push(((Value *) obj)->smallList[0]);
|
||||
push(((Value *) obj)->smallList[1]);
|
||||
break;
|
||||
|
||||
case tListN:
|
||||
push(((Value *) obj)->bigList);
|
||||
break;
|
||||
|
||||
case tThunk:
|
||||
push(((Value *) obj)->thunk.env);
|
||||
break;
|
||||
|
||||
case tApp:
|
||||
case tPrimOpApp:
|
||||
push(((Value *) obj)->app.left);
|
||||
push(((Value *) obj)->app.right);
|
||||
break;
|
||||
|
||||
case tLambda:
|
||||
push(((Value *) obj)->lambda.env);
|
||||
break;
|
||||
|
||||
case tBlackhole:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case tPrimOp:
|
||||
// FIXME: GC primops?
|
||||
break;
|
||||
|
||||
default:
|
||||
printError("don't know how to traverse object at %x (tag %d)", obj, obj->type);
|
||||
abort();
|
||||
}
|
||||
pushPointers(obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (Root<Object> * p = frontRootSentinel->next; p != backRootSentinel; p = p->next) {
|
||||
pushPointers(&p->value);
|
||||
processStack();
|
||||
}
|
||||
|
||||
for (Ptr<Object> * p = frontSentinel->next; p != backSentinel; p = p->next) {
|
||||
if (!p->value) continue;
|
||||
stack.push(p->value);
|
||||
processStack();
|
||||
}
|
||||
|
||||
Size totalObjectsFreed = 0;
|
||||
|
|
@ -162,7 +188,7 @@ void GC::gc()
|
|||
});
|
||||
}
|
||||
|
||||
printError("freed %d bytes in %d dead objects, keeping %d objects",
|
||||
debug("freed %d bytes in %d dead objects, keeping %d objects",
|
||||
totalWordsFreed * WORD_SIZE, totalObjectsFreed, marked);
|
||||
}
|
||||
|
||||
|
|
@ -224,6 +250,7 @@ std::pair<Size, Size> GC::Arena::freeUnmarked()
|
|||
};
|
||||
|
||||
if (tag == tFree) {
|
||||
//debug("FREE %x %d", obj, obj->getMisc());
|
||||
if (curFree) {
|
||||
// Merge this object into the previous free
|
||||
// object.
|
||||
|
|
@ -239,10 +266,13 @@ std::pair<Size, Size> GC::Arena::freeUnmarked()
|
|||
|
||||
if (obj->isMarked()) {
|
||||
// Unmark to prepare for the next GC run.
|
||||
//debug("KEEP OBJECT %x %d %d", obj, obj->type, objSize);
|
||||
curFree = nullptr;
|
||||
obj->unmark();
|
||||
} else {
|
||||
//printError("FREE %x %d %d", obj, obj->type, objSize);
|
||||
//debug("FREE OBJECT %x %d %d", obj, obj->type, objSize);
|
||||
for (Size i = 0; i < objSize; ++i)
|
||||
((Word *) obj)[i] = 0xdeadc0dedeadbeefULL;
|
||||
objectsFreed += 1;
|
||||
wordsFreed += objSize;
|
||||
if (curFree) {
|
||||
|
|
@ -279,9 +309,18 @@ bool GC::isObject(void * p)
|
|||
return false;
|
||||
}
|
||||
|
||||
GC::ArenaList::ArenaList()
|
||||
: nextSize(1024)
|
||||
void GC::assertObject(void * p)
|
||||
{
|
||||
if (!isObject(p)) {
|
||||
printError("object %p is not an object", p);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
GC::ArenaList::ArenaList()
|
||||
{
|
||||
static Size initialHeapSize = std::stol(getEnv("GC_INITIAL_HEAP_SIZE", "1000000")) / WORD_SIZE;
|
||||
nextSize = initialHeapSize;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue