spore_vm/gc/
keep_reachable_set.rs1use std::{
2 collections::{hash_map::Entry, HashMap},
3 hash::Hash,
4};
5
6use compact_str::CompactString;
7use log::*;
8
9use crate::val::{custom::CustomVal, ByteCode, ListVal, StructVal, UnsafeVal, ValId};
10
11use super::is_garbage_collected;
12
13type ReferenceCounter = usize;
14
15#[derive(Debug, Default)]
18pub struct KeepReachableSet {
19 strings: HashMap<ValId<CompactString>, ReferenceCounter>,
20 mutable_boxes: HashMap<ValId<UnsafeVal>, ReferenceCounter>,
21 lists: HashMap<ValId<ListVal>, ReferenceCounter>,
22 structs: HashMap<ValId<StructVal>, ReferenceCounter>,
23 bytecodes: HashMap<ValId<ByteCode>, ReferenceCounter>,
24 customs: HashMap<ValId<CustomVal>, ReferenceCounter>,
25}
26
27impl KeepReachableSet {
28 pub fn iter(&self) -> impl '_ + Iterator<Item = UnsafeVal> {
30 self.strings
31 .keys()
32 .copied()
33 .map(Into::into)
34 .chain(self.mutable_boxes.keys().copied().map(Into::into))
35 .chain(self.lists.keys().copied().map(Into::into))
36 .chain(self.bytecodes.keys().copied().map(Into::into))
37 .chain(self.customs.keys().copied().map(Into::into))
38 }
39
40 pub fn insert(&mut self, val: UnsafeVal) {
42 match val {
43 UnsafeVal::String(x) => self.strings.increment(x),
44 UnsafeVal::MutableBox(x) => self.mutable_boxes.increment(x),
45 UnsafeVal::List(x) => self.lists.increment(x),
46 UnsafeVal::Struct(x) => self.structs.increment(x),
47 UnsafeVal::ByteCodeFunction(x) => self.bytecodes.increment(x),
48 UnsafeVal::Custom(x) => self.customs.increment(x),
49 v => assert!(!is_garbage_collected(v)),
50 }
51 }
52
53 pub fn remove(&mut self, val: UnsafeVal) {
55 match val {
56 UnsafeVal::String(x) => self.strings.decrement(x),
57 UnsafeVal::MutableBox(x) => self.mutable_boxes.decrement(x),
58 UnsafeVal::List(x) => self.lists.decrement(x),
59 UnsafeVal::Struct(x) => self.structs.decrement(x),
60 UnsafeVal::ByteCodeFunction(x) => self.bytecodes.decrement(x),
61 UnsafeVal::Custom(x) => self.customs.decrement(x),
62 v => assert!(!is_garbage_collected(v)),
63 }
64 }
65}
66
67trait ReachableStoreSealed {
69 type K: Copy + std::fmt::Debug + Hash + Eq;
70 fn as_mut_hashmap(&mut self) -> &mut HashMap<Self::K, ReferenceCounter>;
71
72 fn increment(&mut self, k: Self::K) {
73 match self.as_mut_hashmap().entry(k) {
74 Entry::Occupied(entry) => {
75 *entry.into_mut() += 1;
76 }
77 Entry::Vacant(entry) => {
78 entry.insert(1);
79 }
80 };
81 }
82 fn decrement(&mut self, k: Self::K) {
83 match self.as_mut_hashmap().entry(k) {
84 Entry::Occupied(mut entry) => {
85 match entry.get().saturating_sub(1) {
86 0 => entry.remove(),
87 x => entry.insert(x),
88 };
89 }
90 Entry::Vacant(_) => {
91 warn!("Tried to remove non-existant value {k:?} from keep reachable set. The object reference likely outlived its lifetime. Although not a not considered unsafe, this is likely a memory leak.");
92 }
93 };
94 }
95}
96
97impl<T: std::fmt::Debug> ReachableStoreSealed for HashMap<ValId<T>, ReferenceCounter> {
98 type K = ValId<T>;
99
100 fn as_mut_hashmap(&mut self) -> &mut HashMap<Self::K, ReferenceCounter> {
101 self
102 }
103}