refactor(allocator/fixed-size): return Result from allocation instead of panicking (#17022)

Co-authored-by: overlookmotel <theoverlookmotel@gmail.com>
This commit is contained in:
Cameronandoverlookmotel authored and GitHub committed 2025-12-18 22:23:16 +00:00
1 parent 552f9ef82e
commit 1e2d011da9
2 files changed
+32 -9

No files matched your search

+30 -8
View File
@@ -1,5 +1,7 @@
use std::{
alloc::{self, GlobalAlloc, Layout, System},
alloc::{GlobalAlloc, Layout, System},
error::Error,
fmt,
mem::{self, ManuallyDrop},
ptr::NonNull,
sync::{
@@ -15,6 +17,26 @@ use crate::{
generated::fixed_size_constants::{BLOCK_ALIGN, BLOCK_SIZE, RAW_METADATA_SIZE},
};
/// Error returned when a fixed-size allocator cannot be created due to allocation failure.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AllocError {
/// The layout of the allocation that failed.
pub layout: Layout,
}
impl fmt::Display for AllocError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"memory allocation failed for fixed-size allocator: requested {} bytes with {} byte alignment",
self.layout.size(),
self.layout.align()
)
}
}
impl Error for AllocError {}
const TWO_GIB: usize = 1 << 31;
const FOUR_GIB: usize = 1 << 32;
@@ -53,7 +75,7 @@ impl FixedSizeAllocatorPool {
// Protect against IDs wrapping around.
// TODO: Does this work? Do we need it anyway?
assert!(id < u32::MAX, "Created too many allocators");
FixedSizeAllocator::new(id)
FixedSizeAllocator::try_new(id).unwrap()
});
// Unwrap `FixedSizeAllocator`.
@@ -187,9 +209,11 @@ struct FixedSizeAllocator {
}
impl FixedSizeAllocator {
/// Create a new [`FixedSizeAllocator`].
/// Try to create a new [`FixedSizeAllocator`].
///
/// Returns `Err(AllocError)` if memory allocation fails.
#[expect(clippy::items_after_statements)]
fn new(id: u32) -> Self {
fn try_new(id: u32) -> Result<Self, AllocError> {
// Only support little-endian systems. `Allocator::from_raw_parts` includes this same assertion.
// This module is only compiled on 64-bit little-endian systems, so it should be impossible for
// this panic to occur. But we want to make absolutely sure that if there's a mistake elsewhere,
@@ -203,9 +227,7 @@ impl FixedSizeAllocator {
// Allocate block of memory.
// SAFETY: `ALLOC_LAYOUT` does not have zero size.
let alloc_ptr = unsafe { System.alloc(ALLOC_LAYOUT) };
let Some(alloc_ptr) = NonNull::new(alloc_ptr) else {
alloc::handle_alloc_error(ALLOC_LAYOUT);
};
let alloc_ptr = NonNull::new(alloc_ptr).ok_or(AllocError { layout: ALLOC_LAYOUT })?;
// All code in the rest of this function is infallible, so the allocation will always end up
// owned by a `FixedSizeAllocator`, which takes care of freeing the memory correctly on drop
@@ -254,7 +276,7 @@ impl FixedSizeAllocator {
metadata_ptr.write(metadata);
}
Self { allocator }
Ok(Self { allocator })
}
/// Reset this [`FixedSizeAllocator`].
+2 -1
View File
@@ -66,7 +66,8 @@ impl AllocatorPool {
///
/// # Panics
///
/// Panics if the underlying mutex is poisoned.
/// * Panics if the underlying mutex is poisoned.
/// * Panics if a new allocator needs to be created but memory allocation fails.
pub fn get(&self) -> AllocatorGuard<'_> {
let allocator = match &self.0 {
AllocatorPoolInner::Standard(pool) => pool.get(),