Skip to content

Commit 5d8140d

Browse files
author
Lukas Markeffsky
committed
add test for panicking drop in Box/Rc/Arc
1 parent c667683 commit 5d8140d

File tree

3 files changed

+113
-3
lines changed

3 files changed

+113
-3
lines changed

alloc/tests/arc.rs

+38-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::any::Any;
2-
use std::cell::RefCell;
2+
use std::cell::{Cell, RefCell};
33
use std::iter::TrustedLen;
44
use std::mem;
55
use std::sync::{Arc, Weak};
@@ -89,7 +89,7 @@ fn eq() {
8989

9090
// The test code below is identical to that in `rc.rs`.
9191
// For better maintainability we therefore define this type alias.
92-
type Rc<T> = Arc<T>;
92+
type Rc<T, A = std::alloc::Global> = Arc<T, A>;
9393

9494
const SHARED_ITER_MAX: u16 = 100;
9595

@@ -210,6 +210,42 @@ fn weak_may_dangle() {
210210
// borrow might be used here, when `val` is dropped and runs the `Drop` code for type `std::sync::Weak`
211211
}
212212

213+
/// Test that a panic from a destructor does not leak the allocation.
214+
#[test]
215+
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
216+
fn panic_no_leak() {
217+
use std::alloc::{AllocError, Allocator, Global, Layout};
218+
use std::panic::{AssertUnwindSafe, catch_unwind};
219+
use std::ptr::NonNull;
220+
221+
struct AllocCount(Cell<i32>);
222+
unsafe impl Allocator for AllocCount {
223+
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
224+
self.0.set(self.0.get() + 1);
225+
Global.allocate(layout)
226+
}
227+
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
228+
self.0.set(self.0.get() - 1);
229+
unsafe { Global.deallocate(ptr, layout) }
230+
}
231+
}
232+
233+
struct PanicOnDrop;
234+
impl Drop for PanicOnDrop {
235+
fn drop(&mut self) {
236+
panic!("PanicOnDrop");
237+
}
238+
}
239+
240+
let alloc = AllocCount(Cell::new(0));
241+
let rc = Rc::new_in(PanicOnDrop, &alloc);
242+
assert_eq!(alloc.0.get(), 1);
243+
244+
let panic_message = catch_unwind(AssertUnwindSafe(|| drop(rc))).unwrap_err();
245+
assert_eq!(*panic_message.downcast_ref::<&'static str>().unwrap(), "PanicOnDrop");
246+
assert_eq!(alloc.0.get(), 0);
247+
}
248+
213249
/// This is similar to the doc-test for `Arc::make_mut()`, but on an unsized type (slice).
214250
#[test]
215251
fn make_mut_unsized() {

alloc/tests/boxed.rs

+38
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,44 @@ fn box_deref_lval() {
5959
assert_eq!(x.get(), 1000);
6060
}
6161

62+
/// Test that a panic from a destructor does not leak the allocation.
63+
#[test]
64+
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
65+
fn panic_no_leak() {
66+
use std::alloc::{AllocError, Allocator, Global, Layout};
67+
use std::panic::{AssertUnwindSafe, catch_unwind};
68+
use std::ptr::NonNull;
69+
70+
struct AllocCount(Cell<i32>);
71+
unsafe impl Allocator for AllocCount {
72+
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
73+
self.0.set(self.0.get() + 1);
74+
Global.allocate(layout)
75+
}
76+
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
77+
self.0.set(self.0.get() - 1);
78+
unsafe { Global.deallocate(ptr, layout) }
79+
}
80+
}
81+
82+
struct PanicOnDrop {
83+
_data: u8,
84+
}
85+
impl Drop for PanicOnDrop {
86+
fn drop(&mut self) {
87+
panic!("PanicOnDrop");
88+
}
89+
}
90+
91+
let alloc = AllocCount(Cell::new(0));
92+
let b = Box::new_in(PanicOnDrop { _data: 42 }, &alloc);
93+
assert_eq!(alloc.0.get(), 1);
94+
95+
let panic_message = catch_unwind(AssertUnwindSafe(|| drop(b))).unwrap_err();
96+
assert_eq!(*panic_message.downcast_ref::<&'static str>().unwrap(), "PanicOnDrop");
97+
assert_eq!(alloc.0.get(), 0);
98+
}
99+
62100
#[allow(unused)]
63101
pub struct ConstAllocator;
64102

alloc/tests/rc.rs

+37-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::any::Any;
2-
use std::cell::RefCell;
2+
use std::cell::{Cell, RefCell};
33
use std::iter::TrustedLen;
44
use std::mem;
55
use std::rc::{Rc, Weak};
@@ -206,6 +206,42 @@ fn weak_may_dangle() {
206206
// borrow might be used here, when `val` is dropped and runs the `Drop` code for type `std::rc::Weak`
207207
}
208208

209+
/// Test that a panic from a destructor does not leak the allocation.
210+
#[test]
211+
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
212+
fn panic_no_leak() {
213+
use std::alloc::{AllocError, Allocator, Global, Layout};
214+
use std::panic::{AssertUnwindSafe, catch_unwind};
215+
use std::ptr::NonNull;
216+
217+
struct AllocCount(Cell<i32>);
218+
unsafe impl Allocator for AllocCount {
219+
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
220+
self.0.set(self.0.get() + 1);
221+
Global.allocate(layout)
222+
}
223+
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
224+
self.0.set(self.0.get() - 1);
225+
unsafe { Global.deallocate(ptr, layout) }
226+
}
227+
}
228+
229+
struct PanicOnDrop;
230+
impl Drop for PanicOnDrop {
231+
fn drop(&mut self) {
232+
panic!("PanicOnDrop");
233+
}
234+
}
235+
236+
let alloc = AllocCount(Cell::new(0));
237+
let rc = Rc::new_in(PanicOnDrop, &alloc);
238+
assert_eq!(alloc.0.get(), 1);
239+
240+
let panic_message = catch_unwind(AssertUnwindSafe(|| drop(rc))).unwrap_err();
241+
assert_eq!(*panic_message.downcast_ref::<&'static str>().unwrap(), "PanicOnDrop");
242+
assert_eq!(alloc.0.get(), 0);
243+
}
244+
209245
#[allow(unused)]
210246
mod pin_coerce_unsized {
211247
use alloc::rc::{Rc, UniqueRc};

0 commit comments

Comments
 (0)