pub struct SpanMeta {
pub class: u8,
pub live: u16,
pub high_water: u16,
pub discarded: u16,
pub retired: bool,
/* private fields */
}Expand description
Per-span bookkeeping. Deliberately not small: the bitmap is the price of page-granular reclaim, and it lives in the header span, which exists to be spent on exactly this.
Fields§
§class: u8Size class this span serves, or NO_CLASS.
live: u16Slots handed out and not yet freed.
high_water: u16Slots at or above this index have never been handed out; their pages were never touched and are not resident.
discarded: u16Pages returned to the OS (MADV_DONTNEED). Cleared per page when
an allocation lands back in one; set wholesale by
Heap::retire_empty_span when the span is emptied
and its pages go back together.
retired: boolSet when this span was emptied and handed back to the free pool, as opposed to never having been assigned at all.
Both are class == NO_CLASS, and they are opposite kinds of
unassigned: one was never touched, the other was touched and then
either discarded or deliberately kept. Without this bit all three
collapse into one bucket, which is what they did — the identity
balances the same whichever way they fall, so nothing caught it.
§Examples
The three states a span with no class can be in, and the two fields that tell them apart:
use kevy_alloc::pagemap::{ALL_PAGES_DISCARDED, NO_CLASS};
// (class, retired, discarded) -> what it is
let never_claimed = (NO_CLASS, false, 0u16);
let given_back = (NO_CLASS, true, ALL_PAGES_DISCARDED);
let held = (NO_CLASS, true, 0u16);
// All three read as "unassigned", and only `retired` plus the
// discard bitmap separate the one that was never touched from the
// one whose pages went back and the one still resident.
for (class, _, _) in [never_claimed, given_back, held] {
assert_eq!(class, NO_CLASS);
}
assert!(!never_claimed.1);
assert_ne!(given_back.2, held.2);Implementations§
Source§impl SpanMeta
impl SpanMeta
Sourcepub fn reset(&mut self, class: u8)
pub fn reset(&mut self, class: u8)
Assign this span to a class, forgetting everything it held. Only legal when nothing in it is live.
Sourcepub fn alloc_slot(&mut self) -> Option<u32>
pub fn alloc_slot(&mut self) -> Option<u32>
Take the lowest free slot, or None when the span is full.
Sourcepub fn claim_word(&mut self) -> Option<(u8, u64)>
pub fn claim_word(&mut self) -> Option<(u8, u64)>
Claim every free bit of the lowest holed word for local
handout: the bits are marked live in the bitmap (a claimed bit
pins its pages exactly as a live slot does, which is what makes
the claim invisible to reclaim), and the caller hands them out
from its own copy without touching this header again. Returns
(word_index, claimed_mask), or None when the span is full.
The far-line arithmetic this exists for: one header round-trip claims up to 64 slots, so the per-allocation touch that profiled at 17.3% of collection-write self time amortizes 64:1. Position-awareness coarsens from bit to word — the claim still takes the LOWEST holed word, so densification’s lowest-first semantics survive at word granularity.
Sourcepub fn retire_word(&mut self, w: u8, unused: u64)
pub fn retire_word(&mut self, w: u8, unused: u64)
Return the bits of a claimed word that were never handed out (or were handed out and locally freed). The exact inverse of the claim’s marking; the hint walks back so lowest-first allocation sees the holes again.
Sourcepub fn is_live(&self, i: u32) -> bool
pub fn is_live(&self, i: u32) -> bool
Whether slot i is live (or parked foreign, which pins pages
identically).
Sourcepub fn range_has_live(&self, first: u32, last: u32) -> bool
pub fn range_has_live(&self, first: u32, last: u32) -> bool
Whether any slot in first..=last is live.