pub struct Reservation { /* private fields */ }Expand description
An owning handle to one aligned span of anonymous virtual memory.
as_ptr() is non-null, aligned to the align requested at reservation, and
valid for len() bytes for the lifetime of this handle with the following
exceptions:
-
Decommitted ranges: Ranges that the caller has decommitted (via the free functions or the safe methods) and not yet recommitted have platform-specific behavior:
- Windows: pages are unmapped until
recommit; access beforerecommitcrashes withSTATUS_ACCESS_VIOLATION. - Linux (eager
decommit): pages are zeroed on next access viaMADV_DONTNEED. - Linux (lazy
decommit_lazy): pages keep old contents until kernel reclaims them under pressure; writes before reclamation cancel the free. - Darwin/BSD: pages keep old contents;
MADV_DONTNEEDis advisory-only and does not reliably zero. - Huge reservations,
decommit_lazy(both layers), anddecommit/try_decommiton Windows or on a non-huge-page-aligned range on Linux/Android: old contents remain. The safe methodsReservation::decommit/Reservation::decommit_lazyskip the backend call outright in this case (they can consultis_huge()and, fordecommit, the requested range); the free functions cannot consultis_huge(), so they still issue the syscall — which the OS then refuses or ignores. Same observable outcome, different mechanism; do not read “no-op” as “no syscall” for the free functions. - Huge reservations, eager
decommit/try_decommit, Linux/Android kernel >= 5.18, range aligned to the huge page size (2 MiB) at both endpoints (task #1140): this is the ONE huge-page case where decommit actually works — pages ARE zeroed on next access viaMADV_DONTNEED, same as the ordinary eager-Linux case above. Both the safe method and the free function issue the real syscall here; they agree. SeeReservation::decommit’s own doc for the exact eligibility rule.
- Windows: pages are unmapped until
-
Lazy reservations on Windows (feature
lazy-commit): When created viareserve_aligned_lazy, only theinitial_commitprefix is committed at reservation time. The tail[initial_commit, len())must be committed viacommit_rangebefore it becomes writable. Writing to the uncommitted tail results in an access violation.
The span is not initialised. Dropping the handle returns the whole underlying OS reservation to the OS exactly once.
For a self-hosted allocator that records (reservation, reservation_len) in
its own metadata rather than keeping a Vec<Reservation>, use
into_parts to take the raw reservation (suppressing the
Drop) and release it later with release.
Reservation is Send (the span is owned exclusively) but not Sync
(writes through the raw pointer are unsynchronised — that is the caller’s
concern).
Implementations§
Source§impl Reservation
impl Reservation
Sourcepub fn as_ptr(&self) -> *mut u8
pub fn as_ptr(&self) -> *mut u8
The aligned usable base of this span. Non-null, aligned to the align
requested at reservation.
Validity scope: Valid for len() bytes, with the
following exceptions:
-
Decommitted ranges: Ranges decommitted via the free functions or safe methods and not yet recommitted have platform-specific behavior:
- Windows: pages are unmapped until
recommit; access beforerecommitcrashes withSTATUS_ACCESS_VIOLATION. - Linux (eager
decommit): pages are zeroed on next access viaMADV_DONTNEED. - Linux (lazy
decommit_lazy): pages keep old contents until kernel reclaims them under pressure; writes before reclamation cancel the free. - Darwin/BSD: pages keep old contents;
MADV_DONTNEEDis advisory-only and does not reliably zero. - Huge reservations,
decommit_lazy(both layers), anddecommit/try_decommiton Windows or on a non-huge-page-aligned range on Linux/Android: old contents remain. The safe methodsSelf::decommit/Self::decommit_lazyskip the backend call outright in this case (they can consultSelf::is_hugeand, fordecommit, the requested range); the free functions cannot consultSelf::is_huge, so they still issue the syscall — which the OS then refuses or ignores. Same observable outcome, different mechanism; do not read “no-op” as “no syscall” for the free functions. - Huge reservations, eager
decommit/try_decommit, Linux/Android kernel >= 5.18, range aligned to the huge page size (2 MiB) at both endpoints (task #1140): the one huge-page case where decommit actually works — pages ARE zeroed on next access viaMADV_DONTNEED. Both layers issue the real syscall here and agree. SeeSelf::decommit’s own doc for the exact eligibility rule.
- Windows: pages are unmapped until
-
Lazy reservations on Windows (feature
lazy-commit): When created viareserve_aligned_lazy, only theinitial_commitprefix is committed at reservation time. The tail[initial_commit, len())must be committed viacommit_rangebefore it becomes writable. Writing to the uncommitted tail results in an access violation.
Returns *mut u8 (rather than the std convention of *const T from
&self) because a raw pointer carries no borrow obligation in this
crate’s model, and the span is exclusively owned by this Reservation
handle. The mutability reflects ownership, not mutability of the
borrow itself.
Sourcepub fn reservation_ptr(&self) -> *mut u8
pub fn reservation_ptr(&self) -> *mut u8
The start of the underlying OS reservation (may sit below
as_ptr because the reservation is over-reserved
to achieve alignment and the full mapping is kept).
Sourcepub const fn reservation_len(&self) -> usize
pub const fn reservation_len(&self) -> usize
The requested/logical span length of this reservation.
This value is NOT necessarily the actual OS reservation size — at least three paths under-report the true VA span the OS mapped:
- Windows single-call fast path (
align <= 64 KiB): this returnscommit_len(which equalssize), not the rounded-up VA reservation size. Windows rounds VA reservations up to the 64 KiB allocation granularity internally, soreserve_aligned(4096, 4096)reportsreservation_len() == 4096while actually consuming 64 KiB of address space. - Windows two-call path’s fast-reserve sub-path (
align <= 64 KiBviareserve_aligned_lazy): when the candidateVirtualAlloc(NULL, size, MEM_RESERVE)happens to be aligned, this returnssizedirectly, not the rounded-up 64 KiB granularity. The underlying reservation still consumes a 64 KiB-granular region. - Any page-rounding
mmapwhere the OS page size exceeds the requested granularity — e.g. Apple-Silicon macOS’s 16 KiB pages, or 64 KiB on some Linux configurations (seeMIN_PAGE’s doc above):mmaproundslengthup to the page size, soreserve_aligned(PAGE, PAGE)on a 16 KiB-page host actually maps a full 16 KiB page while this returns4096.
Both cases are harmless for correctness (VirtualFree(base, 0, MEM_RELEASE) ignores the length argument; munmap rounds its length
argument up to the page size the same way mmap did, so release
still unmaps the whole underlying mapping) — but the return value is
not a portable measure of the true reservation size.
Sourcepub const fn is_huge(&self) -> bool
pub const fn is_huge(&self) -> bool
Whether OS large/huge pages were actually granted for this reservation.
Returns true if the reservation successfully obtained large/huge pages
from the OS (Linux MAP_HUGETLB or Windows MEM_LARGE_PAGES), and false
if it fell back to ordinary pages or was not a huge-page request.
This is the “best-effort” observable: a caller using reserve_aligned_huge
can now detect whether the huge-page feature actually engaged, rather than
receiving only an indistinguishable Ok(Reservation) on every fallback.
Windows limitation (task #848 single-call fast path): on Windows,
this returns true only when ALL of the following hold:
- The fast-path condition
align <= GetLargePageMinimum()is satisfied (typicallyalign <= 2 MiBon x86_64) sizeis a multiple of the system’s large-page minimum- The calling process has
SeLockMemoryPrivilegegranted AND has enabled it viaAdjustTokenPrivileges(the crate does not do this for you — a process with the privilege granted but not enabled fails exactly like an unprivileged one and silently falls back to ordinary pages)
NOTE: The widened fast-path condition (II-3, 2026-08-16 audit finding) expanded
the single-call ATTEMPT window from align <= 64 KiB to align <= GetLargePageMinimum(),
but on an unprivileged host the actual paths that SUCCEED (pass the post-call alignment
check) are typically still limited. When large pages are NOT granted (unprivileged),
VirtualAlloc’s alignment guarantee is only 64 KiB; in practice it typically does NOT
happen to land on the requested alignment, so the post-call check fails and the fast
path falls through to the two-call path. Practically, this means is_huge() == true only
for shapes where large pages are actually granted, which requires all three conditions
above to hold.
If any of these conditions fail, the function falls back to ordinary pages
and this flag is false. On Windows, large pages (MEM_LARGE_PAGES)
are only ever requested and possibly granted via the single-call fast path;
the two-call path never requests large pages, so
is_huge() is always false for a reservation that takes it. See
reserve_aligned_huge’s rustdoc for details.
Note: reservations adopted via from_raw_parts
report whatever granted_huge value the caller passed to that constructor,
which the caller is responsible for getting right (see that constructor’s
# Safety section).
This method has no huge-pages feature gate — Self::decommit’s
eligible-forward behavior does (task #1156, finding F10). is_huge()
reports true/false identically regardless of which features are
enabled; whether a true result also gets you a real Linux/Android
kernel >= 5.18 decommit forward instead of a guaranteed skip depends
on the huge-pages feature being enabled too. See Self::decommit’s
doc for the full explanation — this asymmetry matters most for
from_raw_parts callers, since that
constructor is also unconditionally compiled.
Sourcepub const fn decommit_reclaims_and_zeroes() -> bool
pub const fn decommit_reclaims_and_zeroes() -> bool
Returns true if the current platform’s ordinary native backend guarantees
that eager Self::decommit returns physical backing to the OS and zero-fills
on next access, false otherwise.
Scope: this is a platform-level query about the ordinary native backend’s contract. It does NOT apply to:
- huge-page reservations (those with
Self::is_huge==true) — eligibility there depends on the platform, the requested range, and (on Linux/Android) the running kernel, not on a single platform-wide answer: on Windows decommit is a guaranteed no-op; on Linux/Android with kernel >= 5.18, a huge-page-size-aligned range CAN actually decommit (see the freedecommitfunction’s “Huge-page granularity” rustdoc section for the exact split, andSelf::can_decommit_reclaim_and_zero’s own huge-page bullet for the conservative instance-level answer this platform-level query cannot give). - miri — under miri, the backend is a no-op that doesn’t model RSS or reclaim.
- the
aligned_vmem_mockcfg (RUSTFLAGS="--cfg aligned_vmem_mock") — the recording mock backend’s decommit logs the call WITHOUT touching the OS, so it reclaims nothing and zeroes nothing (task #1066). Excluded for the same reason the sibling capability querylazy_commit_is_honored()(featurelazy-commit) excludes it: this family answers for the backend actually linked into the compilation, and the miri bullet above is already that same substituted-backend category rather than a platform property.
For an instance-level query that accounts for huge pages, use
Self::can_decommit_reclaim_and_zero.
Platform behavior (ordinary native backend only, eager decommit path):
- Linux (all targets): returns
true.MADV_DONTNEEDunmaps physical pages and re-faults fresh zero pages on next access. - Windows: returns
true.MEM_DECOMMITunmaps physical pages and re-faults fresh zero pages on next access. - Darwin family (macOS/iOS/tvOS/watchOS): returns
false.MADV_DONTNEEDis advisory-only for anonymous memory and does not reliably unmap/zero pages. A decommit+recommit roundtrip can observe old data still resident. - BSD family (FreeBSD/DragonFly/NetBSD/OpenBSD): returns
false. Same advisory-only caveat as Darwin for eager decommit. (Note: lazy decommit viadecommit_lazyDOES reclaim on BSD viaMADV_FREE, even though eager decommit does not.)
This is a compile-time constant per platform: the return value is the same
for all calls within a single compilation unit, determined by the target
OS triple, whether miri is active, and whether the aligned_vmem_mock recording
backend is compiled in. It provides programmatic access to the
platform-specific guarantee that Self::decommit’s rustdoc describes in prose.
Sourcepub fn can_decommit_reclaim_and_zero(&self) -> bool
pub fn can_decommit_reclaim_and_zero(&self) -> bool
Returns true if eager Self::decommit on this specific reservation
guarantees reclaim+zero-fill semantics, false otherwise.
This is an advisory capability query. It is computed from
compile-time platform capability and the reservation’s huge-page status only,
and does not issue any runtime syscall or observe whether a prior decommit
call actually succeeded. Specifically:
- On Linux/Windows (native — not miri, not the
aligned_vmem_mockcfg),truemeans the platform guarantees thatdecommitwill return physical backing and zero-fill on next access viaMADV_DONTNEED/MEM_DECOMMIT. Backend syscall failures (e.g. rare kernel failures) are silently discarded and not reflected in this query’s return value. - On Darwin/BSDs, under miri, or under the
aligned_vmem_mockcfg,falsemeans decommit is advisory-only (Darwin/BSDs) or a recorded no-op (miri, mock) with no reclaim or zero-fill guarantee. - On huge-page reservations,
false— this bool is CONSERVATIVE and is NOT range-aware (task #1140): on Windows it is unconditionally correct (large-page decommit never works there). On Linux/Android with kernel >= 5.18, it UNDER-reports:Self::decommit/Self::try_decommitDO issue a realMADV_DONTNEEDfor a[start, end)range that is itself huge-page-size-aligned (2 MiB) at both endpoints — see those methods’ own doc comments — but this instance-level query has nostart/endparameters to judge that per-call, so it answersfalsefor EVERY range on a huge reservation, including the ranges that actually do work. CallSelf::try_decommitdirectly and judge by itsDecommitOutcomereturn value (task #1180:SkippedvsAdvisedvsRefused—Self::decommititself stays()/infallible and carries no such signal) / thebench-internalshuge_decommit_attemptscounter (not an intra-doc link:bench-internalsis excluded from the published docs.rs feature set) if you need to distinguish “this exact range worked” from “this bool said no.”
A true return is therefore a statement about the platform and reservation type,
not a guarantee that a specific decommit call actually released memory or zeroed
pages — OS errors in that path are unobservable through this API by design
(the same contract as the infallible decommit method itself). A false return is
similarly not a guarantee that no range on this reservation can ever be decommitted
(see the huge-page bullet above).
This query combines:
- the platform-level guarantee (see
Self::decommit_reclaims_and_zeroes), and - the reservation’s huge-page status (via
Self::is_huge).
Returns false if EITHER condition fails:
- the platform doesn’t guarantee reclaim+zero-fill (Darwin/BSDs, miri, or the
aligned_vmem_mockcfg), or - this reservation uses huge pages — conservatively: on Windows this is
always correct (huge-page decommit is a genuine no-op there), but on
Linux/Android >= 5.18 a huge-page-size-aligned range CAN actually
decommit (see
Self::decommit’s doc and the bullet on this fact above); this bool has no range to judge, so it answersfalseunconditionally for a huge reservation regardless of platform.
Use this when you have an actual Reservation and need to know whether decommit
will work on it for an ordinary (non-huge) reservation, or to conservatively rule
out a huge one. Use the associated function Self::decommit_reclaims_and_zeroes
when you only care about platform capability without a reservation instance. For a
huge reservation on Linux/Android, this bool cannot tell you whether a SPECIFIC
[start, end) will work — call Self::try_decommit and judge by its
DecommitOutcome instead (see the huge-page bullet above).
§Example
Ordinary reservation: decommit works on Linux/Windows (except miri):
let ordinary = reserve_aligned(1024 * 1024, 4096).expect("reserve");
// On Linux/Windows (native): ordinary.can_decommit_reclaim_and_zero() == true
// On Darwin/BSD, under miri, or under `aligned_vmem_mock`:
// ordinary.can_decommit_reclaim_and_zero() == falseHuge-page reservation: this bool is always false, but on Linux/Android
= 5.18 that does NOT mean
decommititself is a no-op for every range:
let huge = reserve_aligned_huge(2 * 1024 * 1024, 2 * 1024 * 1024);
if let Some(ref reservation) = huge {
if reservation.is_huge() {
// The bool is always false, regardless of platform — conservative,
// not "decommit never works" (see the doc above this example).
assert!(!reservation.can_decommit_reclaim_and_zero());
}
}NOTE: On Linux/Android with the huge-pages feature enabled, the
arguments must be multiples of the huge page size (2 MiB); the example
above uses 2 MiB for both size and align to avoid rejection. On other
platforms, the function is a best-effort no-op and any size/align will
succeed (falling back to ordinary pages).
See the tests in tests/decommit_capability.rs for runnable coverage of both cases.
Sourcepub fn into_parts(self) -> (*mut u8, usize, usize)
pub fn into_parts(self) -> (*mut u8, usize, usize)
Consume the handle WITHOUT releasing the OS reservation, returning the
(reservation_ptr, reservation_len, align) the caller must later hand to
release exactly once. Use this when your allocator records the
reservation in its own self-hosted metadata instead of relying on
Drop.
align is the alignment originally requested; the native release paths
ignore it, but it is required for the miri fallback to reconstruct the
exact Layout. A self-hosting allocator that always uses one alignment
can pass that constant to release instead of storing this value.
Warning: This method returns a raw tuple. Consider using
into_reservation_parts instead, which
returns a named struct that prevents accidentally swapping len and align.
Sourcepub fn into_reservation_parts(self) -> ReservationParts
pub fn into_reservation_parts(self) -> ReservationParts
Consume the handle WITHOUT releasing the OS reservation, returning the
ReservationParts struct the caller must later hand to release_parts
exactly once. Use this when your allocator records the reservation in its
own self-hosted metadata instead of relying on Drop.
This method is the typed, named alternative to into_parts;
it prevents the footgun of accidentally swapping len and align, which
would be undefined behavior on the native backend and cause leaks or crashes
on the Unix backend.
WARNING: This method discards base, len, and granted_huge. To
reconstruct a full Reservation via from_raw_parts,
you MUST preserve these three fields separately alongside the returned
ReservationParts. If you omit granted_huge, the reconstructed reservation
will incorrectly report is_huge() == false even if the original used huge
pages, which can lead to incorrect decommit-availability decisions.
For backwards compatibility with code that already uses the tuple form,
you can call ReservationParts::as_tuple to get a raw tuple.
No message-less #[must_use] on this function itself (task
#1213/L3): the return type ReservationParts now carries its own
#[must_use] with a leak-specific message (dropping it leaks the
reservation), which already fires for every caller of every function
returning it, this one included — clippy’s double_must_use lint
flags a redundant message-less attribute stacked on top of that.
Sourcepub fn into_full_parts(self) -> ReservationFullParts
pub fn into_full_parts(self) -> ReservationFullParts
Consume the handle WITHOUT releasing the OS reservation, returning a
full ReservationFullParts struct containing all six fields needed to
reconstruct the original Reservation via from_raw_parts.
This is the lossless round-trip alternative to into_reservation_parts:
it preserves base, len, and granted_huge in addition to the underlying
reservation metadata, eliminating the risk of silent huge-page status loss
or usable-span information loss.
Use this when you need to temporarily extract all reservation state for later reconstruction, such as in a custom allocator that hands off reservations between components within the same process.
IMPORTANT: ReservationFullParts is a plain struct with no Drop
implementation — dropping or forgetting it does NOT release the underlying
OS reservation. The reservation will leak until you reconstruct it via
into_reservation() and drop the resulting Reservation, or release it
manually via release (using the reservation, reservation_len, and
align fields from ReservationFullParts). If you only need manual
release and don’t require preserving base, len, and granted_huge,
prefer into_reservation_parts instead,
which provides the release_parts function.
No message-less #[must_use] on this function itself (task
#1213/L3): the return type ReservationFullParts now carries its
own #[must_use] with a leak-specific message, for the same reason
as into_reservation_parts above.
Sourcepub fn decommit(&mut self, start: usize, end: usize)
pub fn decommit(&mut self, start: usize, end: usize)
Decommit pages [start, end) within this reservation.
This is the safe, bounds-checked alternative to the free decommit
function for callers already holding a Reservation. It delegates to
the underlying implementation with self.as_ptr() as base and
automatically ensures [start, end) is within the reservation’s usable span.
Takes &mut self (task #1113): OS-state mutation requires exclusive
access, so a shared &Reservation can reach none of the seven state
mutators. This is what structurally seals the LazyReservation
watermark (finding H1, task #1104): a leaked &Reservation is now
read-only by construction, not by policing.
Programmatically check platform guarantees: use
Self::decommit_reclaims_and_zeroes to query whether the current
platform guarantees reclaim+zero-fill semantics.
Hint the OS to return the physical backing of [start, end) while keeping the
address-space reservation alive. On Linux and Windows this is guaranteed to
return physical backing and zero-fill on next access (Linux MADV_DONTNEED,
Windows MEM_DECOMMIT). On the Darwin family (macOS/iOS/tvOS/watchOS) and the
four BSDs (FreeBSD/DragonFly/NetBSD/OpenBSD), this is a best-effort hint with no
zero-fill or reclaim guarantee — the physical pages may remain resident and
old data may be observed after a decommit+recommit roundtrip.
start and end must be multiples of the runtime page size (page_size()).
A no-op if the range is out of bounds (end > self.len()); an empty
range is a no-op only when page-ALIGNED — an empty MISALIGNED range
(start == end, endpoints not page multiples, e.g. decommit(1, 1))
is a contract violation like any other, with the SAME profile split
as every other violated range: a silent no-op in a RELEASE build
(the forwarded free function returns at start >= end once the
debug_assert! is compiled out) and a tripwire panic in a DEBUG
build — EXCEPT on a huge-page reservation, where a NON-huge-aligned
(or inverted) range never reaches the forward at all, so it is a
silent no-op on EVERY profile there and the debug tripwire never
fires (see # Panics; task #1084/M2 wrote the split into # Panics,
task #1097/L4 qualified this summary line to match, task #1108 added
the huge exception that the paragraph below and # Panics both
already stated but this sentence did not; task #1140 narrowed the
huge exception to “non-huge-aligned or inverted” — see below).
Contract violations, by build profile (task #1051, narrowed task
#1140): this method forwards to the free decommit function
UNFILTERED whenever it forwards at all, so a violated range
(start > end, or an endpoint not a multiple of
page_size()) follows that function’s
documented profile split exactly on a NON-huge reservation — a silent
no-op in a RELEASE build (no OS call, nothing recorded), a tripwire
panic in a DEBUG build. On a HUGE-page reservation
(Self::is_huge == true), whether this method forwards at all
now depends on the range (task #1140, Linux/Android kernel >= 5.18
only): a WELL-FORMED range that is ALSO aligned to the huge page size
(2 MiB) at both endpoints forwards to the real backend exactly like a
non-huge reservation would (and can therefore reach that same debug
tripwire, only for a range that manages to be simultaneously
huge-aligned AND page-size-misaligned — impossible in practice since
2 MiB is already page-size-aligned on every supported page size, so
this case cannot actually occur); every OTHER range on a huge
reservation (not huge-aligned, or start > end) never reaches the
forward and is a silent no-op on every profile (see # Panics).
Self::try_decommit is the fallible form: it reports a violated
range as Err on every profile — including huge reservations (task
#1084/M3) — and never trips the tripwire.
See decommit for platform divergence notes (Windows crashes on write
before recommit, Linux and Android do not), huge-page incompatibility,
and Darwin zero-fill caveats. Under the bench-internals feature, the
huge_decommit_attempts counter (not an intra-doc link: bench-internals is excluded from the published docs.rs feature set) is incremented when decommit
is called on a huge-page reservation with a range that is NOT eligible
for the Linux/Android >= 5.18 huge-aligned real-call path (i.e. the
counter tracks calls that hit the silent-no-op path, not every call on
a huge reservation — task #1140 narrowed this from “every huge-reservation
call” to “every huge-reservation call that is actually skipped”).
The Linux/Android kernel >= 5.18 eligible-forward path itself
requires the huge-pages feature (task #1156, finding F10) —
Self::is_huge does NOT. The eligibility check this method
consults (linux_huge_range_is_madvise_eligible) is compiled only
under #[cfg(all(not(miri), not(aligned_vmem_mock), any(target_os = "linux", target_os = "android"), feature = "huge-pages"))]; without that
feature enabled, EVERY range on a huge-flagged reservation takes the
silent-no-op early-exit above, unconditionally, on every platform —
there is no huge-aligned-range exception without the feature.
Self::from_raw_parts no longer creates a mismatch here (task
#1172/M1-hybrid, closing finding M2): that constructor now
assert!s that granted_huge: true requires the huge-pages feature
to be enabled in THIS crate, so a reservation with is_huge() == true
cannot exist without huge-pages — the scenario this paragraph used
to describe (an adopted MAP_HUGETLB reservation reporting
is_huge() == true while decommit/try_decommit silently and
permanently skip the backend regardless of range or kernel version,
because the CONSUMER’s Cargo feature set diverged from what the flag
promised) can no longer arise: without huge-pages, adopting such a
reservation panics at construction instead of silently under-serving
it later. decommit_lazy is NOT a
workaround (task #1172, correcting the advice this paragraph used
to give): Self::decommit_lazy skips its backend call
UNCONDITIONALLY for every huge-flagged reservation, on every
platform, regardless of feature flags or kernel version — it has no
Linux >= 5.18 huge-aligned carve-out at all (see its own doc). Routing
around a huge-pages-gated no-op into a permanent no-op is not a
substitute. If you cannot enable huge-pages, either accept the
no-op (RSS will not drop for this reservation) or track the huge-page
state yourself and avoid relying on either decommit path for it.
§Panics
DEBUG builds only, and only for a contract-violating range (start > end, or an endpoint not a multiple of the runtime
page_size()) that actually reaches the
forwarded free decommit: unconditionally true on a NON-huge
reservation, or — since task #1140 — on a huge reservation whenever the
range happens to be huge-page-size-aligned at both endpoints (in
practice this can only be a WELL-FORMED range, since a huge-page-size
multiple is always also a page_size() multiple, so the tripwire is
not actually reachable through the huge-aligned path — this bullet
exists to be precise about the forwarding rule, not because a real
input triggers it). That includes an EMPTY MISALIGNED range such as
decommit(1, 1) — emptiness is NOT a pre-check (task #1084, finding
M2, rewrote this section, which previously claimed “empty and
out-of-bounds ranges are checked by this method first and never
panic”; only the out-of-bounds half of that sentence was true). The
two classes that never panic on any profile: out-of-bounds
(end > self.len()), the one range class this method itself
pre-checks, and an empty PAGE-ALIGNED range (start == end, both
endpoints multiples of page_size()), which forwards as
well-formed. On a huge-page reservation (Self::is_huge == true)
a range that is NOT huge-page-size-aligned at both endpoints (or is
inverted, start > end) never reaches the tripwire: it is a silent
no-op there on every profile, same as before task #1140. RELEASE
builds silently skip a violated range regardless of huge-page status.
This is the free function’s own documented panic surface reached
through the safe method, not a new one (task #1079 added this
# Panics section to a doc that previously promised “the same
silent-skip behavior as the free decommit function” with no profile
qualifier; task #1084 corrected its empty-range claim; task #1140
narrowed the huge-page exception). A poisoned page-size query is
NOT a panic source, on any profile (task #1173/L1) — see the free
decommit’s own “Contract violations, by build profile” section
for why that state is a silent no-op unconditionally, unlike the
range-contract tripwire this section describes.
Sourcepub fn try_decommit(
&mut self,
start: usize,
end: usize,
) -> Result<DecommitOutcome, VmemError>
pub fn try_decommit( &mut self, start: usize, end: usize, ) -> Result<DecommitOutcome, VmemError>
Fallible Self::decommit: Ok(DecommitOutcome) on a well-formed
range, Err(VmemError::invalid_argument()) if the offsets violated
the contract (misaligned, start > end, or end > self.len()) — on
EVERY reservation kind, huge included (task #1084/M3: the huge-page
skip used to sit ahead of validation and answer Ok(()) for a
malformed range on a huge reservation, disagreeing with both this
promise and the free try_decommit’s validate-first order — that
ordering is unchanged by task #1180, only the Ok payload is new).
Never panics on any build profile: the violation is rejected here,
before the eager path’s tripwire can see it.
Ok payload, task #1180 (PUB-R2 phase 2): before this task the
Ok case was a bare Ok(()), unable to distinguish “the range was
empty”, “this is a huge-page reservation and the backend call was
skipped”, “the backend was called and the OS refused it”, and “the
backend was called and the OS accepted it” — all four collapsed into
the same signal. DecommitOutcome now names each case:
DecommitOutcome::Skipped— an empty page-aligned range (start == end), OR a well-formed non-empty range on a huge-page reservation that does not reach the real backend (see the “huge-page reservations” paragraph below for exactly which ranges those are). No syscall was issued either way.DecommitOutcome::Advised— the SELECTED BACKEND accepted the call — see that variant’s own doc for the native-vs-mock-vs-miri split. Never a claim that physical pages were actually reclaimed, even on the native backend. Task #1174 (closed) addedci_hugetlb_real_pool_decommit_actually_zeroes_memory_on_reaccess(tests/decommit_capability.rs), hard-enabled in thealigned-vmem-hugetlb-realCI job: it writes a non-zero pattern, decommits an eligible huge-aligned range under a realMAP_HUGETLBgrant, and hard-asserts EVERY byte reads back zero — proving zero-fill-on-readback for that one case. What #1174 did NOT prove and does not claim to: physical reclaim to the OS/hugetlb pool. That same CI job’s own comments are explicit thatHugePages_Free(the kernel’s pool-page-count) is logged only as an OBSERVATION around the test, never a pass/fail gate, because it is a kernel-global counter shared with the job’s other concurrent reservations and cannot be safely attributed to one test’s owndecommit()call. So: zero-fill on readback is proven for the real-HugeTLB/eligible-range case, on a Linux runner — the code path itself is gated on Linux and Android as a pair (as every huge-page mechanism in this crate is), so the Android half is inherited from that sharedcfg, not separately executed by any CI job; physical page return to the pool remains unmeasured. Do not conflate the two when readingAdvised.DecommitOutcome::Refused— the backend call was made and the OS/kernel refused it (carries the capturedVmemError).
This is the safe, bounds-checked alternative to the free try_decommit
function for callers already holding a Reservation — and the form to
reach for when Self::decommit’s DEBUG-build tripwire is itself
unwelcome. Until task #1079 this was the one fallible pair with no
safe-method twin: recommit/try_recommit and commit_range/
try_commit_range already existed at both layers, and
Self::decommit’s forwarded tripwire message (“Use try_decommit
for the fallible form”) pointed safe-API callers straight at an
unsafe fn with a raw-pointer signature.
Huge-page reservations — FOR A WELL-FORMED RANGE: on Windows, or
on a Linux/Android range that is NOT huge-page-size-aligned at both
endpoints, this method skips the backend call entirely, same as
Self::decommit, incrementing the same bench-internals
huge_decommit_attempts counter (not an intra-doc link: bench-internals is excluded from the published docs.rs feature set) and returning
Ok(DecommitOutcome::Skipped). On Linux/Android kernel >= 5.18 (task
#1140), a well-formed range that IS huge-page-size-aligned at both
endpoints instead forwards to the real backend (same as a non-huge
reservation) and returns whatever that call reports —
DecommitOutcome::Advised or DecommitOutcome::Refused, never
Err, per the “best-effort” note below, but now backed by a real
attempt rather than a guaranteed skip. A malformed range is Err even
on a huge reservation: validation runs before the skip/forward
decision, so neither the counter nor the real backend ever sees a
malformed range (task #1084/M3).
Decommit is best-effort by nature; use
Self::decommit_reclaims_and_zeroes to learn what the platform
actually does. Refused is reported through the Ok payload, not as
an Err of the outer Result — see the free try_decommit’s own
# Errors section for why the outer Result stays reserved for
caller-contract validity.
The Linux/Android >= 5.18 eligible-forward path requires the
huge-pages feature (task #1156, finding F10); Self::is_huge
does not — it is a pure query, always compiled. Without
huge-pages enabled, this method always takes the
skip-and-return-Ok(DecommitOutcome::Skipped) path on a huge-flagged
reservation, on every platform, regardless of range or kernel
version. A huge-flagged reservation cannot even be constructed
without huge-pages, as of task #1172/M1-hybrid —
Self::from_raw_parts now requires the feature to accept
granted_huge: true — see Self::decommit’s doc and
Self::from_raw_parts’s “Correctness contract” section for the
full explanation.
Sourcepub fn decommit_lazy(&mut self, start: usize, end: usize)
pub fn decommit_lazy(&mut self, start: usize, end: usize)
Lazy decommit variant: hint the OS it MAY reclaim [start, end) under memory
pressure, cheaper than Self::decommit (Linux MADV_FREE, macOS/iOS
MADV_FREE_REUSABLE, FreeBSD/DragonFly MADV_FREE, NetBSD/OpenBSD
MADV_FREE, other Unix (including tvOS/watchOS) falls back to MADV_DONTNEED;
Windows falls back to the eager Self::decommit path, which has no lazy equivalent).
This is the safe, bounds-checked alternative to the free decommit_lazy
function for callers already holding a Reservation. It delegates to the
underlying implementation with self.as_ptr() as base and automatically
ensures [start, end) is within the reservation’s usable span.
start and end must be multiples of the runtime page size
(page_size()); an empty or
out-of-bounds (end > self.len()) range is a no-op, and a VIOLATED
range (start > end, or a misaligned endpoint) is a silent no-op on
EVERY build profile — the deliberate eager/lazy asymmetry settled by
task #1072: the eager Self::decommit trips a debug-build
tripwire, this lazy variant has none on any profile.
See decommit_lazy for the platform-specific cost inversion on macOS/iOS
(this variant actually drops RSS immediately there, unlike the eager path)
and other caveats. Under the bench-internals feature, the
huge_decommit_attempts counter (not an intra-doc link: bench-internals is excluded from the published docs.rs feature set) is incremented when decommit is called
on a huge-page reservation (same logic as Self::decommit).
Sourcepub fn recommit(&mut self, start: usize, end: usize) -> bool
pub fn recommit(&mut self, start: usize, end: usize) -> bool
Recommit pages [start, end) previously passed to Self::decommit.
This is the safe, bounds-checked alternative to the free recommit
function for callers already holding a Reservation. It delegates to
the underlying implementation with self.as_ptr() as base and automatically
ensures [start, end) is within the reservation’s usable span.
Returns true if the range is now committed (or the call was a well-formed
no-op — an empty PAGE-ALIGNED range, start == end), and false if the
OS refused to
commit the pages (commit-charge exhaustion / true OOM) OR the offsets
violated the contract below. On false the caller MUST NOT write into
[start, end). Never panics. For the cause use Self::try_recommit.
start and end must be multiples of the runtime page size (page_size()).
A well-formed no-op (an empty PAGE-ALIGNED range, start == end)
returns true; any other contract violation (misaligned, or
start > end, or end > self.len()) returns false.
Sourcepub fn try_recommit(
&mut self,
start: usize,
end: usize,
) -> Result<(), VmemError>
pub fn try_recommit( &mut self, start: usize, end: usize, ) -> Result<(), VmemError>
Fallible Self::recommit: Ok(()) if the range is now committed
(or was a well-formed no-op), Err(VmemError::invalid_argument()) if the
offsets violated the contract (misaligned, or start > end, or end > self.len()),
Err(VmemError) carrying the OS cause on genuine commit failure.
This is the safe, bounds-checked alternative to the free try_recommit
function for callers already holding a Reservation.
Sourcepub fn commit_range(&mut self, start: usize, end: usize) -> bool
Available on crate feature lazy-commit only.
pub fn commit_range(&mut self, start: usize, end: usize) -> bool
lazy-commit only.Commit pages [start, end) within this reservation.
This is the safe, bounds-checked alternative to the free commit_range
function for callers already holding a Reservation. It delegates to
the underlying implementation with self.as_ptr() as base and automatically
ensures [start, end) is within the reservation’s usable span.
After a reserve_aligned_lazy call that left some pages reserved-but-uncommitted,
commit_range commits exactly the requested sub-range so it becomes writable.
Returns true if the range is now committed, false if the OS refused
(commit-charge exhaustion / true OOM) OR the offsets violated the contract
above. On false the caller MUST NOT write into the range. Never panics.
For the cause use Self::try_commit_range.
start and end must be multiples of the runtime page size (page_size()).
A well-formed no-op (an empty PAGE-ALIGNED range, start == end)
returns true; any other contract violation (misaligned, or
start > end, or end > self.len()) returns false.
Sourcepub fn try_commit_range(
&mut self,
start: usize,
end: usize,
) -> Result<(), VmemError>
Available on crate feature lazy-commit only.
pub fn try_commit_range( &mut self, start: usize, end: usize, ) -> Result<(), VmemError>
lazy-commit only.Fallible Self::commit_range: Ok(()) on success (or was a well-formed no-op),
Err(VmemError::invalid_argument()) if the offsets violated the contract
(misaligned, or start > end, or end > self.len()), Err(VmemError) carrying
the OS cause on genuine commit failure.
This is the safe, bounds-checked alternative to the free try_commit_range
function for callers already holding a Reservation.
Sourcepub unsafe fn from_raw_parts(
base: *mut u8,
len: usize,
reservation: *mut u8,
reservation_len: usize,
align: usize,
granted_huge: bool,
) -> Self
pub unsafe fn from_raw_parts( base: *mut u8, len: usize, reservation: *mut u8, reservation_len: usize, align: usize, granted_huge: bool, ) -> Self
Wrap a pre-existing OS reservation (e.g. one obtained from
VirtualAllocExNuma or another platform-specific allocator that
reserve_aligned does not call directly) in a Reservation handle.
The handle then participates in the normal RAII lifecycle: on Drop
(or via release) the underlying reservation is returned to the OS
using the platform-appropriate release routine
(VirtualFree(MEM_RELEASE) on Windows, munmap on Unix,
std::alloc::dealloc on miri).
This is not the inverse of into_parts: that
method returns only 3 of the 6 fields this constructor requires
(reservation_ptr, reservation_len, align), discarding base, len,
and granted_huge entirely. into_parts’s true structural complement
is release, whose signature is exactly the 3-tuple into_parts
returns — that is the intended matched pair for “take ownership out of
RAII, then give it back to the OS manually”. from_raw_parts is a
separate, more general constructor for the cross-crate handoff pattern:
a sibling crate (numa-shim on Windows) issues a platform-specific
reservation call that aligned-vmem itself does not wrap, then adopts
the result via this constructor — it needs base/len too because the
adopted reservation’s usable span need not start at the OS reservation’s
own base (this crate over-reserves size + align and keeps the full
mapping whenever the exact-size fast path misses, or on Windows when
align > 64 KiB, which is exactly that shape).
§Safety
This section covers ONLY memory-safety preconditions: liveness,
exclusive ownership, pointer provenance, and exact-once release. A
violation here is undefined behaviour. Functional/behavioral
requirements — whether granted_huge accurately describes the
mapping, and Windows commit-state compatibility — are NOT memory-
safety preconditions and live in the “Correctness contract” section
below instead (task #1172/M3: this section used to mix both kinds
together, which made it impossible to state honestly that this
crate’s own integration tests deliberately violate some of the
mixed-in conditions while remaining sound — see that section’s
opening paragraph for why that is not a contradiction).
All six values must describe a live, exclusively-owned OS
reservation compatible with aligned-vmem’s release path:
-
baseis the aligned usable start; non-null, valid forlenbytes, aligned toalign. For correctdecommit/decommit_lazybehavior,basemust also be aligned to the runtimepage_size()(not just the compile-timePAGE). On systems with non-4 KiB pages (e.g., 16 KiB on Apple Silicon), passing a 4 KiB-alignedbasewill causedecommit,decommit_lazy, ormunmapcalls to fail silently or returnEINVAL. This alignment to page_size() is NOT checked by the constructor’sassert!— it is the caller’s responsibility to ensure it. -
lenis the usable span size, a non-zero multiple ofPAGE. -
reservationis the underlying OS reservation start (often equal tobase, but may be lower because the reservation is over-reserved to achieve alignment and the full mapping is kept). For correct OS release behavior, it must be aligned to the runtimepage_size(). This alignment to page_size() is NOT checked by the constructor’sassert!— it is the caller’s responsibility to ensure it. -
Under miri specifically,
reservation— NOTbase— MUST be the exact pointer returned by astd::alloc::alloccall, and that call’sLayoutmust equalLayout::from_size_align(reservation_len, align). The mirirelease_reservationreconstructs precisely thatLayoutand handsreservationtostd::alloc::dealloc, which requires the pointer to be the oneallocreturned and the layout to match exactly; anything else is undefined behaviour, not a leak.The distinction between
reservationandbaseis load-bearing here and is why this bullet names one and not the other: they are SEPARATE parameters, andbaseMAY sit at a non-zero offset inside the regionreservationpoints at whenever the caller obtained that region with extra slack to satisfy alignment. Satisfying the provenance requirement atbasewhilereservationpoints somewhere else is exactly the mistake this wording exists to prevent. (This crate’s own miri backend returnsbase == reservation, so the distinction never bites internally — which is what makes it easy to get wrong for a caller-supplied pair.)This requirement is specific to the miri backend; the Windows and Unix backends release by address and do not track allocator provenance. It complements — and does not restate — the
reservation_lenprecision rule below: that one governs the SIZE, this one governs WHICH POINTER and WHERE THE MEMORY CAME FROM. -
reservation_lenmust cover the underlying OS mapping/allocation. The required PRECISION differs per backend, and is spelled out here because the two halves of this rule used to contradict each other (task #1035, finding F9: this bullet said an undersized value “leaks memory (Unix)”, while the “Important” note below said under-reporting on a large-page host is “harmless for correctness” — both about Unix):- Native Unix, ORDINARY (non-huge) mapping:
releasepasses this value straight tomunmap, which ROUNDS THE LENGTH UP to a whole page. A value short of the true mapping by less than one runtime page therefore still unmaps the whole mapping and is harmless — that is exactly the case the “Important” note below describes, and it is the case this crate itself produces on a host whose page size exceedsPAGE. What DOES leak is a value short by a whole page or more: those trailing pages stay mapped for the life of the process. - Native Unix,
granted_huge == true(task #1172/M1, HugeTLB exception to the paragraph above): the “rounds up, so a less-than-one-page shortfall is harmless” reasoning does NOT transfer to a HugeTLB mapping. Linux’s — and Android’s, which shares that kernel interface and is covered by the same#[cfg]gate on the assert below —mmap(2)“Huge TLB mappings” section requires BOTHmunmap(2)’saddrandlengthto be multiples of the huge page size — an undersizedreservation_lenthat is not huge-page-size-aligned getsEINVALfrommunmap, not a rounded-up unmap, and the ENTIRE mapping (plus its pinned physical huge pages) leaks for the life of the process. This crate’s ownreserve_aligned_hugepath already documents and upholds this requirement (seecrates/aligned-vmem/src/os/unix.rs’s huge-page-alignment comments);from_raw_partsdid not previously carry the same requirement for an ADOPTED huge mapping. See the “2 MiB-multiple requirement” bullet in “Correctness contract” below for the checked form of this requirement. - miri:
releasereconstructs aLayoutfromreservation_len/alignand hands it tostd::alloc::dealloc, which requires the EXACT size the allocation was made with — no rounding, and a mismatch is undefined behaviour rather than a leak. The rounding case above cannot arise here: undercfg(miri)query_os_page_size()returnsPAGEunconditionally, sopage_size() == PAGEand there is no larger runtime page to round up to. The exact-size requirement is unqualified under miri. - Windows:
VirtualFree(MEM_RELEASE)ignores the length entirely, so the value is advisory — reporting whateverReservation::reservation_lenwould report for an equivalent reservation is sufficient.
Important: On hosts where the OS page size exceeds
PAGE(e.g., 16 KiB on Apple Silicon macOS, 64 KiB on some Linux configurations),reservation_lenmay under-report the actual OS mapping size —mmaprounds its length argument up to the page size, soreserve_aligned(PAGE, PAGE)actually maps a full 16 KiB page whilereservation_len()returns4096. This is harmless for correctness on an ORDINARY mapping (munmaprounds its length argument up the same way;VirtualFree(MEM_RELEASE)ignores the length on Windows) — it does NOT apply to agranted_huge == truemapping on Linux or Android, per the HugeTLB bullet above — but it meansreservation_lenis a logical length, not a measure of the true OS reservation size. It must be a non-zero multiple ofPAGEwithreservation_len >= len + (base - reservation). - Native Unix, ORDINARY (non-huge) mapping:
-
alignis a power of two>= PAGEand matches the alignment the OS reservation was created with. -
granted_hugeitself carries NO memory-safety precondition: it is stored and read back verbatim by every unsafe operation this constructor,Drop, andrelease_reservationperform, and branched on by neither. Its accuracy requirement, its interaction withreservation_len’s HugeTLB exception above, and Windows commit-state compatibility are all functional requirements — see “Correctness contract” below.
The reservation must be released exactly once — by dropping this
handle, or by extracting via into_parts and calling release
manually. Constructing two Reservation handles over the same OS
reservation is undefined behaviour (double release).
§Correctness contract
These requirements are NOT memory-safety preconditions — violating one
changes observable behavior (a query result, a dispatch decision, or
an OS-level no-op/leak) but never causes undefined behavior by itself.
This crate’s own integration tests deliberately violate the
granted_huge-accuracy requirement below (see
tests/reservation_decommit_contract.rs’s
method_try_decommit_reports_malformed_range_on_huge_flagged_reservation
and tests/decommit_capability.rs‘s
simulated_huge_flag_drives_the_same_branch_dispatch_on_any_host) to
exercise huge-page branch dispatch without a real hugetlb-configured
host — both tests’ own SAFETY comments enumerate every reader of the
flag and confirm none is memory-safety-relevant. That is a deliberate,
reviewed use of this contract’s slack, not a bug in either the tests
or this documentation; production callers must still pass a truthful
granted_huge, because the CONSEQUENCE of getting it wrong (below) is
real even though it is not UB.
-
granted_hugeaccuracy. MUST accurately reflect whether the OS actually granted huge pages for this reservation. Passtrueonly if the reservation was obtained via a huge-page allocation (e.g.reserve_aligned_huge) and the OS confirmed the grant (viaReservation::is_huge()or equivalent platform-specific detection). Consequence of a wrong value:Reservation::is_huge()reports the wrong value, and any decommit-availability decision made from that wrong result is wrong (on huge pages,decommitis a silent no-op — RSS does not drop and reads return the old data). This changes DISPATCH and query results, never memory safety. If you cannot determine whether the OS granted huge pages, you MUST passfalseand usereserve_alignedinstead. If you KNOW the mapping is a HugeTLB mapping whose granularity is not 2 MiB (e.g. 1 GiB on Linux or Android), NEITHER flag value is legal:trueviolates the 2 MiB-multiple contract below, andfalsedoes not make the kernel’smunmapalignment requirement go away — it only misreportsis_huge()(violating this accuracy bullet) and routes release through ordinary-munmap assumptions, wheremunmap(2)on aMAP_HUGETLBmapping still requiresaddrandlengthto be multiples of THAT mapping’s huge-page size, so a release whose shape satisfies 2 MiB but not the mapping’s real granularity can failEINVALand leak the entire mapping, including its pinned pages from the (bounded) hugetlb pool. Do not construct aReservationover such a mapping at all. “Can leak”, not “will leak”: this consequence is reasoned fromman 2 munmapand this crate’s ownos/unix.rscontract docs (unix_reserve’s task-#714 note), not executed in CI — no CI host configures a hugetlb pool larger than 2 MiB, and this crate never creates such a mapping itself, always requestingMAP_HUGE_2MB(crates/aligned-vmem/src/os/unix.rs). -
2 MiB-multiple requirement, Linux/Android,
granted_huge == true(task #1172/M1-hybrid). Ontarget_os = "linux"or"android", whengranted_hugeistrue, this constructor additionallyassert!s thatlen,reservation_len,reservation,base, and the offsetbase - reservationare all multiples of 2 MiB (this crate’s one supported HugeTLB granularity,MAP_HUGE_2MB; seecrates/aligned-vmem/src/os/unix.rs’sLINUX_HUGE_PAGE_SIZE). Five names are listed, but only FOUR are independent:reservationandbaseboth being 2-MiB multiples already implies their differencebase - reservationis too, so the offset conjunct can never be the one that fails (task #1196/OX6-L1). It stays in the assert anyway — for the panic message’s diagnostics, and because it would become load-bearing again if either address conjunct were ever dropped. Consequence of a violation: an immediate, loud, attributable panic at the call site — not deferred toDrop, and not a silent leak — because a non-2-MiB-alignedreservation/reservation_lenon a real HugeTLB mapping would otherwise make the eventualmunmapfailEINVALand leak the entire mapping (see thereservation_lenbullet in# Safetyabove). This assert narrows whatgranted_huge == trueis allowed to MEAN through this constructor to “the mapping is in this crate’s own 2 MiB HugeTLB format” — it does not by itself prove the memory is reallyMAP_HUGETLB-backed (that remains a# Safetyprecondition the assert cannot check), only that its shape is consistent with being so. Owner decision (2026-08-20, task #1190): NO. A HugeTLB mapping whose page granularity is not 2 MiB (e.g. 1 GiB) is NOT supported for adoption through this constructor — this assert is the crate’s CURRENT contract, not a temporary narrowing pending a wider one (the decision is recorded in https://github.com/PHPCraftdream/sefer-alloc/blob/main/docs/CORRECTNESS_OPEN_ITEMS.md item 90’s OPEN QUESTION block). A future “yes”, if it ever comes, would arrive as an ADDITIVE new constructor carrying typed huge-granularity metadata — not as a relaxation of this assert presented as a bugfix. That widening would be additive rather than semver-breaking precisely because task #1172 already narrowed whatgranted_huge == trueMEANS here to “the mapping is in this crate’s own 2 MiB HugeTLB format”, so thisboolstays truthful forever for the one case it admits, and because the adoption surface is structurally extensible where it is public:Reservation’s fields are private, andReservationParts,ReservationFullParts, and everymock::Callvariant are#[non_exhaustive]. -
huge-pagesfeature required to passgranted_huge: true(task #1172/M1-hybrid, closing finding M2 as a consequence). Passinggranted_huge: truewhen this crate is built WITHOUT thehuge-pagesfeature is itself a contract violation andassert!s immediately, for the same “loud at the call site, not silently divergent later” reason as the 2 MiB bullet above. Before this requirement, a caller who adopted aMAP_HUGETLBmapping through their own crate but did not separately enablehuge-pagesin THIS crate’sCargo.tomlgotis_huge() == true(accurately reflecting what they passed) whiledecommit/try_decommitsilently, unconditionally skipped the backend call regardless of range or kernel version (finding M2: the SAME live mapping served or skipped decommit depending on the CONSUMER’s Cargo feature set, invisible at the call site). Requiring the feature to accept the flag at all means there is no longer a huge-flagged ADOPTED reservation withouthuge-pagesenabled, so the divergent-behavior scenario cannot arise — seeSelf::decommit’s doc for the full eligible-forward explanation this closes the gap in. -
Windows commit state. On Windows, the reservation’s commit state (which pages are committed vs. reserved-only) must be compatible with the
granted_hugevalue:-
If
granted_huge == false, the reservation may be in any valid commit state: fully committed (created viareserve_alignedor the single-call Windows fast path), partially committed (created via the two-callreserve_aligned_lazypath), or reserved-only (not a common pattern but valid). -
If
granted_huge == true, the reservation MUST have been created withMEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGESin a single call (the only way Windows grants large pages). The crate itself only produces such reservations via itsreserve_aligned_hugesingle-call fast path. The crate’s own two-callreserve_aligned_lazypath (which issuesVirtualAlloc(MEM_RESERVE)followed byVirtualAlloc(MEM_COMMIT)) is incompatible withgranted_huge == true, becauseMEM_COMMITcannot be combined withMEM_LARGE_PAGESon a pre-reserved region — MSDN requires all three flags in a single call.
Consequence of a violation:
Reservation::is_huge()reports a value inconsistent with the reservation’s actual commit state — the same DISPATCH/query-result consequence as the accuracy bullet above, not a new failure mode. If you adopted a reservation from another source and cannot determine whether it was created with the one-call large-page path, you MUST passgranted_huge == false. -