Skip to content

Commit

Permalink
[gc] Fix repeated calls to user scanning
Browse files Browse the repository at this point in the history
  • Loading branch information
titzer committed Nov 12, 2024
1 parent 02e37a9 commit 3830aee
Show file tree
Hide file tree
Showing 13 changed files with 240 additions and 12 deletions.
26 changes: 15 additions & 11 deletions rt/gc/RiGc.v3
Original file line number Diff line number Diff line change
Expand Up @@ -283,29 +283,33 @@ component RiGc {
// live objects.
def runScanners(reloc: Pointer -> Pointer) {
var l = scanners;
var prev: RiGcScannerLink;
scanners = null;
while (l != null) {
var next = l.next;
var np = l.pointer = reloc(l.pointer);
if (np != Pointer.NULL) {
// object is live, scan and move to survivors for this cycle
l.scan();
var old_ptr = l.pointer;
if (debug) D.puts("runScanner @ ").putp(old_ptr).ln();
var new_ptr = reloc(l.pointer);
if (new_ptr != Pointer.NULL) {
// Object is live, scan and move to surviving for this cycle.
l.pointer = new_ptr;
l.next = survivingScanners;
survivingScanners = l;
if (debug) D.puts(" runScanner [live] @ ").putp(old_ptr).puts(" -> ").putp(new_ptr).ln();
l.scan();
} else {
// object is not (yet) live, keep in scanners list
l.next = null;
if (prev != null) prev.next = l;
else scanners = l;
prev = l;
// Object is not (yet) live, put back in scanners list.
l.next = scanners;
scanners = l;
}
l = next;
}
}
// Called by the GC after finishing all scanning. Dump the old scanners list (with
// Called by the GC after finishing all scanning. Drop the old scanners list (with
// nonsurviving objects) and retain the surviving scanners.
def finishScanners() {
if (debug) {
for (l = scanners; l != null; l = l.next) D.puts("dropScanner @ ").putp(l.pointer).ln();
}
scanners = survivingScanners; // overwrite old scanners list with survivors only
survivingScanners = null;
}
Expand Down
File renamed without changes.
File renamed without changes.
1 change: 0 additions & 1 deletion test/rt/runtime_code.v3.expect

This file was deleted.

8 changes: 8 additions & 0 deletions test/rt/runtime_code.v3.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
##+code size check
##-ok
##+generating code
##-ok
##+checking code
##-ok
##+running code
##-ok
91 changes: 91 additions & 0 deletions test/rt/user_scan0.v3
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//@execute 0=42

var globalScans = 0;

// An object that has a hidden pointer to itself.
class C(data: int) {
var pointer: Pointer;
var scans = 0;

new() {
pointer = Pointer.atObject(this); // hidden raw pointer to self
RiGc.registerScanner(this, C.scan); // call scan() upon GC
}
def scan() {
RiGc.scanRoot(Pointer.atField(this.pointer)); // scan the root
if (pointer != Pointer.atObject(this)) { var x = 1/0; } // check the updated pointer
scans++; // count the number of scans
globalScans++;
}
}

def test1(v: int) -> int {
globalScans = 0;
// Allocate some objects that have custom scan routines.
def GC_COUNT = 5;
var objs = [C.new(v - 11), C.new(53 - v)];

// Run some GCs
for (i < GC_COUNT) {
RiGc.forceGC();
}
// Check that all the scans were called.
if (globalScans != GC_COUNT * objs.length) {
return -21;
}
var sum = 0;
for (o in objs) {
if (o.scans != GC_COUNT) return -20;
sum += o.data;
}
// Return the sum of the data fields.
return sum;
}

def test2(v: int) -> int {
globalScans = 0;
// Allocate some objects that have custom scan routines.
def GC_COUNT = 5;
var objs = [C.new(-55), C.new(42)];

// Run some GCs
for (i < GC_COUNT) {
RiGc.forceGC();
}
// Null-out the first entry and run some more.
objs[0] = null;
for (i < GC_COUNT) {
RiGc.forceGC();
}
// Check that all the scans were called.
if (globalScans != GC_COUNT * 3) {
return -22;
}
// Check that all the scans were called.
var sum = 0;
for (o in objs) {
if (o == null) continue;
if (o.scans != 2 * GC_COUNT) return -24;
sum += o.data;
}
// Return the sum of the data fields.
return sum;
}

def main() -> int {
var tests = [test1, test2];
for (i < tests.length) {
System.puts("##+test");
System.puti(i + 1);
System.ln();
var r = tests[i](99 + i);
if (r == 42) {
System.puts("##-ok\n");
} else {
System.puts("##-fail: ");
System.puti(r);
System.ln();
}
}
return 42;
}
4 changes: 4 additions & 0 deletions test/rt/user_scan0.v3.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
##+test1
##-ok
##+test2
##-ok
118 changes: 118 additions & 0 deletions test/rt/user_scan1.v3
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//@execute 0=42

def debug = false; // prints out scanning activity; disable if ordering becomes a problem
var globalScans = 0;

// An object that has a hidden pointer to itself.
class C(name: string, data: int) {
var pointer: Pointer;
var scans = 0;

new() {
RiGc.registerScanner(this, C.scan); // call scan() upon GC
}
def scan() {
if (debug) {
System.puts("scan: ");
System.puts(name);
System.ln();
}
RiGc.scanRoot(Pointer.atField(this.pointer)); // scan the root
scans++; // count the number of scans
globalScans++;
}
def update(that: C) -> this {
this.pointer = Pointer.atObject(that); // hidden raw pointer to {that}
}
def get() -> C {
return Pointer.atField(this.pointer).load<C>();
}
def countScans() -> int {
return scans;
}
}

def alloc2(v: int) -> C {
// Allocate mutually-recursive hidden pointers, but only return one object.
var a = C.new("a", -22 + v), b = C.new("b", 64 - v);
a.update(b);
b.update(a);
return a;
}

def test1(v: int) -> int {
globalScans = 0;
// Allocate some objects that have custom scan routines.
def GC_COUNT = 5;
var a = alloc2(v);

// Run some GCs
for (i < GC_COUNT) RiGc.forceGC();

// Check that all the scans were called.
if (globalScans != 2 * GC_COUNT) return 1000000 + globalScans;
var sum = 0;
var x = a;
for (i < 20) {
if (x.scans != GC_COUNT) return -20;
sum += x.data;
var n = x.get();
if (n == a) break;
x = n;
}
// Return the sum of the data fields.
return sum;
}

// An object that has references to nested objects that also should be scanned.
class D extends C {
def children = [C.new("n1", 33), C.new("n2", 44), C.new("n3", 55)];

new(name: string, data: int) super(name, data) { }

def countScans() -> int {
var sum = scans;
for (c in children) sum += c.scans;
return sum;
}
}

def allocd(v: int) -> C {
var c = C.new("c0", v);
c.update(D.new("d0", 22));
return c;
}

def test2(v: int) -> int {
globalScans = 0;
def GC_COUNT = 5;
// Allocate one object that refers to a whole subtree that needs to be scanned.
var c = allocd(v);

// Run some GCs
for (i < GC_COUNT) RiGc.forceGC();

// Check that all the scans were called.
if (globalScans != 5 * GC_COUNT) return 1000000 + globalScans;
var got = c.countScans() + c.get().countScans();
if (got != globalScans) return 100000 + got;
return 42;
}

def main() -> int {
var tests = [test1, test2];
for (i < tests.length) {
System.puts("##+test");
System.puti(i + 1);
System.ln();
var r = tests[i](99 + i);
if (r == 42) {
System.puts("##-ok\n");
} else {
System.puts("##-fail: ");
System.puti(r);
System.ln();
}
}
return 42;
}
4 changes: 4 additions & 0 deletions test/rt/user_scan1.v3.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
##+test1
##-ok
##+test2
##-ok
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit 3830aee

Please sign in to comment.