1use std::rc::Rc;
18
19use crate::bytecode::{CodeUnit, CodeUnitId, DYNAMIC};
20
21#[derive(Default)]
22pub struct DynamicUnits {
23 slots: Vec<Option<Rc<CodeUnit>>>,
25 free: Vec<u32>,
27 #[cfg(feature = "gc-stress")]
30 quarantine: Vec<u32>,
31 pinned: Vec<u32>,
34 marks: Vec<u64>,
35 live: usize,
36 live_bytes: usize,
37}
38
39#[inline]
40fn bit(index: u32) -> (usize, u64) {
41 ((index / 64) as usize, 1u64 << (index % 64))
42}
43
44impl DynamicUnits {
45 pub(crate) fn publish(&mut self, mut unit: CodeUnit) -> (CodeUnitId, usize) {
48 unit.atoms = unit.key_atoms();
49 let bytes = unit.bytes();
50 let unit = Some(Rc::new(unit));
51 let index = match self.free.pop() {
52 Some(index) => {
53 self.slots[index as usize] = unit;
54 index
55 }
56 None => {
57 let index = u32::try_from(self.slots.len())
58 .ok()
59 .filter(|&index| index < DYNAMIC)
60 .expect("dynamic code overflow");
61 self.slots.push(unit);
62 if index as usize / 64 == self.marks.len() {
63 self.marks.push(0);
64 }
65 index
66 }
67 };
68 self.pinned.push(index);
69 self.live += 1;
70 self.live_bytes += bytes;
71 (CodeUnitId(index | DYNAMIC), bytes)
72 }
73
74 #[inline]
76 #[track_caller]
77 pub fn get(&self, id: CodeUnitId) -> Rc<CodeUnit> {
78 Rc::clone(self.slot(id).expect("a live dynamic unit"))
79 }
80
81 pub fn lookup(&self, id: CodeUnitId) -> Option<&CodeUnit> {
83 self.slot(id).map(|unit| &**unit)
84 }
85
86 #[inline]
87 fn slot(&self, id: CodeUnitId) -> Option<&Rc<CodeUnit>> {
88 debug_assert!(id.is_dynamic());
89 self.slots.get((id.0 & !DYNAMIC) as usize)?.as_ref()
90 }
91
92 pub fn units(&self) -> impl Iterator<Item = (CodeUnitId, &CodeUnit)> {
94 self.slots.iter().enumerate().filter_map(|(index, unit)| {
95 let unit = unit.as_deref()?;
96 Some((CodeUnitId(index as u32 | DYNAMIC), unit))
97 })
98 }
99
100 pub fn len(&self) -> usize {
102 self.live
103 }
104
105 pub fn is_empty(&self) -> bool {
106 self.live == 0
107 }
108
109 pub fn live_bytes(&self) -> usize {
111 self.live_bytes
112 }
113
114 pub(crate) fn pin_mark(&self) -> usize {
116 self.pinned.len()
117 }
118
119 pub(crate) fn unpin(&mut self, mark: usize) {
123 self.pinned.truncate(mark);
124 }
125
126 pub(crate) fn pinned(&self) -> &[u32] {
127 &self.pinned
128 }
129
130 pub(crate) fn clear_marks(&mut self) {
131 self.marks.fill(0);
132 }
133
134 #[inline]
136 pub(crate) fn mark(&mut self, id: CodeUnitId) -> Option<&CodeUnit> {
137 let index = id.0 & !DYNAMIC;
138 let (word, mask) = bit(index);
139 let bits = &mut self.marks[word];
140 if *bits & mask != 0 {
141 return None;
142 }
143 *bits |= mask;
144 let unit = self.slots[index as usize].as_deref();
145 debug_assert!(unit.is_some(), "a record names freed dynamic unit {index}");
146 unit
147 }
148
149 pub(crate) fn is_marked(&self, index: u32) -> bool {
150 let (word, mask) = bit(index);
151 self.marks[word] & mask != 0
152 }
153
154 pub(crate) fn sweep(&mut self, mut dead: impl FnMut(&CodeUnit)) -> usize {
157 #[cfg(feature = "gc-stress")]
158 let held = {
159 let released = std::mem::take(&mut self.quarantine);
160 self.free.extend_from_slice(&released);
161 self.free.len()
162 };
163 let mut freed = 0;
164 for index in (0..self.slots.len() as u32).rev() {
165 if self.is_marked(index) {
166 continue;
167 }
168 let Some(unit) = self.slots[index as usize].take() else {
169 continue;
170 };
171 debug_assert_eq!(
172 Rc::strong_count(&unit),
173 1,
174 "a swept dynamic unit is still running"
175 );
176 dead(&unit);
177 freed += unit.bytes();
178 self.live -= 1;
179 self.free.push(index);
180 }
181 self.live_bytes -= freed;
182 #[cfg(feature = "gc-stress")]
183 self.quarantine.extend(self.free.drain(held..));
184 freed
185 }
186
187 pub(crate) fn verify(&self) {
190 for (index, unit) in self.slots.iter().enumerate() {
191 let index = index as u32;
192 assert_eq!(
193 unit.is_some(),
194 self.is_marked(index),
195 "gc verify: dynamic unit {index} is {}",
196 if unit.is_some() {
197 "live but unreachable"
198 } else {
199 "reachable but freed"
200 }
201 );
202 }
203 }
204}