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