Skip to content

Commit

Permalink
LinkedList: drop remaining items when drop panics
Browse files Browse the repository at this point in the history
  • Loading branch information
jonas-schievink committed Dec 11, 2019
1 parent 27d6f55 commit 5e32da1
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 1 deletion.
16 changes: 15 additions & 1 deletion src/liballoc/collections/linked_list.rs
Expand Up @@ -808,7 +808,21 @@ impl<T> LinkedList<T> {
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<#[may_dangle] T> Drop for LinkedList<T> {
fn drop(&mut self) {
while let Some(_) = self.pop_front_node() {}
struct DropGuard<'a, T>(&'a mut LinkedList<T>);

impl<'a, T> Drop for DropGuard<'a, T> {
fn drop(&mut self) {
// Continue the same loop we do below. This only runs when a destructor has
// panicked. If another one panics this will abort.
while let Some(_) = self.0.pop_front_node() {}
}
}

while let Some(node) = self.pop_front_node() {
let guard = DropGuard(self);
drop(node);
mem::forget(guard);
}
}
}

Expand Down
107 changes: 107 additions & 0 deletions src/liballoc/tests/linked_list.rs
@@ -1,4 +1,5 @@
use std::collections::LinkedList;
use std::panic::catch_unwind;

#[test]
fn test_basic() {
Expand Down Expand Up @@ -529,3 +530,109 @@ fn drain_filter_complex() {
assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]);
}
}


#[test]
fn test_drop() {
static mut DROPS: i32 = 0;
struct Elem;
impl Drop for Elem {
fn drop(&mut self) {
unsafe {
DROPS += 1;
}
}
}

let mut ring = LinkedList::new();
ring.push_back(Elem);
ring.push_front(Elem);
ring.push_back(Elem);
ring.push_front(Elem);
drop(ring);

assert_eq!(unsafe { DROPS }, 4);
}

#[test]
fn test_drop_with_pop() {
static mut DROPS: i32 = 0;
struct Elem;
impl Drop for Elem {
fn drop(&mut self) {
unsafe {
DROPS += 1;
}
}
}

let mut ring = LinkedList::new();
ring.push_back(Elem);
ring.push_front(Elem);
ring.push_back(Elem);
ring.push_front(Elem);

drop(ring.pop_back());
drop(ring.pop_front());
assert_eq!(unsafe { DROPS }, 2);

drop(ring);
assert_eq!(unsafe { DROPS }, 4);
}

#[test]
fn test_drop_clear() {
static mut DROPS: i32 = 0;
struct Elem;
impl Drop for Elem {
fn drop(&mut self) {
unsafe {
DROPS += 1;
}
}
}

let mut ring = LinkedList::new();
ring.push_back(Elem);
ring.push_front(Elem);
ring.push_back(Elem);
ring.push_front(Elem);
ring.clear();
assert_eq!(unsafe { DROPS }, 4);

drop(ring);
assert_eq!(unsafe { DROPS }, 4);
}

#[test]
fn test_drop_panic() {
static mut DROPS: i32 = 0;

struct D(bool);

impl Drop for D {
fn drop(&mut self) {
unsafe {
DROPS += 1;
}

if self.0 {
panic!("panic in `drop`");
}
}
}

let mut q = LinkedList::new();
q.push_back(D(false));
q.push_back(D(false));
q.push_back(D(false));
q.push_back(D(false));
q.push_back(D(false));
q.push_front(D(false));
q.push_front(D(false));
q.push_front(D(true));

catch_unwind(move || drop(q)).ok();

assert_eq!(unsafe { DROPS }, 8);
}

0 comments on commit 5e32da1

Please sign in to comment.