Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

channel: Extend panic_on_drop test #968

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
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());
}