diff --git a/crossbeam-channel/tests/array.rs b/crossbeam-channel/tests/array.rs index 6fd8ffcc6..0dc0421b5 100644 --- a/crossbeam-channel/tests/array.rs +++ b/crossbeam-channel/tests/array.rs @@ -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; @@ -692,45 +693,62 @@ fn channel_through_channel() { #[test] fn panic_on_drop() { - struct Msg1<'a>(&'a mut bool); + struct Msg1<'a>(&'a Cell); 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); 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); }); @@ -738,7 +756,32 @@ fn panic_on_drop() { *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()); } diff --git a/crossbeam-channel/tests/list.rs b/crossbeam-channel/tests/list.rs index ebe6f6f85..0400a7bfd 100644 --- a/crossbeam-channel/tests/list.rs +++ b/crossbeam-channel/tests/list.rs @@ -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; @@ -580,3 +581,94 @@ fn channel_through_channel() { }) .unwrap(); } + +#[test] +fn panic_on_drop() { + struct Msg1<'a>(&'a Cell); + 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); + 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()); +}