Somebase tests for allocator

This commit is contained in:
Andrew Golovashevich 2026-03-04 18:18:49 +03:00
parent 6691847830
commit 744ecde879
3 changed files with 151 additions and 13 deletions

View File

@ -0,0 +1,135 @@
#![cfg(test)]
use super::allocator::Allocator;
use std::panic::catch_unwind;
use std::pin::Pin;
use std::ptr::NonNull;
#[test]
fn create_destroy() {
let h = Allocator::new();
}
struct FlagOnDrop<'a> {
flag: &'a mut bool,
}
impl<'a> FlagOnDrop<'a> {
fn init_pin(self: &mut Pin<&mut Self>, flag: &'a mut bool) {
unsafe {
Pin::into_inner_unchecked(NonNull::from_mut(self).read()).flag = flag;
}
}
}
impl Drop for FlagOnDrop<'_> {
fn drop(&mut self) {
*self.flag = true;
}
}
#[test]
fn destruct_auto_1_success() {
let mut destructed = false;
{
let mut h = Allocator::new();
unsafe {
h.alloc(|p| FlagOnDrop::init_pin(p, &mut destructed));
}
}
assert!(destructed);
}
#[test]
fn destruct_auto_2_success() {
let mut destructed_1 = false;
let mut destructed_2 = false;
{
let mut h = Allocator::new();
unsafe {
h.alloc(|p| FlagOnDrop::init_pin(p, &mut destructed_1));
h.alloc(|p| FlagOnDrop::init_pin(p, &mut destructed_2));
}
}
assert!(destructed_1);
assert!(destructed_2);
}
#[test]
fn destruct_manual_1_auto_2_success() {
let mut destructed_1 = false;
let mut destructed_2 = false;
{
let mut h = Allocator::new();
unsafe {
let a = h.alloc(|p| FlagOnDrop::init_pin(p, &mut destructed_1));
h.free(a);
h.alloc(|p| FlagOnDrop::init_pin(p, &mut destructed_2));
}
}
assert!(destructed_1);
assert!(destructed_2);
}
#[test]
fn destruct_manual_2_auto_1_success() {
let mut destructed_1 = false;
let mut destructed_2 = false;
{
let mut h = Allocator::new();
unsafe {
h.alloc(|p| FlagOnDrop::init_pin(p, &mut destructed_1));
let b = h.alloc(|p| FlagOnDrop::init_pin(p, &mut destructed_2));
h.free(b);
}
}
assert!(destructed_1);
assert!(destructed_2);
}
#[test]
fn destruct_manual_2_1() {
let mut destructed_1 = false;
let mut destructed_2 = false;
{
let mut h = Allocator::new();
unsafe {
let a = h.alloc(|p| FlagOnDrop::init_pin(p, &mut destructed_1));
let b = h.alloc(|p| FlagOnDrop::init_pin(p, &mut destructed_2));
h.free(b);
h.free(a);
}
}
assert!(destructed_1);
assert!(destructed_2);
}
#[test]
fn destruct_manual_1_2() {
let mut destructed_1 = false;
let mut destructed_2 = false;
{
let mut h = Allocator::new();
unsafe {
let a = h.alloc(|p| FlagOnDrop::init_pin(p, &mut destructed_1));
let b = h.alloc(|p| FlagOnDrop::init_pin(p, &mut destructed_2));
h.free(a);
h.free(b);
}
}
assert!(destructed_1);
assert!(destructed_2);
}
/*
#[allow(unused_must_use)]
#[test]
fn destruct_panic() {
let mut destructed = false;
catch_unwind(|| {
let mut h = Allocator::new();
unsafe {
h.alloc(|p| FlagOnDrop::init_pin(p, &mut destructed));
panic!("Test panic");
}
});
assert!(destructed);
}*/

View File

@ -1,7 +1,10 @@
use crate::errors::throw_from_windows_err_code;
use std::cell::UnsafeCell;
use std::ffi::c_void;
use std::marker::PhantomData;
use std::pin::Pin;
use std::ptr::NonNull;
use std::rc::Rc;
use std::thread::panicking;
use windows::Win32::Foundation::{GetLastError, HANDLE};
use windows::Win32::System::Memory::{
@ -10,7 +13,8 @@ use windows::Win32::System::Memory::{
pub(crate) struct Allocator {
win_heap: HANDLE,
last: Option<NonNull<Header>>,
last: UnsafeCell<Option<NonNull<Header>>>,
__not_send: PhantomData<Rc<()>>,
}
struct Header {
@ -30,7 +34,8 @@ impl Allocator {
}
return Allocator {
win_heap: h,
last: None,
last: UnsafeCell::new(None),
__not_send: PhantomData::default(),
};
}
}
@ -45,10 +50,7 @@ impl Allocator {
}
}
pub unsafe fn alloc<'s, T>(
&'s mut self,
init: impl FnOnce(&mut Pin<&mut T>),
) -> Pin<&'s mut T> {
pub unsafe fn alloc<'s, T>(&'s self, init: impl FnOnce(&mut Pin<&mut T>)) -> Pin<&'s mut T> {
let mut h = NonNull::new(
HeapAlloc(
self.win_heap,
@ -67,13 +69,13 @@ impl Allocator {
let mut p = Pin::new_unchecked(h.as_mut().value.cast::<T>().as_mut());
heap_free_on_exit::<false, _>(self.win_heap, h.cast(), || init(&mut p));
if let Some(mut last) = self.last {
if let Some(mut last) = self.last.get().read() {
last.as_mut().next = Some(h);
}
h.as_mut().prev = self.last;
h.as_mut().prev = self.last.get().read();
h.as_mut().next = None;
h.as_mut().destructor = Self::_destructor::<T>;
self.last = Some(h);
self.last.get().write(Some(h));
return p;
}
@ -82,7 +84,7 @@ impl Allocator {
drop(obj.cast::<T>().read())
}
pub unsafe fn free<T: Drop>(&mut self, p: Pin<&mut T>) {
pub unsafe fn free<T: Drop>(&self, p: Pin<&mut T>) {
let p = NonNull::from_mut(Pin::into_inner_unchecked(p));
let h = NonNull::new_unchecked(
p.as_ptr()
@ -93,8 +95,8 @@ impl Allocator {
h.next.map(|mut n| n.as_mut().prev = h.prev);
h.prev.map(|mut n| n.as_mut().next = h.next);
if self.last == Some(NonNull::from_mut(h)) {
self.last = h.prev;
if self.last.get().read() == Some(NonNull::from_mut(h)) {
self.last.get().write(h.prev);
}
heap_free_on_exit::<true, _>(self.win_heap, NonNull::from_mut(h).cast(), || {
(h.destructor)(p.cast::<c_void>())
@ -140,7 +142,7 @@ impl Drop for Allocator {
fn drop(&mut self) {
drop(RecursiveHeapDestructor {
win_heap: self.win_heap,
next: self.last,
next: unsafe { self.last.get().read() },
});
}
}

View File

@ -1,6 +1,7 @@
mod socket;
mod task;
mod allocator;
mod _allocator_tests;
use socket::WindowsOverlappingIcmpSocket;