aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/runtime/rt.zc12
1 files changed, 9 insertions, 3 deletions
diff --git a/src/runtime/rt.zc b/src/runtime/rt.zc
index 3d448b0..906e56a 100644
--- a/src/runtime/rt.zc
+++ b/src/runtime/rt.zc
@@ -427,7 +427,7 @@ const int F_SHARE = 8;
// p[-4] bits for drops (docs/design/destructors.md): the value has a drop in the side
// table (dtab), its drop has run (it never runs again), and the collector found it
// unreachable and runs its drop once the sweep is over.
-const int F_HASDROP = 16, F_DROPPED = 32, F_FINAL = 64;
+const int F_HASDROP = 16, F_DROPPED = 32, F_FINAL = 64, F_CYCLE = 128; // F_CYCLE: during gc_drops only, a candidate its own contents reach
int o3_depth, gc_pending;
// flags live in these bits; the size class is above them.
const int F_LOW = 65535;
@@ -1415,7 +1415,8 @@ int o3_leave() {
// the collector's part, between mark and sweep. the drops of live values are kept;
// an unreachable value with a drop that hasn't run is kept for one more cycle and
// flagged F_FINAL, unless another such value reaches it (its parent drops first and
-// it waits for a later collection). nothing runs user code here.
+// it waits for a later collection) and it doesn't reach itself (a cycle drops one
+// value per collection). nothing runs user code here.
int gc_drops() {
int i, again = 1; word k;
while (again) {
@@ -1436,6 +1437,7 @@ int gc_drops() {
k[-4] = k[-4] | F_FINAL;
markrange(k, k + k[-5] - HDR); mark(dtab[i * 3 + 1] - 1); mark(dtab[i * 3 + 2] - 1);
drain();
+ if ((k[-4] & 7) != 1) k[-4] = k[-4] | F_CYCLE; // what it reaches (or its drop) reaches it back: a cycle
}
i++;
}
@@ -1443,7 +1445,11 @@ int gc_drops() {
while (i < dt_cap) {
k = dtab[i * 3];
if (k > 1 && (k[-4] & F_FINAL)) {
- if ((k[-4] & 7) == 1) mark(k); else k[-4] = k[-4] & ~F_FINAL; // reached from another candidate: later
+ // reached from another candidate: later. one in a cycle has nothing outside it
+ // to drop first, so it drops now (it is marked, so kept), and the rest of the
+ // cycle in later collections, one each, never twice
+ if ((k[-4] & 7) == 1) mark(k); else if (!(k[-4] & F_CYCLE)) k[-4] = k[-4] & ~F_FINAL;
+ k[-4] = k[-4] & ~F_CYCLE;
}
i++;
}