Skip to main content

reify_graph/
arc.rs

1//! Arc-pointer graph reification, parallel to the [`Rc<RefCell<T>>`](std::rc::Rc) API.
2//!
3//! Identical semantics to the top-level [`crate::reify_graph`] /
4//! [`crate::reflect_graph`] pair, but for [`Arc<Mutex<T>>`](std::sync::Arc)
5//! graphs. [`NodeId`] is shared between the two APIs.
6
7use crate::NodeId;
8use std::collections::HashMap;
9use std::sync::{Arc, Mutex};
10
11/// Extract a [`NodeId`] from an `Arc<Mutex<T>>` using pointer identity.
12///
13/// # Examples
14///
15/// ```
16/// use reify_graph::arc::node_id_of_arc;
17/// use std::sync::{Arc, Mutex};
18///
19/// let a = Arc::new(Mutex::new(42));
20/// let b = a.clone();
21/// assert_eq!(node_id_of_arc(&a), node_id_of_arc(&b));
22/// ```
23pub fn node_id_of_arc<T>(arc: &Arc<Mutex<T>>) -> NodeId {
24    NodeId(Arc::as_ptr(arc) as *const () as usize)
25}
26
27/// Collect all reachable nodes from `root`, detecting shared pointers.
28///
29/// # Examples
30///
31/// ```
32/// use reify_graph::arc::collect_nodes_arc;
33/// use std::sync::{Arc, Mutex};
34///
35/// #[derive(Clone)]
36/// struct Node { children: Vec<Arc<Mutex<Node>>> }
37///
38/// let leaf = Arc::new(Mutex::new(Node { children: vec![] }));
39/// let root = Arc::new(Mutex::new(Node { children: vec![leaf.clone()] }));
40///
41/// let nodes = collect_nodes_arc(&root, &|n: &Node| n.children.clone());
42/// assert_eq!(nodes.len(), 2);
43/// ```
44pub fn collect_nodes_arc<T, F>(root: &Arc<Mutex<T>>, children: &F) -> HashMap<NodeId, Arc<Mutex<T>>>
45where
46    F: Fn(&T) -> Vec<Arc<Mutex<T>>>,
47{
48    let mut visited = HashMap::new();
49    collect_inner(root, children, &mut visited);
50    visited
51}
52
53fn collect_inner<T, F>(
54    node: &Arc<Mutex<T>>,
55    children: &F,
56    visited: &mut HashMap<NodeId, Arc<Mutex<T>>>,
57) where
58    F: Fn(&T) -> Vec<Arc<Mutex<T>>>,
59{
60    let id = node_id_of_arc(node);
61    if visited.contains_key(&id) {
62        return;
63    }
64    visited.insert(id, node.clone());
65
66    let kids = children(&node.lock().expect("mutex should not be poisoned"));
67    for kid in &kids {
68        collect_inner(kid, children, visited);
69    }
70}
71
72/// Reify an `Arc<Mutex<T>>` graph into a [`crate::ReifiedGraph`].
73///
74/// # Examples
75///
76/// ```
77/// use reify_graph::arc::reify_graph_arc;
78/// use std::sync::{Arc, Mutex};
79///
80/// #[derive(Clone, Debug)]
81/// struct Node {
82///     value: i32,
83///     children: Vec<Arc<Mutex<Node>>>,
84/// }
85///
86/// let leaf = Arc::new(Mutex::new(Node { value: 1, children: vec![] }));
87/// let root = Arc::new(Mutex::new(Node {
88///     value: 0,
89///     children: vec![leaf.clone()],
90/// }));
91///
92/// let graph = reify_graph_arc(root, |n| n.children.clone());
93/// assert_eq!(graph.nodes.len(), 2);
94/// assert_eq!(graph.edges.len(), 1);
95/// ```
96pub fn reify_graph_arc<T, F>(root: Arc<Mutex<T>>, children: F) -> crate::ReifiedGraph<T>
97where
98    F: Fn(&T) -> Vec<Arc<Mutex<T>>>,
99    T: Clone,
100{
101    let all_nodes = collect_nodes_arc(&root, &children);
102    let root_id = node_id_of_arc(&root);
103
104    let mut nodes = Vec::with_capacity(all_nodes.len());
105    let mut edges = Vec::new();
106
107    for (id, arc) in &all_nodes {
108        let guard = arc.lock().expect("mutex should not be poisoned");
109        nodes.push((*id, guard.clone()));
110
111        for kid in children(&guard) {
112            let kid_id = node_id_of_arc(&kid);
113            edges.push((*id, kid_id));
114        }
115    }
116
117    crate::ReifiedGraph {
118        nodes,
119        edges,
120        root: root_id,
121    }
122}
123
124/// Reconstruct an `Arc<Mutex<T>>` graph from a [`crate::ReifiedGraph`].
125///
126/// # Examples
127///
128/// ```
129/// use reify_graph::arc::{reify_graph_arc, reflect_graph_arc};
130/// use std::sync::{Arc, Mutex};
131///
132/// #[derive(Clone, Debug)]
133/// struct Node {
134///     value: i32,
135///     children: Vec<Arc<Mutex<Node>>>,
136/// }
137///
138/// let leaf = Arc::new(Mutex::new(Node { value: 1, children: vec![] }));
139/// let root = Arc::new(Mutex::new(Node { value: 0, children: vec![leaf] }));
140///
141/// let graph = reify_graph_arc(root, |n| n.children.clone());
142/// let rebuilt = reflect_graph_arc(graph, |n, kids| n.children = kids);
143/// assert_eq!(rebuilt.lock().unwrap().value, 0);
144/// ```
145pub fn reflect_graph_arc<T, F>(graph: crate::ReifiedGraph<T>, set_children: F) -> Arc<Mutex<T>>
146where
147    F: Fn(&mut T, Vec<Arc<Mutex<T>>>),
148    T: Clone,
149{
150    let mut arc_map: HashMap<NodeId, Arc<Mutex<T>>> = HashMap::new();
151    for (id, data) in &graph.nodes {
152        arc_map.insert(*id, Arc::new(Mutex::new(data.clone())));
153    }
154
155    let mut adj: HashMap<NodeId, Vec<NodeId>> = HashMap::new();
156    for (from, to) in &graph.edges {
157        adj.entry(*from).or_default().push(*to);
158    }
159
160    for (id, arc) in &arc_map {
161        if let Some(child_ids) = adj.get(id) {
162            let kids: Vec<Arc<Mutex<T>>> =
163                child_ids.iter().map(|cid| arc_map[cid].clone()).collect();
164            set_children(&mut arc.lock().expect("mutex should not be poisoned"), kids);
165        }
166    }
167
168    arc_map[&graph.root].clone()
169}
170
171#[cfg(test)]
172mod tests {
173    use super::*;
174
175    #[derive(Clone, Debug)]
176    struct Node {
177        #[allow(dead_code)]
178        value: i32,
179        children: Vec<Arc<Mutex<Node>>>,
180    }
181
182    fn children_of(n: &Node) -> Vec<Arc<Mutex<Node>>> {
183        n.children.clone()
184    }
185
186    fn set_children_of(n: &mut Node, kids: Vec<Arc<Mutex<Node>>>) {
187        n.children = kids;
188    }
189
190    #[test]
191    fn arc_node_id_identity() {
192        let a = Arc::new(Mutex::new(Node {
193            value: 1,
194            children: vec![],
195        }));
196        let b = a.clone();
197        assert_eq!(node_id_of_arc(&a), node_id_of_arc(&b));
198
199        let c = Arc::new(Mutex::new(Node {
200            value: 1,
201            children: vec![],
202        }));
203        assert_ne!(node_id_of_arc(&a), node_id_of_arc(&c));
204    }
205
206    #[test]
207    fn arc_round_trip_preserves_sharing() {
208        let shared = Arc::new(Mutex::new(Node {
209            value: 42,
210            children: vec![],
211        }));
212        let root = Arc::new(Mutex::new(Node {
213            value: 0,
214            children: vec![shared.clone(), shared.clone()],
215        }));
216
217        let graph = reify_graph_arc(root, children_of);
218        let rebuilt = reflect_graph_arc(graph, set_children_of);
219
220        let children = rebuilt.lock().unwrap().children.clone();
221        assert_eq!(children.len(), 2);
222        assert_eq!(
223            Arc::as_ptr(&children[0]) as usize,
224            Arc::as_ptr(&children[1]) as usize
225        );
226    }
227
228    #[test]
229    fn arc_cycle_detection() {
230        let a = Arc::new(Mutex::new(Node {
231            value: 0,
232            children: vec![],
233        }));
234        let b = Arc::new(Mutex::new(Node {
235            value: 1,
236            children: vec![a.clone()],
237        }));
238        a.lock().unwrap().children.push(b.clone());
239
240        let nodes = collect_nodes_arc(&a, &children_of);
241        assert_eq!(nodes.len(), 2);
242    }
243}