diff --git a/src/pin_slab.rs b/src/pin_slab.rs index bb4e32f..87826c5 100644 --- a/src/pin_slab.rs +++ b/src/pin_slab.rs @@ -281,12 +281,6 @@ impl Default for PinSlab { } } -impl Drop for PinSlab { - fn drop(&mut self) { - self.clear(); - } -} - #[cfg(test)] mod tests { use super::PinSlab; diff --git a/src/pin_vec.rs b/src/pin_vec.rs index 6e3c407..8d42418 100644 --- a/src/pin_vec.rs +++ b/src/pin_vec.rs @@ -1,5 +1,6 @@ use std::mem::{self, MaybeUninit}; use std::ops::{Index, IndexMut}; +use std::ptr::drop_in_place; pub struct PinVec { // Slots of memory. Once one has been allocated it is never moved. @@ -22,7 +23,25 @@ impl PinVec { } pub fn clear(&mut self) { - self.slots.clear(); + if mem::needs_drop::() { + let (last_slot, offset, _) = calculate_key(self.len()); + for (i, mut slot) in self.slots.drain(..).enumerate() { + let slice: &mut [MaybeUninit] = if i < last_slot { + &mut *slot + } else { + &mut slot[0..offset] + }; + // Safety: we initialized slice to only point to the already-initialized elements. + // It's safe to drop_in_place because we are draining the Vec. + unsafe { + let slice: &mut [T] = mem::transmute(slice); + drop_in_place(slice); + } + } + } else { + self.slots.clear(); + } + debug_assert_eq!(self.slots.len(), 0); self.len = 0; } @@ -66,6 +85,12 @@ impl PinVec { } } +impl Drop for PinVec { + fn drop(&mut self) { + self.clear(); + } +} + impl Index for PinVec { type Output = T; @@ -106,6 +131,8 @@ const fn calculate_key(key: usize) -> (usize, usize, usize) { mod tests { use crate::pin_vec::calculate_key; + use super::PinVec; + #[test] fn key_test() { // NB: range of the first slot. @@ -121,4 +148,24 @@ mod tests { ); } } + + #[test] + fn run_destructors() { + let mut destructor_ran = false; + + struct RunDestructor<'a>(&'a mut bool); + impl<'a> Drop for RunDestructor<'a> { + fn drop(&mut self) { + *self.0 = true; + } + } + + { + // Make sure PinVec runs the destructors + let mut v = PinVec::new(); + v.push(RunDestructor(&mut destructor_ran)); + } + + assert!(destructor_ran); + } } diff --git a/tests/pin_slab_memory_leak.rs b/tests/pin_slab_memory_leak.rs new file mode 100644 index 0000000..7ec6a38 --- /dev/null +++ b/tests/pin_slab_memory_leak.rs @@ -0,0 +1,17 @@ +use unicycle::pin_slab::PinSlab; + +struct Foo(u32); + +struct Bar(Vec); + +#[global_allocator] +static ALLOCATOR: checkers::Allocator = checkers::Allocator::system(); + +#[checkers::test] +fn test_pin_slab_memory_leak() { + let mut copy = PinSlab::new(); + copy.insert(Foo(42)); + + let mut non_copy = PinSlab::new(); + non_copy.insert(Bar(vec![42])); +}