cargo/util/dependency_queue.rs
1//! A graph-like structure used to represent a set of dependencies and in what
2//! order they should be built.
3//!
4//! This structure is used to store the dependency graph and dynamically update
5//! it to figure out when a dependency should be built.
6//!
7//! Dependencies in this queue are represented as a (node, edge) pair. This is
8//! used to model nodes which produce multiple outputs at different times but
9//! some nodes may only require one of the outputs and can start before the
10//! whole node is finished.
11
12use std::collections::{HashMap, HashSet};
13use std::hash::Hash;
14
15#[derive(Debug)]
16pub struct DependencyQueue<N: Hash + Eq, E: Hash + Eq, V> {
17 /// A list of all known keys to build.
18 ///
19 /// The value of the hash map is list of dependencies which still need to be
20 /// built before the package can be built. Note that the set is dynamically
21 /// updated as more dependencies are built.
22 dep_map: HashMap<N, (HashSet<(N, E)>, V)>,
23
24 /// A reverse mapping of a package to all packages that depend on that
25 /// package.
26 ///
27 /// This map is statically known and does not get updated throughout the
28 /// lifecycle of the DependencyQueue.
29 ///
30 /// This is sort of like a `HashMap<(N, E), HashSet<N>>` map, but more
31 /// easily indexable with just an `N`
32 reverse_dep_map: HashMap<N, HashMap<E, HashSet<N>>>,
33
34 /// The total number of packages that are transitively waiting on this package
35 priority: HashMap<N, usize>,
36}
37
38impl<N: Hash + Eq, E: Hash + Eq, V> Default for DependencyQueue<N, E, V> {
39 fn default() -> DependencyQueue<N, E, V> {
40 DependencyQueue::new()
41 }
42}
43
44impl<N: Hash + Eq, E: Hash + Eq, V> DependencyQueue<N, E, V> {
45 /// Creates a new dependency queue with 0 packages.
46 pub fn new() -> DependencyQueue<N, E, V> {
47 DependencyQueue {
48 dep_map: HashMap::new(),
49 reverse_dep_map: HashMap::new(),
50 priority: HashMap::new(),
51 }
52 }
53}
54
55impl<N: Hash + Eq + Clone, E: Eq + Hash + Clone, V> DependencyQueue<N, E, V> {
56 /// Adds a new ndoe and its dependencies to this queue.
57 ///
58 /// The `key` specified is a new node in the dependency graph, and the node
59 /// depend on all the dependencies iterated by `dependencies`. Each
60 /// dependency is a node/edge pair, where edges can be thought of as
61 /// productions from nodes (aka if it's just `()` it's just waiting for the
62 /// node to finish).
63 ///
64 /// An optional `value` can also be associated with `key` which is reclaimed
65 /// when the node is ready to go.
66 pub fn queue(&mut self, key: N, value: V, dependencies: impl IntoIterator<Item = (N, E)>) {
67 assert!(!self.dep_map.contains_key(&key));
68
69 let mut my_dependencies = HashSet::new();
70 for (dep, edge) in dependencies {
71 my_dependencies.insert((dep.clone(), edge.clone()));
72 self.reverse_dep_map
73 .entry(dep)
74 .or_insert_with(HashMap::new)
75 .entry(edge)
76 .or_insert_with(HashSet::new)
77 .insert(key.clone());
78 }
79 self.dep_map.insert(key, (my_dependencies, value));
80 }
81
82 /// All nodes have been added, calculate some internal metadata and prepare
83 /// for `dequeue`.
84 pub fn queue_finished(&mut self) {
85 let mut out = HashMap::new();
86 for key in self.dep_map.keys() {
87 depth(key, &self.reverse_dep_map, &mut out);
88 }
89 self.priority = out.into_iter().map(|(n, set)| (n, set.len())).collect();
90
91 fn depth<'a, N: Hash + Eq + Clone, E: Hash + Eq + Clone>(
92 key: &N,
93 map: &HashMap<N, HashMap<E, HashSet<N>>>,
94 results: &'a mut HashMap<N, HashSet<N>>,
95 ) -> &'a HashSet<N> {
96 if results.contains_key(key) {
97 let depth = &results[key];
98 assert!(!depth.is_empty(), "cycle in DependencyQueue");
99 return depth;
100 }
101 results.insert(key.clone(), HashSet::new());
102
103 let mut set = HashSet::new();
104 set.insert(key.clone());
105
106 for dep in map
107 .get(key)
108 .into_iter()
109 .flat_map(|it| it.values())
110 .flatten()
111 {
112 set.extend(depth(dep, map, results).iter().cloned())
113 }
114
115 let slot = results.get_mut(key).unwrap();
116 *slot = set;
117 &*slot
118 }
119 }
120
121 /// Dequeues a package that is ready to be built.
122 ///
123 /// A package is ready to be built when it has 0 un-built dependencies. If
124 /// `None` is returned then no packages are ready to be built.
125 pub fn dequeue(&mut self) -> Option<(N, V)> {
126 // Look at all our crates and find everything that's ready to build (no
127 // deps). After we've got that candidate set select the one which has
128 // the maximum priority in the dependency graph. This way we should
129 // hopefully keep CPUs hottest the longest by ensuring that long
130 // dependency chains are scheduled early on in the build process and the
131 // leafs higher in the tree can fill in the cracks later.
132 //
133 // TODO: it'd be best here to throw in a heuristic of crate size as
134 // well. For example how long did this crate historically take to
135 // compile? How large is its source code? etc.
136 let next = self
137 .dep_map
138 .iter()
139 .filter(|(_, (deps, _))| deps.is_empty())
140 .map(|(key, _)| key.clone())
141 .max_by_key(|k| self.priority[k]);
142 let key = match next {
143 Some(key) => key,
144 None => return None,
145 };
146 let (_, data) = self.dep_map.remove(&key).unwrap();
147 Some((key, data))
148 }
149
150 /// Returns `true` if there are remaining packages to be built.
151 pub fn is_empty(&self) -> bool {
152 self.dep_map.is_empty()
153 }
154
155 /// Returns the number of remaining packages to be built.
156 pub fn len(&self) -> usize {
157 self.dep_map.len()
158 }
159
160 /// Indicate that something has finished.
161 ///
162 /// Calling this function indicates that the `node` has produced `edge`. All
163 /// remaining work items which only depend on this node/edge pair are now
164 /// candidates to start their job.
165 ///
166 /// Returns the nodes that are now allowed to be dequeued as a result of
167 /// finishing this node.
168 pub fn finish(&mut self, node: &N, edge: &E) -> Vec<&N> {
169 // hashset<Node>
170 let reverse_deps = self.reverse_dep_map.get(node).and_then(|map| map.get(edge));
171 let reverse_deps = match reverse_deps {
172 Some(deps) => deps,
173 None => return Vec::new(),
174 };
175 let key = (node.clone(), edge.clone());
176 let mut result = Vec::new();
177 for dep in reverse_deps.iter() {
178 let edges = &mut self.dep_map.get_mut(dep).unwrap().0;
179 assert!(edges.remove(&key));
180 if edges.is_empty() {
181 result.push(dep);
182 }
183 }
184 result
185 }
186}
187
188#[cfg(test)]
189mod test {
190 use super::DependencyQueue;
191
192 #[test]
193 fn deep_first() {
194 let mut q = DependencyQueue::new();
195
196 q.queue(1, (), vec![]);
197 q.queue(2, (), vec![(1, ())]);
198 q.queue(3, (), vec![]);
199 q.queue(4, (), vec![(2, ()), (3, ())]);
200 q.queue(5, (), vec![(4, ()), (3, ())]);
201 q.queue_finished();
202
203 assert_eq!(q.dequeue(), Some((1, ())));
204 assert_eq!(q.dequeue(), Some((3, ())));
205 assert_eq!(q.dequeue(), None);
206 q.finish(&3, &());
207 assert_eq!(q.dequeue(), None);
208 q.finish(&1, &());
209 assert_eq!(q.dequeue(), Some((2, ())));
210 assert_eq!(q.dequeue(), None);
211 q.finish(&2, &());
212 assert_eq!(q.dequeue(), Some((4, ())));
213 assert_eq!(q.dequeue(), None);
214 q.finish(&4, &());
215 assert_eq!(q.dequeue(), Some((5, ())));
216 }
217}