FA-90541 / Garbage collector invariants / Open access
Reference processing: finalizable object's children freed · case 01
A finalizer runs against an object whose fields point at freed memory.
ROOT CAUSE
Only the finalizable objects themselves are resurrected, not what they reference.
VERIFIED REPAIR
Resurrect the full transitive closure of objects queued for finalization.
Unsuccessful approach: Keeping just their direct children frees the finalizable objects and anything deeper.
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 = set(strong) | set(to_final)
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': [14, 15, 16], 'phantom': ['p1', 'p2'], 'weak': ['w1']}, {'finalize': [], 'freed': [13], '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': [14, 15, 16], 'phantom': [], 'weak': ['w1']}] | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': ['w1']}] | Failed |
| finalizer runs only once | [{'finalize': [13], 'freed': [14, 15, 16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}] | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}] | Failed |
| children of a finalizable object stay alive | [{'finalize': [13], 'freed': [14, 15, 16], 'phantom': ['p1'], 'weak': []}] | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}] | Failed |
| phantom of an object only reachable from a finalizable one | [{'finalize': [13], 'freed': [14, 15, 16], 'phantom': ['p1'], 'weak': []}, {'finalize': [], 'freed': [13], '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 / 9f12be84653553f8cc54a45c57e4cff219d3195be08bd81cdb7237969aecc32a
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 = strong | {c for o in to_final for c in heap[o] if c is not None}
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': [13, 15, 16], 'phantom': ['p2'], 'weak': ['w1']}, {'finalize': [], 'freed': [14], 'phantom': ['p1'], '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': [13, 15, 16], 'phantom': [], 'weak': ['w1']}] | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': ['w1']}] | Failed |
| finalizer runs only once | [{'finalize': [13], 'freed': [13, 15, 16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [14], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}] | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}] | Failed |
| children of a finalizable object stay alive | [{'finalize': [13], 'freed': [13, 15, 16], 'phantom': ['p1'], 'weak': []}] | [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}] | Failed |
| phantom of an object only reachable from a finalizable one | [{'finalize': [13], 'freed': [13, 15, 16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [14], 'phantom': ['p1'], '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 / a3ec62707df180aba33bb4011cfc6e2b7c4a1d4f35bb9a3383ec5a0b312b1b29
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.703510+00:00.
Case digest / b78903d571d9af6009bf94c23056d29f58602bea938d3fbfa8c05f60a45b89cc