1use crate::Value;
8use crate::{GcError, GcPtr, Result};
9use std::collections::HashMap;
10use std::sync::atomic::{AtomicUsize, Ordering};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub struct RootId(pub usize);
15
16pub trait GcRoot: Send + Sync {
18 fn scan(&self) -> Vec<GcPtr<Value>>;
20
21 fn description(&self) -> String;
23
24 fn estimated_size(&self) -> usize {
26 0 }
28
29 fn is_active(&self) -> bool {
31 true }
33}
34
35pub struct StackRoot {
37 stack_ptr: *const Vec<Value>,
39 description: String,
40}
41
42impl StackRoot {
43 pub unsafe fn new(stack: *const Vec<Value>, description: String) -> Self {
49 Self {
50 stack_ptr: stack,
51 description,
52 }
53 }
54}
55
56unsafe impl Send for StackRoot {}
59unsafe impl Sync for StackRoot {}
60
61impl GcRoot for StackRoot {
62 fn scan(&self) -> Vec<GcPtr<Value>> {
63 unsafe {
64 if self.stack_ptr.is_null() {
65 return Vec::new();
66 }
67
68 let stack = &*self.stack_ptr;
69 let mut roots = Vec::new();
70
71 for value in stack {
72 self.collect_value_roots(value, &mut roots);
73 }
74
75 roots
76 }
77 }
78
79 fn description(&self) -> String {
80 self.description.clone()
81 }
82
83 fn estimated_size(&self) -> usize {
84 unsafe {
85 if self.stack_ptr.is_null() {
86 return 0;
87 }
88
89 let stack = &*self.stack_ptr;
90 stack.len() * std::mem::size_of::<Value>()
91 }
92 }
93
94 fn is_active(&self) -> bool {
95 !self.stack_ptr.is_null()
96 }
97}
98
99impl StackRoot {
100 #[allow(clippy::only_used_in_recursion)]
102 fn collect_value_roots(&self, value: &Value, _roots: &mut Vec<GcPtr<Value>>) {
103 if let Value::Cell(cells) = value {
104 for cell_value in &cells.data {
105 self.collect_value_roots(cell_value, _roots);
106 }
107 }
108 }
111}
112
113pub struct VariableArrayRoot {
115 vars_ptr: *const Vec<Value>,
117 description: String,
118}
119
120impl VariableArrayRoot {
121 pub unsafe fn new(vars: *const Vec<Value>, description: String) -> Self {
127 Self {
128 vars_ptr: vars,
129 description,
130 }
131 }
132}
133
134unsafe impl Send for VariableArrayRoot {}
137unsafe impl Sync for VariableArrayRoot {}
138
139impl GcRoot for VariableArrayRoot {
140 fn scan(&self) -> Vec<GcPtr<Value>> {
141 unsafe {
142 if self.vars_ptr.is_null() {
143 return Vec::new();
144 }
145
146 let vars = &*self.vars_ptr;
147 let mut roots = Vec::new();
148
149 for value in vars {
150 self.collect_value_roots(value, &mut roots);
151 }
152
153 roots
154 }
155 }
156
157 fn description(&self) -> String {
158 self.description.clone()
159 }
160
161 fn estimated_size(&self) -> usize {
162 unsafe {
163 if self.vars_ptr.is_null() {
164 return 0;
165 }
166
167 let vars = &*self.vars_ptr;
168 vars.len() * std::mem::size_of::<Value>()
169 }
170 }
171
172 fn is_active(&self) -> bool {
173 !self.vars_ptr.is_null()
174 }
175}
176
177impl VariableArrayRoot {
178 #[allow(clippy::only_used_in_recursion)]
179 fn collect_value_roots(&self, value: &Value, _roots: &mut Vec<GcPtr<Value>>) {
180 if let Value::Cell(cells) = value {
181 for cell_value in &cells.data {
182 self.collect_value_roots(cell_value, _roots);
183 }
184 }
185 }
186}
187
188pub struct GlobalRoot {
190 values: Vec<Value>,
192 description: String,
193}
194
195impl GlobalRoot {
196 pub fn new(values: Vec<Value>, description: String) -> Self {
197 Self {
198 values,
199 description,
200 }
201 }
202
203 pub fn add_value(&mut self, value: Value) {
204 self.values.push(value);
205 }
206
207 pub fn remove_value(&mut self, index: usize) -> Option<Value> {
208 if index < self.values.len() {
209 Some(self.values.remove(index))
210 } else {
211 None
212 }
213 }
214}
215
216impl GcRoot for GlobalRoot {
217 fn scan(&self) -> Vec<GcPtr<Value>> {
218 let mut roots = Vec::new();
219 for value in &self.values {
220 self.collect_value_roots(value, &mut roots);
221 }
222 roots
223 }
224
225 fn description(&self) -> String {
226 self.description.clone()
227 }
228
229 fn estimated_size(&self) -> usize {
230 self.values.len() * std::mem::size_of::<Value>()
231 }
232}
233
234impl GlobalRoot {
235 #[allow(clippy::only_used_in_recursion)]
236 fn collect_value_roots(&self, value: &Value, _roots: &mut Vec<GcPtr<Value>>) {
237 if let Value::Cell(cells) = value {
238 for cell_value in &cells.data {
239 self.collect_value_roots(cell_value, _roots);
240 }
241 }
242 }
243}
244
245pub struct RootScanner {
247 roots: parking_lot::RwLock<HashMap<RootId, Box<dyn GcRoot>>>,
249
250 next_id: AtomicUsize,
252
253 scans_performed: AtomicUsize,
255 total_roots_found: AtomicUsize,
256}
257
258impl RootScanner {
259 pub fn new() -> Self {
260 Self {
261 roots: parking_lot::RwLock::new(HashMap::new()),
262 next_id: AtomicUsize::new(1),
263 scans_performed: AtomicUsize::new(0),
264 total_roots_found: AtomicUsize::new(0),
265 }
266 }
267
268 pub fn register_root(&self, root: Box<dyn GcRoot>) -> Result<RootId> {
270 let id = RootId(self.next_id.fetch_add(1, Ordering::Relaxed));
271
272 log::debug!("Registering GC root {}: {}", id.0, root.description());
273
274 let mut roots = self.roots.write();
275 roots.insert(id, root);
276
277 Ok(id)
278 }
279
280 pub fn unregister_root(&self, root_id: RootId) -> Result<()> {
282 log::debug!("Unregistering GC root {}", root_id.0);
283
284 let mut roots = self.roots.write();
285 match roots.remove(&root_id) {
286 Some(_) => Ok(()),
287 None => Err(GcError::RootRegistrationFailed(format!(
288 "Root {} not found",
289 root_id.0
290 ))),
291 }
292 }
293
294 pub fn scan_roots(&self) -> Result<Vec<GcPtr<Value>>> {
296 log::trace!("Starting root scan");
297 let scan_start = std::time::Instant::now();
298
299 let roots = self.roots.read();
300 let mut all_roots = Vec::new();
301 let mut inactive_roots = Vec::new();
302
303 for (&root_id, root) in roots.iter() {
304 if !root.is_active() {
305 inactive_roots.push(root_id);
306 continue;
307 }
308
309 log::trace!("Scanning root {}: {}", root_id.0, root.description());
310 let root_objects = root.scan();
311 log::trace!(
312 "Found {} objects from root {}",
313 root_objects.len(),
314 root_id.0
315 );
316
317 all_roots.extend(root_objects);
318 }
319
320 drop(roots);
321
322 if !inactive_roots.is_empty() {
324 let mut roots = self.roots.write();
325 for root_id in inactive_roots {
326 log::debug!("Removing inactive root {}", root_id.0);
327 roots.remove(&root_id);
328 }
329 }
330
331 self.scans_performed.fetch_add(1, Ordering::Relaxed);
333 self.total_roots_found
334 .fetch_add(all_roots.len(), Ordering::Relaxed);
335
336 let scan_duration = scan_start.elapsed();
337 log::debug!(
338 "Root scan completed: {} roots found in {:?}",
339 all_roots.len(),
340 scan_duration
341 );
342
343 Ok(all_roots)
344 }
345
346 pub fn root_info(&self) -> Vec<RootInfo> {
348 let roots = self.roots.read();
349 roots
350 .iter()
351 .map(|(&id, root)| RootInfo {
352 id,
353 description: root.description(),
354 estimated_size: root.estimated_size(),
355 is_active: root.is_active(),
356 })
357 .collect()
358 }
359
360 pub fn stats(&self) -> RootScannerStats {
362 let roots = self.roots.read();
363 RootScannerStats {
364 registered_roots: roots.len(),
365 scans_performed: self.scans_performed.load(Ordering::Relaxed),
366 total_roots_found: self.total_roots_found.load(Ordering::Relaxed),
367 average_roots_per_scan: if self.scans_performed.load(Ordering::Relaxed) > 0 {
368 self.total_roots_found.load(Ordering::Relaxed) as f64
369 / self.scans_performed.load(Ordering::Relaxed) as f64
370 } else {
371 0.0
372 },
373 }
374 }
375
376 pub fn cleanup_inactive_roots(&self) -> usize {
378 let mut roots = self.roots.write();
379 let initial_count = roots.len();
380
381 roots.retain(|_, root| root.is_active());
382
383 let removed_count = initial_count - roots.len();
384 if removed_count > 0 {
385 log::debug!("Cleaned up {removed_count} inactive roots");
386 }
387
388 removed_count
389 }
390}
391
392impl Default for RootScanner {
393 fn default() -> Self {
394 Self::new()
395 }
396}
397
398#[derive(Debug, Clone)]
400pub struct RootInfo {
401 pub id: RootId,
402 pub description: String,
403 pub estimated_size: usize,
404 pub is_active: bool,
405}
406
407#[derive(Debug, Clone)]
409pub struct RootScannerStats {
410 pub registered_roots: usize,
411 pub scans_performed: usize,
412 pub total_roots_found: usize,
413 pub average_roots_per_scan: f64,
414}
415
416#[cfg(test)]
417mod tests {
418 use super::*;
419
420 #[test]
421 fn test_global_root() {
422 let values = vec![Value::Num(42.0), Value::String("test".to_string())];
423
424 let root = GlobalRoot::new(values, "test global".to_string());
425 assert_eq!(root.description(), "test global");
426 assert!(root.is_active());
427
428 let scanned = root.scan();
429 assert_eq!(scanned.len(), 0);
431 }
432
433 #[test]
434 fn test_root_scanner() {
435 let scanner = RootScanner::new();
436
437 let root = Box::new(GlobalRoot::new(vec![Value::Num(1.0)], "test".to_string()));
438
439 let root_id = scanner.register_root(root).expect("should register");
440
441 let roots = scanner.scan_roots().expect("should scan");
442 assert_eq!(roots.len(), 0); let info = scanner.root_info();
445 assert_eq!(info.len(), 1);
446 assert_eq!(info[0].description, "test");
447
448 scanner.unregister_root(root_id).expect("should unregister");
449
450 let info = scanner.root_info();
451 assert_eq!(info.len(), 0);
452 }
453
454 #[test]
455 fn test_stack_root() {
456 let stack = vec![Value::Num(1.0), Value::Bool(true)];
457
458 let root = unsafe { StackRoot::new(&stack as *const _, "test stack".to_string()) };
459
460 assert_eq!(root.description(), "test stack");
461 assert!(root.is_active());
462 assert!(root.estimated_size() > 0);
463
464 let scanned = root.scan();
465 assert_eq!(scanned.len(), 0); }
467
468 #[test]
469 fn test_variable_array_root() {
470 let vars = vec![Value::Num(42.0), Value::String("test".to_string())];
471
472 let root = unsafe { VariableArrayRoot::new(&vars as *const _, "test vars".to_string()) };
473
474 assert_eq!(root.description(), "test vars");
475 assert!(root.is_active());
476 assert!(root.estimated_size() > 0);
477 }
478
479 #[test]
480 fn test_root_scanner_stats() {
481 let scanner = RootScanner::new();
482
483 let initial_stats = scanner.stats();
484 assert_eq!(initial_stats.registered_roots, 0);
485 assert_eq!(initial_stats.scans_performed, 0);
486
487 let _root_id = scanner
488 .register_root(Box::new(GlobalRoot::new(vec![], "test".to_string())))
489 .expect("should register");
490
491 let _roots = scanner.scan_roots().expect("should scan");
492
493 let stats = scanner.stats();
494 assert_eq!(stats.registered_roots, 1);
495 assert_eq!(stats.scans_performed, 1);
496 }
497}