use std::cell::{Cell, RefCell};
use std::mem;
use crate::tests::{assert_state_not_collecting, reset_state};
use crate::{collect_cycles, Cc, Context, Finalize, Trace};
fn register_panicking<T: Trace>(#[allow(unused_variables)] cc: &Cc<T>) { #[cfg(miri)]
extern "Rust" {
fn miri_static_root(ptr: *const u8);
}
#[cfg(miri)]
unsafe {
use core::mem::transmute;
miri_static_root(*transmute::<_, &*const u8>(cc));
}
}
fn panicking_collect_cycles(on_panic: impl FnOnce()) {
struct DropGuard<F>
where
F: FnOnce(),
{
on_panic: Option<F>,
}
impl<F: FnOnce()> Drop for DropGuard<F> {
fn drop(&mut self) {
if let Some(f) = self.on_panic.take() {
f();
}
}
}
let drop_guard = DropGuard {
on_panic: Some(on_panic),
};
collect_cycles();
mem::forget(drop_guard); }
struct Panicking {
trace_counter: Cell<usize>,
panic_on_trace: Cell<bool>,
panic_on_finalize: Cell<bool>,
panic_on_drop: Cell<bool>,
cc: RefCell<Option<Cc<Panicking>>>,
}
unsafe impl Trace for Panicking {
fn trace(&self, ctx: &mut Context<'_>) {
let counter = self.trace_counter.get();
if self.panic_on_trace.get() {
if counter == 0 {
panic!("Test panic on Trace");
} else {
self.trace_counter.set(counter - 1);
}
}
self.cc.trace(ctx);
}
}
impl Finalize for Panicking {
fn finalize(&self) {
if self.panic_on_finalize.get() {
panic!("Test panic on Finalize");
}
}
}
impl Drop for Panicking {
fn drop(&mut self) {
if self.panic_on_drop.get() {
panic!("Test panic on Drop");
}
}
}
#[test]
#[should_panic = "Test panic on Trace"]
fn test_panicking_tracing_counting() {
reset_state();
{
let cc = Cc::new(Panicking {
trace_counter: Cell::new(0),
panic_on_trace: Cell::new(true),
panic_on_finalize: Cell::new(false),
panic_on_drop: Cell::new(false),
cc: RefCell::new(None),
});
*cc.cc.borrow_mut() = Some(cc.clone());
register_panicking(&cc);
}
panicking_collect_cycles(assert_state_not_collecting);
}
#[test]
#[should_panic = "Test panic on Trace"]
fn test_panicking_tracing_root() {
reset_state();
let _root = {
let root = Cc::new(Panicking {
trace_counter: Cell::new(1),
panic_on_trace: Cell::new(true),
panic_on_finalize: Cell::new(false),
panic_on_drop: Cell::new(false),
cc: RefCell::new(None),
});
*root.cc.borrow_mut() = Some(Cc::new(Panicking {
trace_counter: Cell::new(usize::MAX),
panic_on_trace: Cell::new(false),
panic_on_finalize: Cell::new(false),
panic_on_drop: Cell::new(false),
cc: RefCell::new(Some(root.clone())),
}));
register_panicking(&root);
register_panicking(root.cc.borrow().as_ref().unwrap());
#[allow(clippy::redundant_clone)]
root.clone()
};
panicking_collect_cycles(assert_state_not_collecting);
}
#[test]
#[cfg_attr(feature = "finalization", should_panic = "Test panic on Finalize")]
fn test_panicking_finalize() {
reset_state();
{
let cc = Cc::new(Panicking {
trace_counter: Cell::new(usize::MAX),
panic_on_trace: Cell::new(false),
panic_on_finalize: Cell::new(true),
panic_on_drop: Cell::new(false),
cc: RefCell::new(None),
});
*cc.cc.borrow_mut() = Some(cc.clone());
register_panicking(&cc);
}
panicking_collect_cycles(assert_state_not_collecting);
}
#[test]
#[should_panic = "Test panic on Drop"]
fn test_panicking_drop() {
reset_state();
struct DropPanicking {
cyclic: RefCell<Option<Cc<DropPanicking>>>,
}
unsafe impl Trace for DropPanicking {
fn trace(&self, ctx: &mut Context<'_>) {
self.cyclic.trace(ctx);
}
}
impl Finalize for DropPanicking {
}
impl Drop for DropPanicking {
fn drop(&mut self) {
panic!("Test panic on Drop");
}
}
{
let cc = Cc::new(DropPanicking {
cyclic: RefCell::new(None),
});
*cc.cyclic.borrow_mut() = Some(cc.clone());
register_panicking(&cc);
}
panicking_collect_cycles(assert_state_not_collecting);
}
#[test]
#[should_panic = "Test panic on Drop"]
fn test_panicking_drop_and_finalize() {
reset_state();
{
let cc = Cc::new(Panicking {
trace_counter: Cell::new(usize::MAX),
panic_on_trace: Cell::new(false),
panic_on_finalize: Cell::new(false),
panic_on_drop: Cell::new(true),
cc: RefCell::new(None),
});
*cc.cc.borrow_mut() = Some(cc.clone());
register_panicking(&cc);
}
panicking_collect_cycles(assert_state_not_collecting);
}
#[test]
#[cfg_attr(feature = "finalization", should_panic = "Test panic on Trace")]
fn test_panicking_tracing_drop() {
reset_state();
{
const CELL_VALUE: usize = 1;
let root = Cc::new(Panicking {
trace_counter: Cell::new(CELL_VALUE),
panic_on_trace: Cell::new(true),
panic_on_finalize: Cell::new(false),
panic_on_drop: Cell::new(false),
cc: RefCell::new(None),
});
*root.cc.borrow_mut() = Some(Cc::new(Panicking {
trace_counter: Cell::new(CELL_VALUE),
panic_on_trace: Cell::new(true),
panic_on_finalize: Cell::new(false),
panic_on_drop: Cell::new(false),
cc: RefCell::new(Some(root.clone())),
}));
register_panicking(&root);
register_panicking(root.cc.borrow().as_ref().unwrap());
}
panicking_collect_cycles(assert_state_not_collecting);
}
#[test]
#[cfg_attr(feature = "finalization", should_panic = "Test panic on Trace")]
fn test_panicking_tracing_resurrecting() {
reset_state();
thread_local! {
static RESURRECTED: Cell<Option<Cc<Panicking>>> = Cell::new(None);
}
struct DropGuard;
impl Drop for DropGuard {
fn drop(&mut self) {
fn reset_panicking(panicking: &Panicking) {
panicking.panic_on_trace.set(false);
panicking.panic_on_finalize.set(false);
panicking.panic_on_drop.set(false);
}
if let Some(replaced) = RESURRECTED.with(|cell| cell.replace(None)) {
reset_panicking(&replaced);
reset_panicking(replaced.cc.borrow().as_ref().unwrap());
}
collect_cycles(); }
}
let _drop_guard = DropGuard;
struct Resurrecter {
panicking: Cc<Panicking>,
cyclic: RefCell<Option<Cc<Resurrecter>>>,
}
unsafe impl Trace for Resurrecter {
fn trace(&self, ctx: &mut Context<'_>) {
self.panicking.trace(ctx);
self.cyclic.trace(ctx);
}
}
impl Finalize for Resurrecter {
fn finalize(&self) {
RESURRECTED.with(|res| res.set(Some(self.panicking.clone())));
}
}
{
let a = Cc::new(Resurrecter {
panicking: Cc::new(Panicking {
trace_counter: Cell::new(2),
panic_on_trace: Cell::new(true),
panic_on_finalize: Cell::new(false),
panic_on_drop: Cell::new(false),
cc: RefCell::new(None),
}),
cyclic: RefCell::new(None),
});
*a.panicking.cc.borrow_mut() = Some(Cc::new(Panicking {
trace_counter: Cell::new(2),
panic_on_trace: Cell::new(true),
panic_on_finalize: Cell::new(false),
panic_on_drop: Cell::new(false),
cc: RefCell::new(Some(a.panicking.clone())),
}));
*a.cyclic.borrow_mut() = Some(a.clone());
register_panicking(&a);
register_panicking(&a.panicking);
register_panicking(a.panicking.cc.borrow().as_ref().unwrap());
drop(a);
}
panicking_collect_cycles(assert_state_not_collecting);
}