tracing_rc/rc/
collector.rs1use std::{
2 cell::RefCell,
3 collections::HashMap,
4 num::NonZeroUsize,
5 rc::{
6 Rc,
7 Weak,
8 },
9};
10
11use indexmap::{
12 IndexMap,
13 IndexSet,
14};
15use petgraph::{
16 csr::{
17 Csr,
18 NodeIndex,
19 },
20 Directed,
21};
22
23use crate::{
24 impl_node,
25 rc::{
26 trace::Trace,
27 Gc,
28 Inner,
29 Status,
30 },
31 CollectOptions,
32 CollectionType,
33};
34
35impl_node!(WeakNode { inner_ptr: Weak<Inner<dyn Trace>> }, upgrade(ptr) => Weak::upgrade(ptr));
36impl_node!(StrongNode { inner_ptr: Rc<Inner<dyn Trace>> }, upgrade(ptr) => ptr);
37
38impl TryFrom<WeakNode> for StrongNode {
39 type Error = ();
40
41 fn try_from(weak: WeakNode) -> Result<Self, Self::Error> {
42 weak.upgrade()
43 .map(|strong| StrongNode { inner_ptr: strong })
44 .ok_or(())
45 }
46}
47
48pub struct GcVisitor<'cycle> {
51 visitor: &'cycle mut dyn FnMut(Rc<Inner<dyn Trace>>),
52}
53
54impl GcVisitor<'_> {
55 pub fn visit_node<T: Trace + 'static>(&mut self, node: &Gc<T>) {
57 (self.visitor)(node.ptr.clone() as Rc<_>);
58 }
59}
60
61type GraphIndex = NodeIndex<usize>;
62
63type ConnectivityGraph = Csr<(), (), Directed, GraphIndex>;
64
65type TracedNodeList = IndexMap<StrongNode, NonZeroUsize>;
66
67thread_local! { pub(super) static OLD_GEN: RefCell<IndexSet<WeakNode>> = RefCell::new(IndexSet::default()) }
68thread_local! { pub(super) static YOUNG_GEN: RefCell<IndexMap<WeakNode, usize>> = RefCell::new(IndexMap::default()) }
69
70#[doc(hidden)]
71pub fn count_roots() -> usize {
72 OLD_GEN.with(|old| old.borrow().len()) + YOUNG_GEN.with(|young| young.borrow().len())
73}
74
75pub fn collect_full() {
77 collect_with_options(CollectOptions::TRACE_AND_COLLECT_ALL);
78}
79
80pub fn collect() {
82 collect_with_options(CollectOptions::default());
83}
84
85pub fn collect_with_options(options: CollectOptions) {
87 collect_new_gen(options);
88 if options.kind != CollectionType::YoungOnly {
89 collect_old_gen();
90 }
91}
92
93fn collect_new_gen(options: CollectOptions) {
94 OLD_GEN.with(|old_gen| {
95 let mut old_gen = old_gen.borrow_mut();
96 YOUNG_GEN.with(|gen| {
97 gen.borrow_mut().retain(|node, generation| {
98 let strong_node = if let Some(node) = node.upgrade() {
99 node
100 } else {
101 return false;
102 };
103 if strong_node.status.get() != Status::RecentlyDecremented {
104 return false;
109 }
110
111 if *generation < options.old_gen_threshold {
112 *generation += 1;
113 return true;
114 }
115
116 old_gen.insert(node.clone());
117 false
118 });
119 });
120 });
121}
122
123fn collect_old_gen() {
124 let mut traced_nodes: TracedNodeList = OLD_GEN.with(|old_gen| {
125 old_gen
126 .borrow_mut()
127 .drain(..)
128 .filter_map(|weak| {
129 weak.try_into()
130 .ok()
131 .map(|strong| (strong, NonZeroUsize::new(1).unwrap()))
132 })
133 .collect()
134 });
135
136 let mut connectivity_graph = ConnectivityGraph::with_nodes(traced_nodes.len());
137
138 for node_ix in 0..connectivity_graph.node_count() {
142 match traced_nodes.get_index(node_ix).unwrap().0.status.get() {
143 Status::Live | Status::Dead => {
144 }
150 Status::RecentlyDecremented => {
151 trace_children(node_ix, &mut traced_nodes, &mut connectivity_graph);
154 }
155 }
156 }
157
158 let mut live_nodes = vec![];
159 let mut dead_nodes = HashMap::default();
160
161 for (node_ix, (node, refs)) in traced_nodes.into_iter().enumerate() {
162 if Rc::strong_count(&node) > refs.get() {
163 live_nodes.push(node_ix);
164 } else {
165 dead_nodes.insert(node_ix, node);
166 }
167 }
168
169 for node_index in live_nodes {
170 filter_live_node_children(&connectivity_graph, node_index, &mut dead_nodes);
171 }
172
173 for node in dead_nodes.values() {
174 node.status.set(Status::Dead);
175 }
176
177 for (_, node) in dead_nodes {
178 node.drop_data();
179 }
180}
181
182fn trace_children(
183 parent_ix: GraphIndex,
184 traced_nodes: &mut TracedNodeList,
185 connectivity_graph: &mut ConnectivityGraph,
186) {
187 let pin_parent = traced_nodes.get_index(parent_ix).unwrap().0.clone();
188 let parent = if let Ok(parent) = pin_parent.data.try_borrow_mut() {
189 parent
190 } else {
191 return;
201 };
202
203 parent.visit_children(&mut GcVisitor {
204 visitor: &mut |node| {
205 match traced_nodes.entry(node.into()) {
206 indexmap::map::Entry::Occupied(mut seen) => {
207 *seen.get_mut() =
211 NonZeroUsize::new(seen.get().get().saturating_add(1)).unwrap();
212
213 connectivity_graph.add_edge(parent_ix, seen.index(), ());
214 }
215 indexmap::map::Entry::Vacant(unseen) => {
216 let child_ix = connectivity_graph.add_node(());
217 debug_assert_eq!(child_ix, unseen.index());
218
219 connectivity_graph.add_edge(parent_ix, child_ix, ());
220
221 unseen.insert(NonZeroUsize::new(2).unwrap());
223 trace_children(child_ix, traced_nodes, connectivity_graph);
224 }
225 };
226 },
227 });
228}
229
230fn filter_live_node_children(
231 graph: &ConnectivityGraph,
232 node: GraphIndex,
233 dead_nodes: &mut HashMap<GraphIndex, StrongNode>,
234) {
235 for child in graph.neighbors_slice(node) {
236 if dead_nodes.remove(child).is_some() {
237 filter_live_node_children(graph, *child, dead_nodes);
238 }
239 }
240}