164 lines
3.8 KiB
D
164 lines
3.8 KiB
D
module clox.gc;
|
|
|
|
import core.stdc.stdio : printf;
|
|
import std.stdio : writeln;
|
|
import std.typecons : Tuple, tuple;
|
|
|
|
import clox.compiler;
|
|
import clox.memory;
|
|
import clox.obj;
|
|
import clox.util;
|
|
import clox.value;
|
|
import clox.vm;
|
|
import clox.container.dynarray;
|
|
import clox.container.table;
|
|
import conf = clox.conf;
|
|
|
|
void collectGarbage(){
|
|
debug(disableGC)
|
|
return;
|
|
debug(stressGC) {} else {
|
|
debug(logGC)
|
|
writeln("bytesAllocated ", vm.bytesAllocated, ", nextGC ", vm.nextGC);
|
|
if(vm.bytesAllocated < vm.nextGC)
|
|
return;
|
|
}
|
|
debug(logGC)
|
|
printf(colour!(" -- GC begin\n", Colour.Yellow).ptr);
|
|
debug {
|
|
static bool isAlreadyRunning = false;
|
|
assert(!isAlreadyRunning);
|
|
isAlreadyRunning = true;
|
|
scope(exit)
|
|
isAlreadyRunning = false;
|
|
}
|
|
markRoots();
|
|
traceReferences();
|
|
vm.strings.removeWhite();
|
|
debug(memTrace){
|
|
import clox.memorydbg : totalGCScanned, totalGCollected, totalGCollections;
|
|
auto sweepStats = sweep();
|
|
totalGCollections++;
|
|
totalGCollected += sweepStats.collected;
|
|
totalGCScanned += sweepStats.total;
|
|
debug(logGC)
|
|
printf(colour!(" -- GC end %zu/%zu\n", Colour.Yellow).ptr, sweepStats.collected, sweepStats.total);
|
|
} else {
|
|
sweep();
|
|
}
|
|
debug(stressGC) {} else {
|
|
vm.nextGC = conf.nextGCGrowthFunc(vm.bytesAllocated);
|
|
}
|
|
}
|
|
void mark(O)(O* o) if(is(O == Obj) || __traits(getMember, O, "obj").offsetof == 0){
|
|
Obj* object = cast(Obj*)o;
|
|
if(object is null || object.isMarked)
|
|
return;
|
|
debug(logGC)
|
|
writeln("\t", object.prettyPtr, colour!(" (-) ", "200", "200", "240"), Obj.toString(object));
|
|
object.isMarked = true;
|
|
vm.greyStack ~= object;
|
|
}
|
|
void mark(Value value){
|
|
if(value.isType(Value.Type.Obj))
|
|
mark(value.asObj);
|
|
}
|
|
void mark(K, V)(ref Table!(K, V) table){
|
|
foreach(ref entry; table.pool){
|
|
mark(entry.key);
|
|
mark(entry.val);
|
|
}
|
|
}
|
|
void mark(T)(ref DynArray!T darray){
|
|
foreach(ref item; darray[]){
|
|
mark(item);
|
|
}
|
|
}
|
|
private void markRoots(){
|
|
for(Value* slot = vm.stack.ptr; slot < vm.stackTop; slot++){
|
|
mark(*slot);
|
|
}
|
|
foreach(frame; vm.frames[0 .. vm.frameCount]){
|
|
mark(frame.closure);
|
|
}
|
|
for(Obj.Upvalue* upvalue = vm.openUpvalues; upvalue !is null; upvalue = upvalue.next){
|
|
mark(upvalue);
|
|
}
|
|
mark(vm.globals);
|
|
mark(vm.initString);
|
|
markCompilerRoots();
|
|
}
|
|
private void blacken(Obj* object){
|
|
debug(logGC)
|
|
writeln("\t", object.prettyPtr, colour!(" (#) ", Colour.Black), Obj.toString(object));
|
|
final switch(object.type){
|
|
case Obj.Type.NativeFunction:
|
|
case Obj.Type.String:
|
|
break;
|
|
case Obj.Type.Upvalue:
|
|
mark(object.asUpvalue.closed);
|
|
break;
|
|
case Obj.Type.Function:
|
|
Obj.Function* func = object.asFunc;
|
|
mark(func.name);
|
|
mark(func.chunk.constants);
|
|
break;
|
|
case Obj.Type.Closure:
|
|
Obj.Closure* closure = object.asClosure;
|
|
mark(closure.func);
|
|
foreach(upvalue; closure.upvalues)
|
|
mark(upvalue);
|
|
break;
|
|
case Obj.Type.Class:
|
|
Obj.Class* cls = object.asClass;
|
|
mark(cls.methods);
|
|
mark(cls.name);
|
|
break;
|
|
case Obj.Type.Instance:
|
|
Obj.Instance* ins = object.asInstance;
|
|
mark(ins.cls);
|
|
mark(ins.fields);
|
|
break;
|
|
case Obj.Type.BoundMethod:
|
|
Obj.BoundMethod* bm = object.asBoundMethod;
|
|
mark(bm.receiver);
|
|
mark(bm.method);
|
|
break;
|
|
case Obj.Type.None: assert(0);
|
|
}
|
|
}
|
|
private void traceReferences(){
|
|
while(vm.greyStack.count > 0){
|
|
Obj* object = vm.greyStack.pop();
|
|
blacken(object);
|
|
}
|
|
}
|
|
private auto sweep(){
|
|
debug(memTrace)
|
|
size_t nCollected, total;
|
|
Obj* previous = null;
|
|
Obj* object = vm.objects;
|
|
while(object !is null){
|
|
debug(memTrace)
|
|
total++;
|
|
if(object.isMarked){
|
|
object.isMarked = false;
|
|
previous = object;
|
|
object = object.next;
|
|
} else {
|
|
Obj* unreached = object;
|
|
object = object.next;
|
|
if(previous !is null){
|
|
previous.next = object;
|
|
} else {
|
|
vm.objects = object;
|
|
}
|
|
freeObject(unreached);
|
|
debug(memTrace)
|
|
nCollected++;
|
|
}
|
|
}
|
|
debug(memTrace)
|
|
return tuple!("collected", "total")(nCollected, total);
|
|
}
|
|
|