1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
use ;
use PAGE;
use crate;
use crate;
/// Cache for [`page_size`]. Three states:
///
/// - `0` — not yet queried: the next call queries the OS and stores one of the
/// other two states.
/// - [`PAGE_SIZE_QUERY_FAILED`] (`usize::MAX`) — queried, and the OS answer was
/// unusable (an error return, zero, not a power of two, or below [`PAGE`]).
/// Cached like a success so the degraded state is stable for the process
/// lifetime and the hot path stays one relaxed load.
/// - any other value — the validated OS page size (a power of two `>= PAGE`).
///
/// `pub(crate)` so the `page_size_override` test seam can store into it.
pub static PAGE_SIZE_CACHE: AtomicUsize = new;
/// The "queried and the answer was unusable" cache state (see
/// [`PAGE_SIZE_CACHE`]). `usize::MAX` is not a power of two, so it can never
/// collide with a real validated page size — and, deliberately, no real
/// offset is a multiple of it except `0`, so even a page-multiple validator
/// that reads it RAW (without an explicit poison check) rejects every
/// non-empty range: the poison fails closed by arithmetic, not by policing.
pub const PAGE_SIZE_QUERY_FAILED: usize = usizeMAX;
/// Internal implementation of page size validation.
pub
/// Raw three-state page-size accessor: the validated OS page size, or
/// [`PAGE_SIZE_QUERY_FAILED`] when the one-time OS query produced an unusable
/// answer. Never returns `0` (the cold path resolves the not-yet-queried
/// state before returning).
///
/// This is what every VALIDATOR in this crate consults — the poison must be
/// visible to validation, which is exactly what the public [`page_size`]
/// deliberately hides (it maps the poison to the conservative [`PAGE`] floor
/// so its "power of two" contract holds). Hot path: one relaxed load and a
/// zero test, identical to the pre-poison `page_size` body.
pub
/// Cold path of [`page_size_or_poison`]: query the OS once and cache either
/// the validated answer or the poison. Racing threads may both query; the
/// query is idempotent and both store the same value.
/// Return the OS page size in bytes, querying the OS once and caching the
/// result.
///
/// Uses `sysconf(_SC_PAGESIZE)` on Unix and `GetSystemInfo` on Windows; under
/// miri it returns [`PAGE`] (4 KiB) by design (there is no real OS page). The
/// value is cached in a process-wide atomic after the first call, so repeated
/// calls are a single relaxed load. The returned value is always a power of
/// two and at least [`PAGE`].
///
/// **Correctness:** on Apple Silicon macOS the page size is 16 KiB, and on
/// some Linux configurations 64 KiB. Use this value (not [`PAGE`]) to round
/// decommit offsets: `decommit`/`decommit_lazy` validate BOTH endpoints
/// against `page_size()` before reaching the OS, and a misaligned endpoint is
/// a crate-level fail-closed skip (the call returns without any effect).
/// That crate-level validation is the load-bearing guard — do not rely on the
/// OS to reject a misaligned range for you. The kernels' own behavior is
/// asymmetric and platform-divergent: Linux `madvise(2)` rejects the call
/// only when the ADDRESS is misaligned, and rounds a misaligned LENGTH **up**
/// to the real page (touching memory past the requested range); Windows
/// `VirtualFree(MEM_DECOMMIT)` rejects nothing and widens the range in BOTH
/// directions (it decommits every page containing any byte of the range).
///
/// **If the one-time OS query fails** (`sysconf` returning an error, or a
/// nonsensical answer — not observed on any supported platform: on
/// Linux/macOS/Windows the page size comes from process-startup data that
/// cannot fail to exist), this function still returns [`MIN_PAGE`](crate::MIN_PAGE)
/// (= [`PAGE`], 4 KiB) so it stays infallible — but the crate records the
/// failure and every page-granular STATE operation fails closed for the
/// process lifetime: `decommit`/`decommit_lazy` become no-ops,
/// `recommit`/`commit_range` return `false`, the `try_*` forms and the lazy
/// reservation constructor report an OS-side no-code error (see
/// [`VmemError::os_refusal_unknown_code`](crate::VmemError::os_refusal_unknown_code)),
/// and [`try_page_size`](crate::try_page_size) returns `Err`. Reserving,
/// using, and releasing memory are unaffected — they never depend on the
/// runtime page size. Rationale: with the real page size unknown, a
/// decommit granularity guess could make the OS round a length up across
/// live data; refusing to decommit loses nothing but an optimization,
/// while guessing risks silent data loss.
///
/// **One bookkeeping value keeps moving even in the degraded state (task
/// #1156, finding F15):**
/// `LazyReservation::shrink_committed` (feature `lazy-commit`; not an
/// intra-doc link here — `page_size` is compiled unconditionally and
/// `LazyReservation` is not, so a real link would break whenever
/// `lazy-commit` is off, e.g. under plain `--no-default-features`) lowers
/// its own watermark unconditionally, even though the `decommit` call it
/// issues underneath is the no-op described
/// above. The watermark is rounded UP using the conservative [`PAGE`] value
/// this degraded state substitutes for the unknown real page size, so the
/// dropped range can be smaller than a true page on a host with a larger
/// real page — but `shrink_committed` documents the watermark as this
/// crate's own bookkeeping guarantee, "not a claim about residency", so a
/// lowered watermark under poison is consistent with that contract, not a
/// violation of it: no committed byte the caller is entitled to is released
/// (the underlying `decommit` did nothing), only the handle's own record of
/// what it considers committed moves. No other primitive's return value
/// (`bool`, `Result`) is affected by this — `shrink_committed` is the one
/// entry point in this enumeration with no fallible/boolean signature to
/// report the degraded state through, because it never had one even outside
/// the degraded state.
/// One-time raw OS page-size query, before validation. Routed through the
/// `aligned_vmem_page_size_override` test seam when that cfg is on, so a test
/// can simulate a failed query (or a larger-page host) on any hardware; the
/// production build compiles the seam out entirely.
pub
pub
pub
pub