#[non_exhaustive]pub enum DecommitOutcome {
Skipped,
Advised,
Refused(VmemError),
}Expand description
The observed result of one try_decommit /
Reservation::try_decommit call —
task #1180 (PUB-R2 phase 2), replacing the pre-#1180
Result<(), VmemError> return, which reported only whether the
CALLER’S ARGUMENTS were valid, never what the OS actually did (or was
even asked to do).
The outer Result<DecommitOutcome, VmemError> still reports a caller
contract violation exactly as before (Err(VmemError::invalid_argument())
for a malformed range, Err(VmemError::os_refusal_unknown_code()) if the
one-time OS page-size query failed) — see
try_decommit’s own # Errors section. What is
new is the Ok payload: three variants that distinguish “no backend
call was made” from “the backend call was made and refused” from “the
SELECTED BACKEND accepted the request”, where the pre-#1180 signature
collapsed all three into the same Ok(()). That acceptance does NOT
by itself imply that a real OS syscall ran — under the
aligned_vmem_mock cfg or miri no syscall runs at all, and Advised
is the simulated backend’s own unconditional answer (see
DecommitOutcome::Advised’s own doc for the per-backend meaning).
None of the three variants is a claim about physical memory having
actually been reclaimed. Decommit is best-effort by nature (see
Reservation::decommit_reclaims_and_zeroes
for which platforms guarantee reclaim+zero-fill at all) — this type
answers “what did this call do”, not “what did it accomplish”.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
Skipped
No backend call was made — a Rust-level skip, decided before any syscall. Two sources, both a well-formed range:
- An empty range (
start == end), on eithertry_decommitorReservation::try_decommit— a deliberate no-op, checked before any huge-page eligibility question even applies. - A well-formed, in-span, non-empty range on a huge-page
reservation (
Reservation::is_huge==true) that does not take the Linux/Android kernel >= 5.18 huge-aligned real-backend path — seeReservation::decommit’s “Huge-page granularity” doc for the exact eligibility split (Windows: always; Linux/Android: only a range that is NOT huge-page-size-aligned at both endpoints, or when thehuge-pagesfeature is off). OnlyReservation::try_decommithas anis_huge()to consult, so this second source is exclusive to it — the freetry_decommitfunction has no such eligibility check and, for a non-empty range, always forwards to the backend.
Advised
The SELECTED BACKEND accepted the request. What that means depends on which backend is actually compiled in — this variant does NOT by itself imply that a real OS syscall ran:
- Native backend (no
aligned_vmem_mockcfg, not miri): a real syscall was made and the kernel/OS accepted it — Linuxmadvise(2)returned0, or WindowsVirtualFree(MEM_DECOMMIT)returned nonzero (success). aligned_vmem_mockcfg (RUSTFLAGS="--cfg aligned_vmem_mock"): no syscall runs at all — the mock backend records the call into its call log and unconditionally reportsAdvised, without touching the OS (see thecrate::mockmodule doc). This is a deliberate simulation, not an OS acceptance.- miri: the backend is a no-op that always “succeeds” — miri models no RSS, so there is no real syscall to refuse.
Never a claim that physical pages were actually returned to the
OS, even on the native backend — let alone that a subsequent access
re-faults zeroed memory. That gap between “the kernel accepted the
advice” and “the kernel acted on the advice as this crate’s docs
describe” is exactly what
Reservation::decommit_reclaims_and_zeroes
answers — it already reports false under aligned_vmem_mock and
under miri (in addition to Darwin/BSD), precisely because “the
selected backend accepted the request” and “the OS actually
reclaimed physical memory” are two different questions, and that
query is the one that distinguishes them; Advised is not a second,
competing channel for the same distinction and must not be read as
one. On Darwin/the four BSDs specifically (the native backend, not
mock/miri), MADV_DONTNEED is well known to return 0 while the
pages stay resident (advisory semantics) — Advised there is
expected and unremarkable, not a stronger signal than the platform
actually gives.
No separate Simulated variant, by design (task #1212). This
type is already #[non_exhaustive], so adding a variant later would
NOT be a semver break — every external match on DecommitOutcome
already requires a wildcard arm. A Simulated variant was
considered and deferred (not rejected outright) because the
mock/miri-vs-real distinction it would carry is already expressed by
the capability-query family:
Reservation::decommit_reclaims_and_zeroes
and, for the commit side,
lazy_commit_is_honored both
already answer false under aligned_vmem_mock/miri specifically
BECAUSE those cfgs substitute simulation for the real OS call — see
each query’s own doc for its exclusion list. A third Simulated
enum variant would duplicate a distinction the crate already exposes
as a queryable bool; revisit if a caller need emerges that the
existing query family cannot serve (e.g. wanting to branch on
simulated-vs-real from a single DecommitOutcome value with no
second call).
Refused(VmemError)
The backend call was made and the kernel/OS refused it — Linux/Android
madvise(2) returned -1 (e.g. EINVAL on a pre-5.18 kernel
receiving a HugeTLB range, or any other kernel-side rejection), or
Windows VirtualFree(MEM_DECOMMIT) returned zero (failure, e.g.
GetLastError() on a large-page region). Carries
VmemError::last_os_error captured immediately after the failing
call, same capture-timing contract as every other OS-refusal error in
this crate.
One optional fault-injection second source of this payload (task
#1219): with the fault-injection feature enabled AND
fault_injection::arm_fail_next_decommit
armed, the syscall is replaced by a simulated failure and the payload
is VmemError::os_refusal_unknown_code instead — no syscall ran, so
there is no last_os_error to capture (the same task-#713 rule the
commit-side seam follows). fault-injection is a public, process-global,
opt-in Cargo feature (see its own Cargo.toml doc comment) — “test-only”
understates it, since any downstream consumer that enables it can arm
this path in a production build too. A build without that feature
enabled can only reach the real-backend path described above.
Implementations§
Source§impl DecommitOutcome
impl DecommitOutcome
Sourcepub const fn is_skipped(&self) -> bool
pub const fn is_skipped(&self) -> bool
true for DecommitOutcome::Skipped.
Sourcepub const fn is_advised(&self) -> bool
pub const fn is_advised(&self) -> bool
true for DecommitOutcome::Advised.
Sourcepub const fn is_refused(&self) -> bool
pub const fn is_refused(&self) -> bool
true for DecommitOutcome::Refused.
Trait Implementations§
Source§impl Clone for DecommitOutcome
impl Clone for DecommitOutcome
Source§fn clone(&self) -> DecommitOutcome
fn clone(&self) -> DecommitOutcome
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more