Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 61 additions & 18 deletions crossbeam-channel/tests/array.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Tests for the array channel flavor.

use std::any::Any;
use std::cell::Cell;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::thread;
Expand Down Expand Up @@ -692,53 +693,95 @@ fn channel_through_channel() {

#[test]
fn panic_on_drop() {
struct Msg1<'a>(&'a mut bool);
struct Msg1<'a>(&'a Cell<bool>);
impl Drop for Msg1<'_> {
fn drop(&mut self) {
if *self.0 && !std::thread::panicking() {
if self.0.get() && !std::thread::panicking() {
panic!("double drop");
} else {
*self.0 = true;
self.0.set(true);
}
}
}

struct Msg2<'a>(&'a mut bool);
struct Msg2<'a>(&'a Cell<bool>);
impl Drop for Msg2<'_> {
fn drop(&mut self) {
if *self.0 {
if self.0.get() {
panic!("double drop");
} else {
*self.0 = true;
self.0.set(true);
panic!("first drop");
}
}
}

// normal
// normal (sender first)
let (s, r) = bounded(2);
let (mut a, mut b) = (false, false);
s.send(Msg1(&mut a)).unwrap();
s.send(Msg1(&mut b)).unwrap();
let (a, b) = (Cell::new(false), Cell::new(false));
s.send(Msg1(&a)).unwrap();
s.send(Msg1(&b)).unwrap();
drop(s);
assert!(!a.get());
assert!(!b.get());
drop(r);
assert!(a);
assert!(b);
assert!(a.get());
assert!(b.get());

// panic on drop
// normal (receiver first)
let (s, r) = bounded(2);
let (mut a, mut b) = (false, false);
s.send(Msg2(&mut a)).unwrap();
s.send(Msg2(&mut b)).unwrap();
let (a, b) = (Cell::new(false), Cell::new(false));
s.send(Msg1(&a)).unwrap();
s.send(Msg1(&b)).unwrap();
drop(r);
// TODO: should be dropped eagerly: https://github.com/rust-lang/rust/issues/107466
assert!(!a.get());
assert!(!b.get());
drop(s);
assert!(a.get());
assert!(b.get());

// panic on drop (sender first)
let (s, r) = bounded(2);
let (a, b) = (Cell::new(false), Cell::new(false));
s.send(Msg2(&a)).unwrap();
s.send(Msg2(&b)).unwrap();
drop(s);
assert!(!a.get());
assert!(!b.get());
let res = std::panic::catch_unwind(move || {
drop(r);
});
assert_eq!(
*res.unwrap_err().downcast_ref::<&str>().unwrap(),
"first drop"
);
assert!(a);
assert!(a.get());
// Elements after the panicked element will leak.
assert!(!b.get());

// panic on drop (receiver first)
let (s, r) = bounded(2);
let (a, b) = (Cell::new(false), Cell::new(false));
s.send(Msg2(&a)).unwrap();
s.send(Msg2(&b)).unwrap();
let res = std::panic::catch_unwind(move || {
drop(r);
});
// This currently doesn't panic, but it should panic when a fix for
// https://github.com/rust-lang/rust/issues/107466 is implemented.
assert!(res.is_ok());
// TODO: `a` should be dropped eagerly: https://github.com/rust-lang/rust/issues/107466
assert!(!a.get());
assert!(!b.get());
let res = std::panic::catch_unwind(move || {
drop(s);
});
assert_eq!(
*res.unwrap_err().downcast_ref::<&str>().unwrap(),
"first drop"
);
assert!(a.get());
// Elements after the panicked element will leak.
assert!(!b);
assert!(!b.get());
}
92 changes: 92 additions & 0 deletions crossbeam-channel/tests/list.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Tests for the list channel flavor.

use std::any::Any;
use std::cell::Cell;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::thread;
Expand Down Expand Up @@ -580,3 +581,94 @@ fn channel_through_channel() {
})
.unwrap();
}

#[test]
fn panic_on_drop() {
struct Msg1<'a>(&'a Cell<bool>);
impl Drop for Msg1<'_> {
fn drop(&mut self) {
if self.0.get() && !std::thread::panicking() {
panic!("double drop");
} else {
self.0.set(true);
}
}
}

struct Msg2<'a>(&'a Cell<bool>);
impl Drop for Msg2<'_> {
fn drop(&mut self) {
if self.0.get() {
panic!("double drop");
} else {
self.0.set(true);
panic!("first drop");
}
}
}

// normal (sender first)
let (s, r) = unbounded();
let (a, b) = (Cell::new(false), Cell::new(false));
s.send(Msg1(&a)).unwrap();
s.send(Msg1(&b)).unwrap();
drop(s);
assert!(!a.get());
assert!(!b.get());
drop(r);
assert!(a.get());
assert!(b.get());

// normal (receiver first)
let (s, r) = unbounded();
let (a, b) = (Cell::new(false), Cell::new(false));
s.send(Msg1(&a)).unwrap();
s.send(Msg1(&b)).unwrap();
drop(r);
// When the receiver is dropped, messages are dropped eagerly.
assert!(a.get());
assert!(b.get());
drop(s);
assert!(a.get());
assert!(b.get());

// panic on drop (sender first)
let (s, r) = unbounded();
let (a, b) = (Cell::new(false), Cell::new(false));
s.send(Msg2(&a)).unwrap();
s.send(Msg2(&b)).unwrap();
drop(s);
assert!(!a.get());
assert!(!b.get());
let res = std::panic::catch_unwind(move || {
drop(r);
});
assert_eq!(
*res.unwrap_err().downcast_ref::<&str>().unwrap(),
"first drop"
);
assert!(a.get());
// Elements after the panicked element will leak.
assert!(!b.get());

// panic on drop (receiver first)
let (s, r) = unbounded();
let (a, b) = (Cell::new(false), Cell::new(false));
s.send(Msg2(&a)).unwrap();
s.send(Msg2(&b)).unwrap();
let res = std::panic::catch_unwind(move || {
drop(r);
});
assert_eq!(
*res.unwrap_err().downcast_ref::<&str>().unwrap(),
"first drop"
);
// When the receiver is dropped, messages are dropped eagerly.
assert!(a.get());
// Elements after the panicked element will leak.
assert!(!b.get());
drop(s);
assert!(a.get());
// Elements after the panicked element will leak.
assert!(!b.get());
}