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}