Avoid stack overflow when dropping RcList

Turn recursion into a loop
This commit is contained in:
Alex Crichton 2017-06-02 17:25:33 -07:00
parent c9fd94a464
commit 19e0fa641d

View file

@ -49,7 +49,6 @@ use std::cmp::Ordering;
use std::collections::{HashSet, HashMap, BinaryHeap, BTreeMap};
use std::iter::FromIterator;
use std::fmt;
use std::mem;
use std::ops::Range;
use std::rc::Rc;
@ -256,28 +255,37 @@ impl<'a> Iterator for DepsNotReplaced<'a> {
}
}
enum RcList<T> {
Data(Rc<(T, RcList<T>)>),
Empty,
struct RcList<T> {
head: Option<Rc<(T, RcList<T>)>>
}
impl<T> RcList<T> {
fn new() -> RcList<T> {
RcList::Empty
RcList { head: None }
}
fn push(&mut self, data: T) {
let node = Rc::new((data, mem::replace(self, RcList::Empty)));
*self = RcList::Data(node);
let node = Rc::new((data, RcList { head: self.head.take() }));
self.head = Some(node);
}
}
// Not derived to avoid `T: Clone`
impl<T> Clone for RcList<T> {
fn clone(&self) -> RcList<T> {
match *self {
RcList::Data(ref d) => RcList::Data(d.clone()),
RcList::Empty => RcList::Empty,
RcList { head: self.head.clone() }
}
}
// Avoid stack overflows on drop by turning recursion into a loop
impl<T> Drop for RcList<T> {
fn drop(&mut self) {
let mut cur = self.head.take();
while let Some(head) = cur {
match Rc::try_unwrap(head) {
Ok((_data, mut next)) => cur = next.head.take(),
Err(_) => break,
}
}
}
}
@ -1085,10 +1093,10 @@ impl<'a> Context<'a> {
fn resolve_replacements(&self) -> HashMap<PackageId, PackageId> {
let mut replacements = HashMap::new();
let mut cur = &self.resolve_replacements;
while let RcList::Data(ref d) = *cur {
let (k, v) = d.0.clone();
while let Some(ref node) = cur.head {
let (k, v) = node.0.clone();
replacements.insert(k, v);
cur = &d.1;
cur = &node.1;
}
return replacements
}
@ -1096,12 +1104,12 @@ impl<'a> Context<'a> {
fn graph(&self) -> Graph<PackageId> {
let mut graph = Graph::new();
let mut cur = &self.resolve_graph;
while let RcList::Data(ref d) = *cur {
match d.0 {
while let Some(ref node) = cur.head {
match node.0 {
GraphNode::Add(ref p) => graph.add(p.clone(), &[]),
GraphNode::Link(ref a, ref b) => graph.link(a.clone(), b.clone()),
}
cur = &d.1;
cur = &node.1;
}
return graph
}