use crate::local_alloc::ThreadAllocator;
use crate::local_alloc::page::{push_page_front_raw, unlink_page_from_list_raw};
use core::ptr::NonNull;
use mnemosyne_arena::{HasSegmentPool, deallocate_segment, try_deallocate_segment};
use mnemosyne_core::constants::NUM_SIZE_CLASSES;
use mnemosyne_core::types::{OccupiedPageBits, Page, Segment};
const MIN_RETAINED_OWNED_SEGMENTS: usize = 3;
const RECLAIM_THRESHOLD_SEGMENTS: usize = MIN_RETAINED_OWNED_SEGMENTS + 1;
use crate::local_alloc::segment::deferred_chain::DeferredChain;
impl<B: HasSegmentPool> ThreadAllocator<B> {
pub fn reclaim_owned_segments(&mut self) {
unsafe { self.set_current_segment(None) };
let mut deferred = DeferredChain::new();
let mut curr = self.owned_segments_head;
while !curr.is_null() {
unsafe {
let next = (*curr).next_owned_segment;
let dynamic_encrypted = (*curr).free_list_encrypted;
let mut total_allocations = 0;
for i in OccupiedPageBits::new((*curr).page_occupied_mask) {
let page = reclaim_and_record(
curr,
i,
dynamic_encrypted,
&mut self.cross_thread_reclaimed,
);
total_allocations += (*page).alloc_count;
}
Segment::clear_ownership(curr);
Segment::set_current(curr, false);
(*curr).next_owned_segment = core::ptr::null_mut();
(*curr).prev_owned_segment = core::ptr::null_mut();
if total_allocations > 0 || !try_deallocate_segment::<B>(curr) {
deferred.push(curr);
}
curr = next;
}
}
unsafe { deferred.place_in_orphan_pool::<B>() };
self.next_page_index = 0;
self.owned_segments_head = core::ptr::null_mut();
self.owned_segment_count = 0;
self.active_pages = [None; NUM_SIZE_CLASSES];
self.full_pages = [None; NUM_SIZE_CLASSES];
self.empty_pages = None;
}
pub unsafe fn try_reclaim_segment(&mut self, segment: *mut Segment) -> bool {
if self
.current_segment
.is_some_and(|current| current.as_ptr() == segment)
{
return false;
}
if self.owned_segment_count < RECLAIM_THRESHOLD_SEGMENTS {
return false;
}
unsafe {
let dynamic_encrypted = (*segment).free_list_encrypted;
for i in OccupiedPageBits::new((*segment).page_occupied_mask) {
let pg = Page::page_in_segment(segment, i);
if (*pg).alloc_count > 0 {
let pg = reclaim_and_record(
segment,
i,
dynamic_encrypted,
&mut self.cross_thread_reclaimed,
);
if (*pg).alloc_count > 0 {
return false;
}
}
}
}
unsafe {
unlink_segment_pages(self, segment);
self.unlink_owned_segment(segment);
}
unsafe { detach_and_release_segment::<B>(segment) };
true
}
pub unsafe fn periodic_defragmentation_sweep(&mut self) {
let mut curr = self.owned_segments_head;
while !curr.is_null() {
let segment = curr;
curr = unsafe { (*segment).next_owned_segment };
if self.is_current_segment(segment) {
continue;
}
let dynamic_encrypted = unsafe { (*segment).free_list_encrypted };
let mut total_allocations = 0;
unsafe {
for i in OccupiedPageBits::new((*segment).page_occupied_mask) {
let pg = &raw mut (*segment).pages[i];
reclaim_and_record(
segment,
i,
dynamic_encrypted,
&mut self.cross_thread_reclaimed,
);
total_allocations += (*pg).alloc_count;
if (*pg).alloc_count == 0 && ((*pg).list_state == 1 || (*pg).list_state == 2) {
let class = (*pg).size_class as usize;
let is_only_active =
crate::free_helpers::is_sole_active_page(self.active_pages[class], pg);
if !is_only_active {
let pg_ptr = NonNull::new_unchecked(pg);
if (*pg).list_state == 1 {
unlink_page_from_list_raw(
pg_ptr,
self.active_pages.get_unchecked_mut(class),
);
} else {
unlink_page_from_list_raw(
pg_ptr,
self.full_pages.get_unchecked_mut(class),
);
}
push_page_front_raw(pg_ptr, &mut self.empty_pages, 3);
}
}
}
}
if total_allocations == 0 && self.owned_segment_count >= RECLAIM_THRESHOLD_SEGMENTS {
unsafe {
unlink_segment_pages(self, segment);
self.unlink_owned_segment(segment);
detach_and_release_segment::<B>(segment);
}
}
}
}
}
unsafe fn unlink_segment_pages<B: HasSegmentPool>(
alloc: &mut ThreadAllocator<B>,
segment: *mut Segment,
) {
for i in OccupiedPageBits::new(unsafe { (*segment).page_linked_mask }) {
let pg = unsafe { &raw mut (*segment).pages[i] };
let state = unsafe { (*pg).list_state };
if state == 1 || state == 2 {
let class = unsafe { (*pg).size_class } as usize;
unsafe { alloc.unlink_page(pg, class) };
} else if state == 3 {
unsafe { alloc.unlink_empty_page(pg) };
}
}
}
#[inline]
unsafe fn detach_and_release_segment<B: HasSegmentPool>(segment: *mut Segment) {
unsafe {
Segment::clear_ownership(segment);
deallocate_segment::<B>(segment);
}
}
#[inline(always)]
unsafe fn reclaim_and_record(
segment: *mut Segment,
page_index: usize,
encrypted: bool,
cross_thread_sink: &mut usize,
) -> *mut Page {
let page = unsafe { Page::page_in_segment(segment, page_index) };
let randomized = unsafe { (*page).secondary_free.is_some() };
let reclaimed = unsafe {
Page::reclaim_thread_free_if_present_in_segment_with_randomized(
segment, page_index, encrypted, randomized,
)
};
if reclaimed > 0 {
*cross_thread_sink += reclaimed;
}
page
}