compose_library/gc/
clean.rs

1use compose_library::gc::trigger::GcEvent;
2use compose_library::{Heap, Trace};
3use slotmap::SecondaryMap;
4use std::collections::VecDeque;
5use std::time::{Duration, Instant};
6
7impl Heap {
8    pub fn maybe_gc(&mut self, root: &impl Trace) -> Option<CleanResult> {
9        if !self.policy.on_event(&GcEvent::MaybeGc, &self.data()) {
10            return None;
11        }
12        Some(self.clean(root))
13    }
14
15    pub fn clean(&mut self, root: &impl Trace) -> CleanResult {
16        let start = Instant::now();
17        let mut worklist = VecDeque::new();
18
19        root.visit_refs(&mut |key| worklist.push_back(key));
20
21        // Mark phase
22        let mut marked = SecondaryMap::new();
23        while let Some(key) = worklist.pop_front() {
24            if marked.insert(key, ()).is_some() {
25                // already marked
26                continue;
27            }
28
29            let Some(item) = self.map.get(key) else {
30                // already cleaned
31                continue;
32            };
33
34            item.visit_refs(&mut |key| worklist.push_back(key));
35        }
36
37        let mut sweeped = 0;
38        let total_allocated = self.map.len();
39
40        // Sweep phase
41        self.map.retain(|key, _| {
42            let keep = marked.contains_key(key);
43            if !keep {
44                sweeped += 1;
45            }
46            keep
47        });
48
49        let gc_duration = start.elapsed();
50
51        let result = CleanResult {
52            sweeped,
53            total_allocated,
54            gc_duration,
55        };
56        
57        self.policy.after_gc(&result, &self.data());
58        
59        result
60    }
61}
62
63#[derive(Debug)]
64pub struct CleanResult {
65    /// The amount of items removed
66    pub sweeped: usize,
67    /// The amount of allocated items, before cleaning
68    pub total_allocated: usize,
69    pub gc_duration: Duration,
70}
71
72impl CleanResult {
73    pub fn marked(&self) -> usize {
74        self.total_allocated - self.sweeped
75    }
76}