use crate::per_cpu;
use crate::{LocalAllocatorSelector, ThreadAllocator, initialize_allocated_bytes};
use mnemosyne_arena::{HasSegmentPool, allocate_large_or_huge};
use mnemosyne_core::constants::MIN_BLOCK_SIZE;
use mnemosyne_core::policy::AllocPolicy;
use mnemosyne_core::size_class::{class_to_size, size_to_class_nonzero};
use mnemosyne_core::validation::{is_valid_alloc_request, is_valid_layout_alloc_request};
#[inline(always)]
pub unsafe fn thread_alloc<P: AllocPolicy, B: HasSegmentPool + LocalAllocatorSelector<B>>(
size: usize,
align: usize,
) -> *mut u8 {
if !is_valid_alloc_request(size, align) {
return core::ptr::null_mut();
}
let ptr = unsafe { thread_alloc_checked::<P, B>(size, align) };
if mnemosyne_prof::is_active() && !ptr.is_null() {
mnemosyne_prof::on_alloc(ptr, size);
}
ptr
}
#[inline(always)]
pub unsafe fn thread_alloc_layout<P: AllocPolicy, B: HasSegmentPool + LocalAllocatorSelector<B>>(
size: usize,
align: usize,
) -> *mut u8 {
if !is_valid_layout_alloc_request(size, align) {
return core::ptr::null_mut();
}
debug_assert!(
align != 0 && align.is_power_of_two(),
"Layout-validated allocation received invalid alignment {align}"
);
let ptr = unsafe { thread_alloc_checked::<P, B>(size, align) };
if mnemosyne_prof::is_active() && !ptr.is_null() {
mnemosyne_prof::on_alloc(ptr, size);
}
ptr
}
#[inline(always)]
pub(crate) fn small_path_class(size: usize, align: usize) -> Option<usize> {
let adjusted_size = core::cmp::max(size, align);
if align <= MIN_BLOCK_SIZE {
return size_to_class_nonzero(adjusted_size);
}
let rounded = (adjusted_size + align - 1) & !(align - 1);
match size_to_class_nonzero(rounded) {
Some(c) if class_to_size(c) & (align - 1) == 0 => Some(c),
_ => None,
}
}
#[inline(always)]
unsafe fn thread_alloc_checked<P: AllocPolicy, B: HasSegmentPool + LocalAllocatorSelector<B>>(
size: usize,
align: usize,
) -> *mut u8 {
let adjusted_size = core::cmp::max(size, align);
let class = match small_path_class(size, align) {
Some(c) => c,
None => {
return unsafe { allocate_large_or_huge_initialized::<P, B>(adjusted_size, align) };
}
};
let slot_ptr = B::get_allocator_ptr_raw_for_policy::<P>();
if !slot_ptr.is_null() {
let alloc = unsafe { &mut *(slot_ptr as *mut ThreadAllocator<B>) };
if !alloc.is_allocating {
if let Some(page_ptr) = unsafe { *alloc.active_pages.get_unchecked(class) } {
let mut page_ptr = crate::local_alloc::page::refresh_page_pointer(page_ptr);
let page = unsafe { page_ptr.as_mut() };
if let Some(block) =
unsafe { crate::local_alloc::page::try_allocate_page_local::<P>(page) }
{
let ptr = block.as_ptr() as *mut u8;
unsafe { initialize_allocated_bytes::<P>(ptr, adjusted_size) };
return ptr;
}
if let Some(block) = unsafe {
crate::local_alloc::page::try_reclaim_and_allocate::<P>(
page,
&mut alloc.cross_thread_reclaimed,
)
} {
let ptr = block.as_ptr() as *mut u8;
unsafe { initialize_allocated_bytes::<P>(ptr, adjusted_size) };
return ptr;
}
}
}
unsafe {
thread_alloc_cold::<P, B>(
class,
adjusted_size,
align,
Some(core::ptr::NonNull::new_unchecked(alloc as *mut _)),
)
}
} else {
unsafe { thread_alloc_cold::<P, B>(class, adjusted_size, align, None) }
}
}
#[cold]
#[inline(never)]
unsafe fn thread_alloc_cold<P: AllocPolicy, B: HasSegmentPool + LocalAllocatorSelector<B>>(
class: usize,
adjusted_size: usize,
align: usize,
alloc_opt: Option<core::ptr::NonNull<ThreadAllocator<B>>>,
) -> *mut u8 {
if B::ENABLE_CPU_CACHE {
let cpu_ptr = per_cpu::try_alloc_cpu::<P>(class);
if !cpu_ptr.is_null() {
unsafe { initialize_allocated_bytes::<P>(cpu_ptr, adjusted_size) };
return cpu_ptr;
}
}
let alloc = if let Some(alloc_ptr) = alloc_opt {
unsafe { &mut *alloc_ptr.as_ptr() }
} else {
let slot_ptr = B::get_allocator_ptr_for_policy::<P>();
if slot_ptr.is_null() {
return unsafe { allocate_large_or_huge_initialized::<P, B>(adjusted_size, align) };
}
unsafe { &mut *(slot_ptr as *mut ThreadAllocator<B>) }
};
if alloc.is_allocating {
return unsafe { allocate_large_or_huge_initialized::<P, B>(adjusted_size, align) };
}
alloc.is_allocating = true;
let ptr = unsafe { alloc.alloc_cold::<P>(class) };
alloc.is_allocating = false;
if ptr.is_null() {
return unsafe { allocate_large_or_huge_initialized::<P, B>(adjusted_size, align) };
}
unsafe { initialize_allocated_bytes::<P>(ptr, adjusted_size) };
ptr
}
#[inline(always)]
unsafe fn allocate_large_or_huge_initialized<P: AllocPolicy, B: HasSegmentPool>(
size: usize,
align: usize,
) -> *mut u8 {
let ptr = unsafe { allocate_large_or_huge::<B>(size, align, P::ENABLE_POISONING) };
if !ptr.is_null() {
unsafe { initialize_allocated_bytes::<P>(ptr, size) };
}
ptr
}