mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
refactor(allocator/fixed-size): return Result from allocation instead of panicking (#17022)
Co-authored-by: overlookmotel <theoverlookmotel@gmail.com>
This commit is contained in:
1 parent
552f9ef82e
commit
1e2d011da9
2 files changed
+32
-9
No files matched your search
@@ -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`].
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in new issue
Block a user