use crate::local_alloc::ThreadAllocator;
use core::ptr::NonNull;
use mnemosyne_arena::{AcquiredSegment, HasSegmentPool, acquire_segment};
use mnemosyne_core::constants::PAGES_PER_SEGMENT;
use mnemosyne_core::policy::AllocPolicy;
use mnemosyne_core::size_class::class_to_size;
use mnemosyne_core::types::{Page, Segment};
use super::super::page::{try_allocate_page_local_dynamic, try_reclaim_and_allocate_dynamic};
impl<B: HasSegmentPool> ThreadAllocator<B> {
#[inline(always)]
fn page_init_random(&mut self, randomize: bool, ptr_bits: u64, class: usize) -> u64 {
if randomize {
self.next_random() ^ ptr_bits ^ (class as u64).rotate_left(17)
} else {
0
}
}
#[inline(always)]
#[expect(
clippy::too_many_arguments,
reason = "the dynamic page setup keeps the segment metadata inputs explicit"
)]
unsafe fn setup_and_activate_page_dynamic(
&mut self,
page: *mut Page,
segment: *mut Segment,
page_index: usize,
class: usize,
block_size: usize,
enable_encryption: bool,
randomize: bool,
) {
let random_value = self.page_init_random(randomize, page as u64, class);
unsafe {
(*page).block_size = block_size as _;
(*page).size_class = class as u8;
let page_start = Page::page_start_in_segment(segment, page_index);
Page::initialize_free_list_in_segment_dynamic(
segment,
page_index,
page_start,
random_value,
enable_encryption,
randomize,
);
self.push_active_page(NonNull::new_unchecked(page), class);
}
}
}
impl<B: HasSegmentPool> ThreadAllocator<B> {
#[inline(never)]
pub unsafe fn alloc_cold<P: AllocPolicy>(&mut self, class: usize) -> *mut u8 {
unsafe {
self.alloc_cold_raw(
class,
P::ENABLE_FREE_LIST_ENCRYPTION,
P::RANDOMIZE_ALLOCATION,
)
}
}
#[inline(never)]
unsafe fn alloc_cold_raw(
&mut self,
class: usize,
enable_encryption: bool,
randomize: bool,
) -> *mut u8 {
unsafe { self.record_defrag_operation(true) };
if let Some(active_ptr) = unsafe { *self.active_pages.get_unchecked(class) } {
let active_page = active_ptr.as_ptr();
if let Some(block) = unsafe {
try_reclaim_and_allocate_dynamic(
active_page,
&mut self.cross_thread_reclaimed,
enable_encryption,
randomize,
)
} {
return block.as_ptr() as *mut u8;
}
unsafe {
self.unlink_page(active_ptr.as_ptr(), class);
self.push_full_page(active_ptr, class);
}
}
if let Some(active_ptr) = unsafe { *self.active_pages.get_unchecked(class) } {
let active_page = active_ptr.as_ptr();
if let Some(block) = unsafe {
try_allocate_page_local_dynamic(active_page, enable_encryption, randomize)
} {
return block.as_ptr() as *mut u8;
}
if let Some(block) = unsafe {
try_reclaim_and_allocate_dynamic(
active_page,
&mut self.cross_thread_reclaimed,
enable_encryption,
randomize,
)
} {
return block.as_ptr() as *mut u8;
}
}
let mut curr_opt = unsafe { *self.full_pages.get_unchecked(class) };
let mut checked = 0;
while let Some(page_ptr) = curr_opt {
if checked >= 128 {
break;
}
checked += 1;
let page = page_ptr.as_ptr();
let block_opt = unsafe {
try_reclaim_and_allocate_dynamic(
page,
&mut self.cross_thread_reclaimed,
enable_encryption,
randomize,
)
};
if let Some(block) = block_opt {
if unsafe { ((*page).alloc_count as usize) < (*page).max_blocks() } {
unsafe {
let _ = self.move_full_page_to_active(page_ptr, class);
}
}
return block.as_ptr() as *mut u8;
}
curr_opt = unsafe { (*page).next_page };
}
let new_page_ptr =
unsafe { self.get_new_page_dynamic(class, enable_encryption, randomize) };
if new_page_ptr.is_null() {
return core::ptr::null_mut();
}
self.page_refills += 1;
let page = new_page_ptr;
let block = unsafe { Page::pop_block_dynamic(page, enable_encryption, randomize) };
unsafe { Page::increment_alloc_count(page) };
if unsafe { (*page).alloc_count as usize == (*page).max_blocks() } {
unsafe {
let ptr = NonNull::new_unchecked(new_page_ptr);
self.unlink_page(ptr.as_ptr(), class);
self.push_full_page(ptr, class);
}
}
block.as_ptr() as *mut u8
}
pub(crate) unsafe fn get_new_page_dynamic(
&mut self,
class: usize,
enable_encryption: bool,
randomize: bool,
) -> *mut Page {
let block_size = class_to_size(class);
if let Some(page_ptr) = unsafe { self.pop_best_empty_page() } {
unsafe {
let page = page_ptr.as_ptr();
let segment = Page::parent_segment_of(page);
let page_index = (*page).page_index as usize;
self.setup_and_activate_page_dynamic(
page,
segment,
page_index,
class,
block_size,
enable_encryption,
randomize,
);
self.recycled_pages += 1;
return page;
}
}
if self.current_segment.is_none() || self.next_page_index >= PAGES_PER_SEGMENT {
let Some(acquired) =
(unsafe { acquire_policy_compatible_segment::<B>(enable_encryption) })
else {
return core::ptr::null_mut();
};
match acquired {
AcquiredSegment::Orphan(seg_ptr) => {
self.orphan_segments_adopted += 1;
let mut found_page: *mut Page = core::ptr::null_mut();
let mut found_page_index = 0;
unsafe {
self.push_owned_segment_dynamic(seg_ptr, enable_encryption);
self.set_current_segment(Some(NonNull::new_unchecked(seg_ptr)));
self.next_page_index = PAGES_PER_SEGMENT;
(*seg_ptr).page_linked_mask = 0;
for i in 1..PAGES_PER_SEGMENT {
let page_ptr = &raw mut (*seg_ptr).pages[i];
let has_content = (*page_ptr).block_size > 0;
if has_content {
let encrypted = (*seg_ptr).free_list_encrypted;
debug_assert_eq!(
encrypted, enable_encryption,
"adopted an orphan whose free-list mode does not match the policy"
);
let reclaimed =
Page::reclaim_thread_free_if_present_for_policy(seg_ptr, i);
if reclaimed > 0 {
self.record_cross_thread_reclaimed(reclaimed);
}
}
if has_content && (*page_ptr).alloc_count > 0 {
let pg_class = (*page_ptr).size_class as usize;
let ptr = NonNull::new_unchecked(page_ptr);
if ((*page_ptr).alloc_count as usize) < (*page_ptr).max_blocks() {
self.push_active_page(ptr, pg_class);
} else {
self.push_full_page(ptr, pg_class);
}
} else if found_page.is_null() {
found_page = page_ptr;
found_page_index = i;
} else {
self.push_empty_page(NonNull::new_unchecked(page_ptr));
}
}
}
if !found_page.is_null() {
unsafe {
self.setup_and_activate_page_dynamic(
found_page,
seg_ptr,
found_page_index,
class,
block_size,
enable_encryption,
randomize,
);
}
return found_page;
}
return unsafe {
self.get_new_page_dynamic(class, enable_encryption, randomize)
};
}
AcquiredSegment::Free(seg_ptr) => {
self.fresh_segments += 1;
unsafe {
self.push_owned_segment_dynamic(seg_ptr, enable_encryption);
self.set_current_segment(Some(NonNull::new_unchecked(seg_ptr)));
}
self.next_page_index = 1; }
}
}
let Some(seg) = self.current_segment else {
return core::ptr::null_mut();
};
let seg = seg.as_ptr();
let page_index = self.next_page_index;
let page_ptr = unsafe { &raw mut (*seg).pages[page_index] };
self.next_page_index += 1;
unsafe {
self.setup_and_activate_page_dynamic(
page_ptr,
seg,
page_index,
class,
block_size,
enable_encryption,
randomize,
);
}
self.fresh_pages += 1;
page_ptr
}
}
#[inline(never)]
unsafe fn acquire_policy_compatible_segment<B: HasSegmentPool>(
enable_encryption: bool,
) -> Option<AcquiredSegment> {
let mut deferred: *mut Segment = core::ptr::null_mut();
let chosen = loop {
let Some(acquired) = (unsafe { acquire_segment::<B>() }) else {
break None;
};
let AcquiredSegment::Orphan(seg_ptr) = acquired else {
break Some(acquired);
};
if unsafe { (*seg_ptr).free_list_encrypted } != enable_encryption {
unsafe {
(*seg_ptr)
.next_free_segment
.store(deferred, core::sync::atomic::Ordering::Relaxed);
}
deferred = seg_ptr;
continue;
}
break Some(acquired);
};
while !deferred.is_null() {
unsafe {
let next = (*deferred)
.next_free_segment
.load(core::sync::atomic::Ordering::Relaxed);
(*deferred)
.next_free_segment
.store(core::ptr::null_mut(), core::sync::atomic::Ordering::Relaxed);
B::global_orphan_pool().push_unbounded(deferred);
deferred = next;
}
}
chosen
}