FA-90546 / Garbage collector invariants / Open access
Reference processing: phantoms enqueued while referent is still finalizer-reachable · case 01
Cleanup actions run for objects that a pending finalizer can still reach.
ROOT CAUSE
Phantom references are checked against strong reachability only.
VERIFIED REPAIR
Enqueue a phantom only when the referent is unreachable after resurrection.
Unsuccessful approach: Exempting only the finalizable objects still enqueues phantoms of their children.
Case contract
Each cycle: trace strong reachability from roots (weak and phantom references are not traced); weak references whose referent is not strongly reachable are cleared; pending finalizable objects that are not strongly reachable are queued for finalization once and resurrected together with everything they reach for this cycle; phantom references are enqueued only when their referent is not reachable even through the resurrected set; everything else is freed. Return per cycle the cleared weak refs, finalized objects, enqueued phantoms and freed objects.
Why this case matters
Reference-processing order decides whether code can observe resurrected or half-dead objects.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(heap, roots, weak, finalizable, phantom, cycles):
heap = {k: list(v) for k, v in heap.items()}
weak = {r: t for r, t in weak}
phantom = {r: t for r, t in phantom}
pending = set(finalizable)
out = []
def trace(starts, seen):
stack = list(starts)
while stack:
o = stack.pop()
if o is None or o in seen or o not in heap:
continue
seen.add(o)
stack.extend(heap[o])
return seen
for _ in range(cycles):
strong = trace(roots, set())
to_final = sorted(o for o in pending if o not in strong and o in heap)
reach = trace(to_final, set(strong))
cleared = sorted(r for r, t in weak.items() if t is not None and t not in strong)
for r in cleared:
weak[r] = None
pending -= set(to_final)
enq = sorted(r for r, t in phantom.items() if t is not None and t not in strong)
for r in enq:
phantom[r] = None
freed = sorted(o for o in heap if o not in reach)
for o in freed:
del heap[o]
out.append({'weak': cleared, 'finalize': to_final, 'phantom': enq, 'freed': freed})
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression: finalizable object over two cycles',
({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []},
[11],
[['w1', 13], ['w2', 12]],
[13],
[['p1', 14], ['p2', 16]],
2),
[{'finalize': [13], 'freed': [16], 'phantom': ['p2'], 'weak': ['w1']},
{'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}]),
('weak reference to a resurrected object',
({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [['w1', 13]], [13], [], 1),
[{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': ['w1']}]),
('finalizer runs only once',
({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [], [13], [], 3),
[{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
('children of a finalizable object stay alive',
({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [], [13], [['p1', 15]], 1),
[{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}]),
('phantom of an object only reachable from a finalizable one',
({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [], [13], [['p1', 14]], 2),
[{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}]),
('control: all strongly reachable',
({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []},
[11, 13, 16],
[['w1', 13], ['w2', 12]],
[13],
[['p1', 14], ['p2', 16]],
2),
[{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
[('regression: finalizable object over two cycles',
({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []},
[21],
[['w1', 23], ['w2', 22]],
[23],
[['p1', 24], ['p2', 26]],
2),
[{'finalize': [23], 'freed': [26], 'phantom': ['p2'], 'weak': ['w1']},
{'finalize': [], 'freed': [23, 24, 25], 'phantom': ['p1'], 'weak': []}]),
('weak reference to a resurrected object',
({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [['w1', 23]], [23], [], 1),
[{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': ['w1']}]),
('finalizer runs only once',
({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [], [23], [], 3),
[{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [23, 24, 25], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
('children of a finalizable object stay alive',
({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [], [23], [['p1', 25]], 1),
[{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': []}]),
('phantom of an object only reachable from a finalizable one',
({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [], [23], [['p1', 24]], 2),
[{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [23, 24, 25], 'phantom': ['p1'], 'weak': []}]),
('control: all strongly reachable',
({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []},
[21, 23, 26],
[['w1', 23], ['w2', 22]],
[23],
[['p1', 24], ['p2', 26]],
2),
[{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
[('regression: finalizable object over two cycles',
({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []},
[31],
[['w1', 33], ['w2', 32]],
[33],
[['p1', 34], ['p2', 36]],
2),
[{'finalize': [33], 'freed': [36], 'phantom': ['p2'], 'weak': ['w1']},
{'finalize': [], 'freed': [33, 34, 35], 'phantom': ['p1'], 'weak': []}]),
('weak reference to a resurrected object',
({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [['w1', 33]], [33], [], 1),
[{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': ['w1']}]),
('finalizer runs only once',
({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [], [33], [], 3),
[{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [33, 34, 35], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
('children of a finalizable object stay alive',
({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [], [33], [['p1', 35]], 1),
[{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': []}]),
('phantom of an object only reachable from a finalizable one',
({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [], [33], [['p1', 34]], 2),
[{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [33, 34, 35], 'phantom': ['p1'], 'weak': []}]),
('control: all strongly reachable',
({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []},
[31, 33, 36],
[['w1', 33], ['w2', 32]],
[33],
[['p1', 34], ['p2', 36]],
2),
[{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
[('regression: finalizable object over two cycles',
({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []},
[41],
[['w1', 43], ['w2', 42]],
[43],
[['p1', 44], ['p2', 46]],
2),
[{'finalize': [43], 'freed': [46], 'phantom': ['p2'], 'weak': ['w1']},
{'finalize': [], 'freed': [43, 44, 45], 'phantom': ['p1'], 'weak': []}]),
('weak reference to a resurrected object',
({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [['w1', 43]], [43], [], 1),
[{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': ['w1']}]),
('finalizer runs only once',
({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [], [43], [], 3),
[{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [43, 44, 45], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
('children of a finalizable object stay alive',
({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [], [43], [['p1', 45]], 1),
[{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': []}]),
('phantom of an object only reachable from a finalizable one',
({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [], [43], [['p1', 44]], 2),
[{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [43, 44, 45], 'phantom': ['p1'], 'weak': []}]),
('control: all strongly reachable',
({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []},
[41, 43, 46],
[['w1', 43], ['w2', 42]],
[43],
[['p1', 44], ['p2', 46]],
2),
[{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
[('regression: finalizable object over two cycles',
({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []},
[51],
[['w1', 53], ['w2', 52]],
[53],
[['p1', 54], ['p2', 56]],
2),
[{'finalize': [53], 'freed': [56], 'phantom': ['p2'], 'weak': ['w1']},
{'finalize': [], 'freed': [53, 54, 55], 'phantom': ['p1'], 'weak': []}]),
('weak reference to a resurrected object',
({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [['w1', 53]], [53], [], 1),
[{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': ['w1']}]),
('finalizer runs only once',
({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [], [53], [], 3),
[{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [53, 54, 55], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
('children of a finalizable object stay alive',
({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [], [53], [['p1', 55]], 1),
[{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': []}]),
('phantom of an object only reachable from a finalizable one',
({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [], [53], [['p1', 54]], 2),
[{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [53, 54, 55], 'phantom': ['p1'], 'weak': []}]),
('control: all strongly reachable',
({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []},
[51, 53, 56],
[['w1', 53], ['w2', 52]],
[53],
[['p1', 54], ['p2', 56]],
2),
[{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])]]
for label, args, expected in cases[N - 1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: finalizable object over two cycles | [{'finalize': [13], 'freed': [16], 'phantom': ['p1', 'p2'], 'weak': ['w1']}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []}] | [{'finalize': [13], 'freed': [16], 'phantom': ['p2'], 'weak': ['w1']}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}] | Failed |
| weak reference to a resurrected object | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': ['w1']}] | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': ['w1']}] | Passed |
| finalizer runs only once | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}] | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}] | Passed |
| children of a finalizable object stay alive | [{'finalize': [13], 'freed': [16], 'phantom': ['p1'], 'weak': []}] | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}] | Failed |
| phantom of an object only reachable from a finalizable one | [{'finalize': [13], 'freed': [16], 'phantom': ['p1'], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []}] | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}] | Failed |
| control: all strongly reachable | [{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}] | [{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}] | Passed |
SHA-256 / 23d0c57373ce47c2f4dd48b7a7d4c605acbba916731016027118dbe9fe648998
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(heap, roots, weak, finalizable, phantom, cycles):
heap = {k: list(v) for k, v in heap.items()}
weak = {r: t for r, t in weak}
phantom = {r: t for r, t in phantom}
pending = set(finalizable)
out = []
def trace(starts, seen):
stack = list(starts)
while stack:
o = stack.pop()
if o is None or o in seen or o not in heap:
continue
seen.add(o)
stack.extend(heap[o])
return seen
for _ in range(cycles):
strong = trace(roots, set())
to_final = sorted(o for o in pending if o not in strong and o in heap)
reach = trace(to_final, set(strong))
cleared = sorted(r for r, t in weak.items() if t is not None and t not in strong)
for r in cleared:
weak[r] = None
pending -= set(to_final)
enq = sorted(r for r, t in phantom.items() if t is not None and t not in strong and t not in to_final)
for r in enq:
phantom[r] = None
freed = sorted(o for o in heap if o not in reach)
for o in freed:
del heap[o]
out.append({'weak': cleared, 'finalize': to_final, 'phantom': enq, 'freed': freed})
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression: finalizable object over two cycles',
({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []},
[11],
[['w1', 13], ['w2', 12]],
[13],
[['p1', 14], ['p2', 16]],
2),
[{'finalize': [13], 'freed': [16], 'phantom': ['p2'], 'weak': ['w1']},
{'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}]),
('weak reference to a resurrected object',
({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [['w1', 13]], [13], [], 1),
[{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': ['w1']}]),
('finalizer runs only once',
({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [], [13], [], 3),
[{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
('children of a finalizable object stay alive',
({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [], [13], [['p1', 15]], 1),
[{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}]),
('phantom of an object only reachable from a finalizable one',
({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [], [13], [['p1', 14]], 2),
[{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}]),
('control: all strongly reachable',
({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []},
[11, 13, 16],
[['w1', 13], ['w2', 12]],
[13],
[['p1', 14], ['p2', 16]],
2),
[{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
[('regression: finalizable object over two cycles',
({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []},
[21],
[['w1', 23], ['w2', 22]],
[23],
[['p1', 24], ['p2', 26]],
2),
[{'finalize': [23], 'freed': [26], 'phantom': ['p2'], 'weak': ['w1']},
{'finalize': [], 'freed': [23, 24, 25], 'phantom': ['p1'], 'weak': []}]),
('weak reference to a resurrected object',
({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [['w1', 23]], [23], [], 1),
[{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': ['w1']}]),
('finalizer runs only once',
({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [], [23], [], 3),
[{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [23, 24, 25], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
('children of a finalizable object stay alive',
({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [], [23], [['p1', 25]], 1),
[{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': []}]),
('phantom of an object only reachable from a finalizable one',
({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [], [23], [['p1', 24]], 2),
[{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [23, 24, 25], 'phantom': ['p1'], 'weak': []}]),
('control: all strongly reachable',
({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []},
[21, 23, 26],
[['w1', 23], ['w2', 22]],
[23],
[['p1', 24], ['p2', 26]],
2),
[{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
[('regression: finalizable object over two cycles',
({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []},
[31],
[['w1', 33], ['w2', 32]],
[33],
[['p1', 34], ['p2', 36]],
2),
[{'finalize': [33], 'freed': [36], 'phantom': ['p2'], 'weak': ['w1']},
{'finalize': [], 'freed': [33, 34, 35], 'phantom': ['p1'], 'weak': []}]),
('weak reference to a resurrected object',
({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [['w1', 33]], [33], [], 1),
[{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': ['w1']}]),
('finalizer runs only once',
({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [], [33], [], 3),
[{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [33, 34, 35], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
('children of a finalizable object stay alive',
({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [], [33], [['p1', 35]], 1),
[{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': []}]),
('phantom of an object only reachable from a finalizable one',
({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [], [33], [['p1', 34]], 2),
[{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [33, 34, 35], 'phantom': ['p1'], 'weak': []}]),
('control: all strongly reachable',
({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []},
[31, 33, 36],
[['w1', 33], ['w2', 32]],
[33],
[['p1', 34], ['p2', 36]],
2),
[{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
[('regression: finalizable object over two cycles',
({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []},
[41],
[['w1', 43], ['w2', 42]],
[43],
[['p1', 44], ['p2', 46]],
2),
[{'finalize': [43], 'freed': [46], 'phantom': ['p2'], 'weak': ['w1']},
{'finalize': [], 'freed': [43, 44, 45], 'phantom': ['p1'], 'weak': []}]),
('weak reference to a resurrected object',
({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [['w1', 43]], [43], [], 1),
[{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': ['w1']}]),
('finalizer runs only once',
({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [], [43], [], 3),
[{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [43, 44, 45], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
('children of a finalizable object stay alive',
({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [], [43], [['p1', 45]], 1),
[{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': []}]),
('phantom of an object only reachable from a finalizable one',
({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [], [43], [['p1', 44]], 2),
[{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [43, 44, 45], 'phantom': ['p1'], 'weak': []}]),
('control: all strongly reachable',
({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []},
[41, 43, 46],
[['w1', 43], ['w2', 42]],
[43],
[['p1', 44], ['p2', 46]],
2),
[{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
[('regression: finalizable object over two cycles',
({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []},
[51],
[['w1', 53], ['w2', 52]],
[53],
[['p1', 54], ['p2', 56]],
2),
[{'finalize': [53], 'freed': [56], 'phantom': ['p2'], 'weak': ['w1']},
{'finalize': [], 'freed': [53, 54, 55], 'phantom': ['p1'], 'weak': []}]),
('weak reference to a resurrected object',
({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [['w1', 53]], [53], [], 1),
[{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': ['w1']}]),
('finalizer runs only once',
({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [], [53], [], 3),
[{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [53, 54, 55], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
('children of a finalizable object stay alive',
({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [], [53], [['p1', 55]], 1),
[{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': []}]),
('phantom of an object only reachable from a finalizable one',
({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [], [53], [['p1', 54]], 2),
[{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [53, 54, 55], 'phantom': ['p1'], 'weak': []}]),
('control: all strongly reachable',
({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []},
[51, 53, 56],
[['w1', 53], ['w2', 52]],
[53],
[['p1', 54], ['p2', 56]],
2),
[{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])]]
for label, args, expected in cases[N - 1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: finalizable object over two cycles | [{'finalize': [13], 'freed': [16], 'phantom': ['p1', 'p2'], 'weak': ['w1']}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []}] | [{'finalize': [13], 'freed': [16], 'phantom': ['p2'], 'weak': ['w1']}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}] | Failed |
| weak reference to a resurrected object | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': ['w1']}] | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': ['w1']}] | Passed |
| finalizer runs only once | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}] | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}] | Passed |
| children of a finalizable object stay alive | [{'finalize': [13], 'freed': [16], 'phantom': ['p1'], 'weak': []}] | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}] | Failed |
| phantom of an object only reachable from a finalizable one | [{'finalize': [13], 'freed': [16], 'phantom': ['p1'], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []}] | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}] | Failed |
| control: all strongly reachable | [{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}] | [{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}] | Passed |
SHA-256 / f1671110163da1b7e553b12083c22c4a3e7a7ac708579608f69b4077528bc0cb
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(heap, roots, weak, finalizable, phantom, cycles):
heap = {k: list(v) for k, v in heap.items()}
weak = {r: t for r, t in weak}
phantom = {r: t for r, t in phantom}
pending = set(finalizable)
out = []
def trace(starts, seen):
stack = list(starts)
while stack:
o = stack.pop()
if o is None or o in seen or o not in heap:
continue
seen.add(o)
stack.extend(heap[o])
return seen
for _ in range(cycles):
strong = trace(roots, set())
to_final = sorted(o for o in pending if o not in strong and o in heap)
reach = trace(to_final, set(strong))
cleared = sorted(r for r, t in weak.items() if t is not None and t not in strong)
for r in cleared:
weak[r] = None
pending -= set(to_final)
enq = sorted(r for r, t in phantom.items() if t is not None and t not in reach)
for r in enq:
phantom[r] = None
freed = sorted(o for o in heap if o not in reach)
for o in freed:
del heap[o]
out.append({'weak': cleared, 'finalize': to_final, 'phantom': enq, 'freed': freed})
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression: finalizable object over two cycles',
({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []},
[11],
[['w1', 13], ['w2', 12]],
[13],
[['p1', 14], ['p2', 16]],
2),
[{'finalize': [13], 'freed': [16], 'phantom': ['p2'], 'weak': ['w1']},
{'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}]),
('weak reference to a resurrected object',
({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [['w1', 13]], [13], [], 1),
[{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': ['w1']}]),
('finalizer runs only once',
({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [], [13], [], 3),
[{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
('children of a finalizable object stay alive',
({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [], [13], [['p1', 15]], 1),
[{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}]),
('phantom of an object only reachable from a finalizable one',
({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [], [13], [['p1', 14]], 2),
[{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}]),
('control: all strongly reachable',
({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []},
[11, 13, 16],
[['w1', 13], ['w2', 12]],
[13],
[['p1', 14], ['p2', 16]],
2),
[{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
[('regression: finalizable object over two cycles',
({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []},
[21],
[['w1', 23], ['w2', 22]],
[23],
[['p1', 24], ['p2', 26]],
2),
[{'finalize': [23], 'freed': [26], 'phantom': ['p2'], 'weak': ['w1']},
{'finalize': [], 'freed': [23, 24, 25], 'phantom': ['p1'], 'weak': []}]),
('weak reference to a resurrected object',
({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [['w1', 23]], [23], [], 1),
[{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': ['w1']}]),
('finalizer runs only once',
({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [], [23], [], 3),
[{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [23, 24, 25], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
('children of a finalizable object stay alive',
({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [], [23], [['p1', 25]], 1),
[{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': []}]),
('phantom of an object only reachable from a finalizable one',
({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [], [23], [['p1', 24]], 2),
[{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [23, 24, 25], 'phantom': ['p1'], 'weak': []}]),
('control: all strongly reachable',
({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []},
[21, 23, 26],
[['w1', 23], ['w2', 22]],
[23],
[['p1', 24], ['p2', 26]],
2),
[{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
[('regression: finalizable object over two cycles',
({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []},
[31],
[['w1', 33], ['w2', 32]],
[33],
[['p1', 34], ['p2', 36]],
2),
[{'finalize': [33], 'freed': [36], 'phantom': ['p2'], 'weak': ['w1']},
{'finalize': [], 'freed': [33, 34, 35], 'phantom': ['p1'], 'weak': []}]),
('weak reference to a resurrected object',
({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [['w1', 33]], [33], [], 1),
[{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': ['w1']}]),
('finalizer runs only once',
({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [], [33], [], 3),
[{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [33, 34, 35], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
('children of a finalizable object stay alive',
({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [], [33], [['p1', 35]], 1),
[{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': []}]),
('phantom of an object only reachable from a finalizable one',
({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [], [33], [['p1', 34]], 2),
[{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [33, 34, 35], 'phantom': ['p1'], 'weak': []}]),
('control: all strongly reachable',
({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []},
[31, 33, 36],
[['w1', 33], ['w2', 32]],
[33],
[['p1', 34], ['p2', 36]],
2),
[{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
[('regression: finalizable object over two cycles',
({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []},
[41],
[['w1', 43], ['w2', 42]],
[43],
[['p1', 44], ['p2', 46]],
2),
[{'finalize': [43], 'freed': [46], 'phantom': ['p2'], 'weak': ['w1']},
{'finalize': [], 'freed': [43, 44, 45], 'phantom': ['p1'], 'weak': []}]),
('weak reference to a resurrected object',
({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [['w1', 43]], [43], [], 1),
[{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': ['w1']}]),
('finalizer runs only once',
({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [], [43], [], 3),
[{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [43, 44, 45], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
('children of a finalizable object stay alive',
({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [], [43], [['p1', 45]], 1),
[{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': []}]),
('phantom of an object only reachable from a finalizable one',
({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [], [43], [['p1', 44]], 2),
[{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [43, 44, 45], 'phantom': ['p1'], 'weak': []}]),
('control: all strongly reachable',
({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []},
[41, 43, 46],
[['w1', 43], ['w2', 42]],
[43],
[['p1', 44], ['p2', 46]],
2),
[{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
[('regression: finalizable object over two cycles',
({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []},
[51],
[['w1', 53], ['w2', 52]],
[53],
[['p1', 54], ['p2', 56]],
2),
[{'finalize': [53], 'freed': [56], 'phantom': ['p2'], 'weak': ['w1']},
{'finalize': [], 'freed': [53, 54, 55], 'phantom': ['p1'], 'weak': []}]),
('weak reference to a resurrected object',
({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [['w1', 53]], [53], [], 1),
[{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': ['w1']}]),
('finalizer runs only once',
({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [], [53], [], 3),
[{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [53, 54, 55], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
('children of a finalizable object stay alive',
({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [], [53], [['p1', 55]], 1),
[{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': []}]),
('phantom of an object only reachable from a finalizable one',
({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [], [53], [['p1', 54]], 2),
[{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [53, 54, 55], 'phantom': ['p1'], 'weak': []}]),
('control: all strongly reachable',
({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []},
[51, 53, 56],
[['w1', 53], ['w2', 52]],
[53],
[['p1', 54], ['p2', 56]],
2),
[{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])]]
for label, args, expected in cases[N - 1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: finalizable object over two cycles | [{'finalize': [13], 'freed': [16], 'phantom': ['p2'], 'weak': ['w1']}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}] | [{'finalize': [13], 'freed': [16], 'phantom': ['p2'], 'weak': ['w1']}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}] | Passed |
| weak reference to a resurrected object | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': ['w1']}] | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': ['w1']}] | Passed |
| finalizer runs only once | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}] | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}] | Passed |
| children of a finalizable object stay alive | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}] | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}] | Passed |
| phantom of an object only reachable from a finalizable one | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}] | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}] | Passed |
| control: all strongly reachable | [{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}] | [{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}] | Passed |
SHA-256 / 1bc257fc71f29ad925cf0532cf3655eb6fabe21082ed79dfc30904232ea303fa
Verification & scope
A deterministic, bounded teaching model of one garbage-collector mechanism with stipulated rules; it is not a production collector and claims no conformance to any particular runtime. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:51:27.743441+00:00.
Case digest / e346e17b066e5a238d0b737cfd4f8db50878b3d21c5a38b94ef01eef2c3ee364