From 6691847830f780ea470d038eb3c80da364d3e1f8 Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Tue, 3 Mar 2026 23:23:16 +0300 Subject: [PATCH] Heap safe destructor --- network/windows/src/eventloop/allocator.rs | 109 +++++++++++++++++---- 1 file changed, 88 insertions(+), 21 deletions(-) diff --git a/network/windows/src/eventloop/allocator.rs b/network/windows/src/eventloop/allocator.rs index 2b4d9f8..ff2131e 100644 --- a/network/windows/src/eventloop/allocator.rs +++ b/network/windows/src/eventloop/allocator.rs @@ -2,9 +2,10 @@ use crate::errors::throw_from_windows_err_code; use std::ffi::c_void; use std::pin::Pin; use std::ptr::NonNull; +use std::thread::panicking; use windows::Win32::Foundation::{GetLastError, HANDLE}; use windows::Win32::System::Memory::{ - HeapAlloc, HeapCreate, HeapFree, HEAP_FLAGS, HEAP_NO_SERIALIZE, + HEAP_FLAGS, HEAP_NO_SERIALIZE, HeapAlloc, HeapCreate, HeapDestroy, HeapFree, }; pub(crate) struct Allocator { @@ -16,6 +17,7 @@ struct Header { prev: Option>, next: Option>, destructor: unsafe fn(NonNull), + value: NonNull, } impl Allocator { @@ -57,6 +59,14 @@ impl Allocator { ) .unwrap_or_else(|| throw_from_windows_err_code(GetLastError())); + h.as_mut().value = NonNull::new_unchecked( + h.as_ptr() + .map_addr(|a| a + Self::_calc_offset::()) + .cast::(), + ); + let mut p = Pin::new_unchecked(h.as_mut().value.cast::().as_mut()); + heap_free_on_exit::(self.win_heap, h.cast(), || init(&mut p)); + if let Some(mut last) = self.last { last.as_mut().next = Some(h); } @@ -65,15 +75,6 @@ impl Allocator { h.as_mut().destructor = Self::_destructor::; self.last = Some(h); - let mut p = Pin::new_unchecked( - &mut *(h - .as_ptr() - .map_addr(|a| a + Self::_calc_offset::()) - .cast::()), - ); - - init(&mut p); - return p; } @@ -95,16 +96,82 @@ impl Allocator { if self.last == Some(NonNull::from_mut(h)) { self.last = h.prev; } - - (h.destructor)(p.cast::()); - - match HeapFree( - self.win_heap, - HEAP_FLAGS::default(), - Some((h as *mut Header).cast()), - ) { - Ok(_) => return, - Err(err) => throw_from_windows_err_code(err.code()) - }; + heap_free_on_exit::(self.win_heap, NonNull::from_mut(h).cast(), || { + (h.destructor)(p.cast::()) + }); + } +} + +fn heap_free_on_exit( + win_heap: HANDLE, + ptr: NonNull, + scope: impl FnOnce() -> R, +) -> R { + let defer = HeapFreeOnDrop:: { win_heap, ptr }; + let ret = scope(); + drop(defer); + return ret; +} + +struct HeapFreeOnDrop { + win_heap: HANDLE, + ptr: NonNull, +} + +impl Drop for HeapFreeOnDrop { + fn drop(&mut self) { + if !panicking() && !FreeOnSuccess { + return; + } + unsafe { + match HeapFree( + self.win_heap, + HEAP_FLAGS::default(), + Some(self.ptr.cast().as_mut()), + ) { + Ok(_) => return, + Err(err) => throw_from_windows_err_code(err.code()), + }; + } + } +} + +impl Drop for Allocator { + fn drop(&mut self) { + drop(RecursiveHeapDestructor { + win_heap: self.win_heap, + next: self.last, + }); + } +} + +struct RecursiveHeapDestructor { + win_heap: HANDLE, + next: Option>, +} + +impl Drop for RecursiveHeapDestructor { + fn drop(&mut self) { + unsafe { + match self.next { + Some(mut h) => { + let h = h.as_mut(); + let next_destructor = RecursiveHeapDestructor { + win_heap: self.win_heap, + next: h.prev, + }; + heap_free_on_exit::( + self.win_heap, + NonNull::from_mut(h).cast(), + || (h.destructor)(h.value), + ); + drop(next_destructor); + } + None => match HeapDestroy(self.win_heap) { + Ok(()) => return, + Err(err) => throw_from_windows_err_code(err.code()), + }, + } + } } }