use core::ptr::NonNull;
use crate::class;
use crate::class::SPAN_BYTES;
use crate::heap::{EMPTY_SPAN_HYSTERESIS, Heap};
use crate::os;
use crate::segment::{FIRST_DATA_SPAN, NO_CLASS, SPANS_PER_SEGMENT, Segment};
impl Heap {
pub fn reclaim(&mut self) {
self.flush_claims();
crate::large::pool_drain();
if !self.outbound.is_empty() {
self.outbound.flush();
}
self.drain_foreign();
let mut kept: u16 = 0;
let mut seg = self.segments;
while !seg.is_null() {
let s = unsafe { &mut *seg };
for ix in FIRST_DATA_SPAN..SPANS_PER_SEGMENT {
if s.spans[ix].class == NO_CLASS {
continue;
}
if s.spans[ix].live != 0 {
Self::discard_free_pages(s, ix);
continue;
}
if kept < EMPTY_SPAN_HYSTERESIS {
kept += 1;
continue;
}
let c = s.spans[ix].class as usize;
if self.partial[c] == Some((unsafe { NonNull::new_unchecked(seg) }, ix as u8)) {
self.partial[c] = None;
}
self.spans_in_class[c] -= 1;
s.spans[ix].reset(crate::pagemap::NO_CLASS);
let base = s.span_base(ix);
unsafe {
os::discard(NonNull::new_unchecked(base), SPAN_BYTES);
}
}
seg = s.next;
}
}
fn discard_free_pages(s: &mut Segment, ix: usize) {
use crate::pagemap::{PAGES_PER_SPAN, slots_of_page};
let meta = &mut s.spans[ix];
let slot = class::size_of(meta.class as usize);
let cap = meta.capacity();
let hw_bytes = meta.high_water as usize * slot;
let base = s.span_base(ix);
let mut run: Option<usize> = None;
for p in 0..PAGES_PER_SPAN {
let meta = &mut s.spans[ix];
let fresh = meta.discarded & (1u16 << p) == 0 && p * os::PAGE < hw_bytes && {
let (a, b) = slots_of_page(p, slot, cap);
!meta.range_has_live(a, b)
};
if fresh {
meta.discarded |= 1u16 << p;
run.get_or_insert(p);
} else if let Some(r0) = run.take() {
unsafe {
os::discard(
NonNull::new_unchecked(base.wrapping_add(r0 * os::PAGE)),
(p - r0) * os::PAGE,
);
}
}
}
if let Some(r0) = run {
unsafe {
os::discard(
NonNull::new_unchecked(base.wrapping_add(r0 * os::PAGE)),
(PAGES_PER_SPAN - r0) * os::PAGE,
);
}
}
}
}