Consider the following Allocator struct that delays deallocation until the Allocator itself is dropped:
/// An Allocator that delays deallocation until it is dropped. #[derive(Debug)] struct DelayedDeallocator<A: Allocator> { allocator: A, // Use the System allocator for the Vec to avoid recursions. dealloc_queue: Mutex<Vec<(NonNull<u8>, Layout), System>> } impl<A: Allocator> DelayedDeallocator<A> { pub fn new(allocator: A) -> Self { Self { allocator, dealloc_queue: Mutex::new(Vec::new_in(System)) } } } impl<A: Allocator> Drop for DelayedDeallocator<A> { fn drop(&mut self) { let queue_inner = self.dealloc_queue.get_mut().unwrap(); for (ptr, layout) in queue_inner.iter() { /* * SAFETY: values on the vec were pushed on by our deallocate, and we expect those * callers to have satisfied the deallocate preconditions. */ unsafe { self.allocator.deallocate(*ptr, *layout); } } } } unsafe impl<A: Allocator> Allocator for DelayedDeallocator<A> { fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> { self.allocator.allocate(layout) } unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) { self.dealloc_queue.lock().unwrap().push((ptr, layout)); } } At a glance, this code seems to make sense in that it delays deallocation using a System-backed Vec for its own storage. However, the safety documentation for Allocator states that "It is undefined behavior for the allocator to read, write, or deallocate any memory that is currently allocated." Extending the Vec with new entries is a "write...[to] any memory that is currently allocated", and so it seems that this code would cause UB.
Would it make sense for the documentation to instead state "It is undefined behavior for the allocator to read, write, or deallocate any memory that it has itself currently allocated. [emphasis mine]", or is this code actually UB?
1 post - 1 participant


