about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-08-04 19:52:57 -0700
committerbors <bors@rust-lang.org>2013-08-04 19:52:57 -0700
commit6c12ca3ac2d0db3f9636e16fc5a507a12f313e7f (patch)
tree2d75d8ba37f5d36072016bc26691239a9649fa8f /src
parentdc5b0b94101a1fb9094b62ed1bb1b0be3b073fcc (diff)
parent4898a0de04600afefcb095b55ea0d924f125a892 (diff)
downloadrust-6c12ca3ac2d0db3f9636e16fc5a507a12f313e7f.tar.gz
rust-6c12ca3ac2d0db3f9636e16fc5a507a12f313e7f.zip
auto merge of #8297 : brson/rust/dlist-dtor, r=brson
The compiler-generated dtor for DList recurses deeply to drop Nodes.
For big lists this can overflow the stack.

This is a problem for the new scheduler, where split stacks are not implemented.

Thanks @blake2-ppc
Diffstat (limited to 'src')
-rw-r--r--src/libextra/dlist.rs42
1 files changed, 38 insertions, 4 deletions
diff --git a/src/libextra/dlist.rs b/src/libextra/dlist.rs
index 4142bdadaf6..75487a44f26 100644
--- a/src/libextra/dlist.rs
+++ b/src/libextra/dlist.rs
@@ -92,6 +92,11 @@ impl<T> Rawlink<T> {
             Some(unsafe { cast::transmute(self.p) })
         }
     }
+
+    /// Return the `Rawlink` and replace with `Rawlink::none()`
+    fn take(&mut self) -> Rawlink<T> {
+        util::replace(self, Rawlink::none())
+    }
 }
 
 impl<T> Clone for Rawlink<T> {
@@ -280,13 +285,16 @@ impl<T> DList<T> {
     /// Add all elements from `other` to the end of the list
     ///
     /// O(1)
-    pub fn append(&mut self, other: DList<T>) {
+    pub fn append(&mut self, mut other: DList<T>) {
         match self.list_tail.resolve() {
             None => *self = other,
             Some(tail) => {
-                match other {
-                    DList{list_head: None, _} => return,
-                    DList{list_head: Some(node), list_tail: o_tail, length: o_length} => {
+                // Carefully empty `other`.
+                let o_tail = other.list_tail.take();
+                let o_length = other.length;
+                match other.list_head.take() {
+                    None => return,
+                    Some(node) => {
                         tail.next = link_with_prev(node, self.list_tail);
                         self.list_tail = o_tail;
                         self.length += o_length;
@@ -404,6 +412,32 @@ impl<T: Ord> DList<T> {
     }
 }
 
+#[unsafe_destructor]
+impl<T> Drop for DList<T> {
+    fn drop(&self) {
+        let mut_self = unsafe {
+            cast::transmute_mut(self)
+        };
+        // Dissolve the dlist in backwards direction
+        // Just dropping the list_head can lead to stack exhaustion
+        // when length is >> 1_000_000
+        let mut tail = mut_self.list_tail;
+        loop {
+            match tail.resolve() {
+                None => break,
+                Some(prev) => {
+                    prev.next.take(); // release ~Node<T>
+                    tail = prev.prev;
+                }
+            }
+        }
+        mut_self.length = 0;
+        mut_self.list_head = None;
+        mut_self.list_tail = Rawlink::none();
+    }
+}
+
+
 impl<'self, A> Iterator<&'self A> for DListIterator<'self, A> {
     #[inline]
     fn next(&mut self) -> Option<&'self A> {