Skip to content

Commit 9475e46

Browse files
author
Neo Dev
committed
Fix mark-sweep RC semantics and add equivalence tests
1 parent 9eefcab commit 9475e46

File tree

4 files changed

+2112
-10
lines changed

4 files changed

+2112
-10
lines changed

src/Neo.VM/MarkSweepReferenceCounter.cs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,7 @@ public void RemoveStackReference(StackItem item)
105105

106106
private void Track(StackItem item)
107107
{
108-
if (trackedItems.Add(item))
109-
zeroReferred.Add(item);
108+
trackedItems.Add(item);
110109
}
111110

112111
private void Collect()
@@ -120,31 +119,37 @@ private void Collect()
120119
Mark(item);
121120
}
122121

122+
// Legacy Tarjan RC clears zero-referred up-front and then removes every
123+
// unmarked tracked item. To preserve identical semantics (and avoid
124+
// keeping unreachable descendants that were never re-added to zeroReferred),
125+
// we do the same here.
126+
zeroReferred.Clear();
127+
123128
foreach (var item in trackedItems)
124129
{
125-
if (!marked.Contains(item) && zeroReferred.Contains(item))
130+
if (!marked.Contains(item))
126131
unreachable.Add(item);
127132
}
128133

129134
foreach (var item in unreachable)
130135
RemoveTracked(item);
131-
132-
zeroReferred.RemoveWhere(marked.Contains);
133136
}
134137

135138
private void Mark(StackItem root)
136139
{
137140
if (!marked.Add(root)) return;
138141
pending.Push(root);
142+
139143
while (pending.Count > 0)
140144
{
141145
var current = pending.Pop();
142-
if (current.ObjectReferences is null) continue;
143-
foreach (var entry in current.ObjectReferences.Values)
146+
if (current is not CompoundType compound) continue;
147+
148+
foreach (var child in compound.SubItems)
144149
{
145-
if (entry.References <= 0) continue;
146-
if (marked.Add(entry.Item))
147-
pending.Push(entry.Item);
150+
if (!NeedTrack(child)) continue;
151+
if (marked.Add(child))
152+
pending.Push(child);
148153
}
149154
}
150155
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright (C) 2015-2025 The Neo Project.
2+
//
3+
// UT_MarkSweepReferenceCounter.cs file belongs to the neo project and is free
4+
// software distributed under the MIT software license, see the
5+
// accompanying file LICENSE in the main directory of the
6+
// repository or http://www.opensource.org/licenses/mit-license.php
7+
// for more details.
8+
//
9+
// Redistribution and use in source and binary forms with or without
10+
// modifications are permitted.
11+
12+
using Microsoft.VisualStudio.TestTools.UnitTesting;
13+
using Neo.VM;
14+
using Neo.VM.Types;
15+
using Array = Neo.VM.Types.Array;
16+
17+
namespace Neo.Test;
18+
19+
[TestClass]
20+
public class UT_MarkSweepReferenceCounter
21+
{
22+
[TestMethod]
23+
public void TestReachableChildIsNotCollected()
24+
{
25+
var rc = new MarkSweepReferenceCounter();
26+
27+
var root = new Array(rc);
28+
var child = new Array(rc);
29+
30+
rc.AddStackReference(root);
31+
child.Add(1);
32+
child.Add(2);
33+
child.Add(3);
34+
35+
root.Add(child);
36+
37+
Assert.AreEqual(5, rc.Count);
38+
rc.CheckZeroReferred();
39+
Assert.AreEqual(5, rc.Count);
40+
}
41+
}
42+

0 commit comments

Comments
 (0)