Skip to content

Commit b907afd

Browse files
committed
Make an effort to clear collected references from the __subclasses__ dict
1 parent c7cca46 commit b907afd

File tree

4 files changed

+51
-6
lines changed

4 files changed

+51
-6
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/EconomicMapStorage.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import com.oracle.graal.python.builtins.objects.common.ObjectHashMap.DictKey;
5151
import com.oracle.graal.python.builtins.objects.common.ObjectHashMap.MapCursor;
5252
import com.oracle.graal.python.builtins.objects.common.ObjectHashMap.PutNode;
53+
import com.oracle.graal.python.builtins.objects.common.ObjectHashMap.RemoveNode;
5354
import com.oracle.graal.python.lib.PyObjectHashNode;
5455
import com.oracle.truffle.api.CompilerAsserts;
5556
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -149,19 +150,24 @@ protected void setValueForAllKeys(VirtualFrame frame, Node inliningTarget, Objec
149150
}
150151
}
151152

153+
@TruffleBoundary
154+
public Object removeUncached(Object key, long hash) {
155+
return RemoveNode.removeUncached(map, key, hash);
156+
}
157+
152158
@TruffleBoundary
153159
public void putUncached(TruffleString key, Object value) {
154-
ObjectHashMap.PutNode.putUncached(this.map, key, PyObjectHashNode.hash(key, HashCodeNode.getUncached()), value);
160+
PutNode.putUncached(map, key, PyObjectHashNode.hash(key, HashCodeNode.getUncached()), value);
155161
}
156162

157163
@TruffleBoundary
158164
public void putUncached(Object key, Object value) {
159-
PutNode.putUncached(this.map, key, PyObjectHashNode.executeUncached(key), value);
165+
PutNode.putUncached(map, key, PyObjectHashNode.executeUncached(key), value);
160166
}
161167

162168
@TruffleBoundary
163169
public void putUncached(int key, Object value) {
164-
PutNode.putUncached(this.map, key, PyObjectHashNode.hash(key), value);
170+
PutNode.putUncached(map, key, PyObjectHashNode.hash(key), value);
165171
}
166172

167173
@TruffleBoundary

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingStorageNodes.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodesFactory.HashingStorageGetItemWithHashNodeGen;
5555
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodesFactory.HashingStorageGetIteratorNodeGen;
5656
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodesFactory.HashingStorageGetReverseIteratorNodeGen;
57+
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodesFactory.HashingStorageIteratorKeyHashNodeGen;
5758
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodesFactory.HashingStorageIteratorKeyNodeGen;
5859
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodesFactory.HashingStorageIteratorNextNodeGen;
5960
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodesFactory.HashingStorageIteratorValueNodeGen;
@@ -1120,6 +1121,9 @@ static Object foreign(ForeignHashingStorage self, HashingStorageIterator it,
11201121
@GenerateInline
11211122
@GenerateCached(false)
11221123
public abstract static class HashingStorageIteratorKeyHash extends PNodeWithContext {
1124+
public static long executeUncached(HashingStorage storage, HashingStorageIterator it) {
1125+
return HashingStorageIteratorKeyHashNodeGen.getUncached().execute(null, null, storage, it);
1126+
}
11231127

11241128
public abstract long execute(Frame frame, Node inliningTarget, HashingStorage storage, HashingStorageIterator it);
11251129

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/ObjectHashMap.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import java.util.Arrays;
4646

4747
import com.oracle.graal.python.builtins.objects.common.ObjectHashMapFactory.PutNodeGen;
48+
import com.oracle.graal.python.builtins.objects.common.ObjectHashMapFactory.RemoveNodeGen;
4849
import com.oracle.graal.python.lib.PyObjectRichCompareBool;
4950
import com.oracle.graal.python.util.PythonUtils;
5051
import com.oracle.truffle.api.CompilerDirectives;
@@ -677,6 +678,10 @@ private boolean needsCompaction() {
677678
@GenerateInline
678679
@GenerateCached(false)
679680
public abstract static class RemoveNode extends Node {
681+
public static Object removeUncached(ObjectHashMap map, Object key, long keyHash) {
682+
return RemoveNodeGen.getUncached().execute(null, null, map, key, keyHash);
683+
}
684+
680685
public abstract Object execute(Frame frame, Node inliningTarget, ObjectHashMap map, Object key, long keyHash);
681686

682687
// "public" for testing...

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,33 @@ static TruffleString getQualName(Node inliningTarget, PythonAbstractNativeObject
803803
}
804804

805805
public static final class GetSubclassesNode {
806+
static record KeyAndHash(Object key, long hash) {
807+
}
808+
809+
static final class CollectEmptyKeys extends HashingStorageForEachCallback<List<KeyAndHash>> {
810+
@Override
811+
public final List<KeyAndHash> execute(Frame frame, Node inliningTarget, HashingStorage storage, HashingStorageIterator it, List<KeyAndHash> acc) {
812+
Object value = HashingStorageIteratorValue.executeUncached(storage, it);
813+
if (value instanceof PReferenceType pref) {
814+
Object subclassValue = pref.getObject();
815+
if (subclassValue == null) {
816+
Object key = HashingStorageIteratorKey.executeUncached(storage, it);
817+
long hash = HashingStorageIteratorKeyHash.executeUncached(storage, it);
818+
acc.add(new KeyAndHash(key, hash));
819+
}
820+
}
821+
return acc;
822+
}
823+
}
824+
825+
static void clearEmptyReferences(EconomicMapStorage storage) {
826+
List<KeyAndHash> acc = new ArrayList<>();
827+
HashingStorageForEach.executeUncached(storage, new CollectEmptyKeys(), acc);
828+
for (KeyAndHash k : acc) {
829+
storage.removeUncached(k.key(), k.hash());
830+
}
831+
}
832+
806833
protected static void addSubclass(PythonAbstractClass base, PythonManagedClass subclass) {
807834
CompilerAsserts.neverPartOfCompilation();
808835
PDict dict = executeUncached(base);
@@ -811,9 +838,11 @@ protected static void addSubclass(PythonAbstractClass base, PythonManagedClass s
811838
if (!(storage instanceof EconomicMapStorage)) {
812839
assert storage == EmptyStorage.INSTANCE : "Unexpected storage type!";
813840
storage = EconomicMapStorage.create();
841+
dict.setDictStorage(storage);
842+
} else {
843+
clearEmptyReferences((EconomicMapStorage) storage);
814844
}
815845
((EconomicMapStorage) storage).putUncached(weakref, weakref);
816-
dict.setDictStorage(storage);
817846
}
818847

819848
static final class RemoveSubclassValue extends HashingStorageForEachCallback<PythonManagedClass> {
@@ -838,8 +867,9 @@ protected static void removeSubclass(PythonAbstractClass base, PythonManagedClas
838867
CompilerAsserts.neverPartOfCompilation();
839868
PDict dict = executeUncached(base);
840869
HashingStorage storage = dict.getDictStorage();
841-
if (storage instanceof EconomicMapStorage) {
842-
HashingStorageForEach.executeUncached(storage, new RemoveSubclassValue(), subclass);
870+
if (storage instanceof EconomicMapStorage ems) {
871+
HashingStorageForEach.executeUncached(ems, new RemoveSubclassValue(), subclass);
872+
clearEmptyReferences(ems);
843873
} else {
844874
assert storage == EmptyStorage.INSTANCE : "Unexpected storage type!";
845875
}

0 commit comments

Comments
 (0)