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
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
//! Phase 12.3 -- the alloc face: [`SeferAlloc`], an `unsafe impl GlobalAlloc`
//! over the global heap registry (Phase 12.2) via raw-pointer TLS (Phase 12.3).
//!
//! This is the **drop-in face** -- the campaign's victory deliverable. One
//! substrate (the segment-backed, self-hosted, registry-resident heap
//! allocator), two faces: the `Handle` face (typed, generational,
//! relocatable) and this `alloc` face (raw `*mut u8`, drop-in
//! `#[global_allocator]` replacement).
//!
//! ## Phase 12.3 rewiring
//!
//! Previously (Phase 11) this face routed through
//! `RefCell<Option<Heap>>` via `with_heap_try`. That binding ABORTED under
//! libtest's reentrant harness: `RefCell::try_borrow_mut` returns `Err` on
//! a reentrant borrow → the alloc face returned null → std aborted.
//!
//! Phase 12.3 replaces that with [`tls_heap::current`](super::tls_heap::current):
//! a raw `Cell<*mut HeapCore>` TLS cache (no borrow state to fail) over the
//! global [`HeapRegistry`](crate::registry::HeapRegistry). The heap lives in
//! a registry slot (not in TLS); thread exit abandons + recycles the slot
//! (not drops the heap). The alloc face is therefore **reentrancy-safe**
//! (M5) and **never-null** (M10): [`current`] returns a non-null pointer in
//! every case (cached slot, fresh claim, or the process-global fallback
//! heap).
//!
//! ## M5 (reentrancy-freedom) -- how it is upheld
//!
//! The whole point (§4 M5, §8 of `ALLOC_PLAN.md`): when WE are the global
//! allocator, ANY use of `Vec`/`Box`/`HashSet`/`std::alloc`/`format!` on the
//! alloc path would recurse infinitely. This module contains NONE of those.
//! `current()` is a plain thread-local load + null check. `bind_slow` claims
//! a registry slot (which bootstraps via the OS aperture, never `std::alloc`);
//! the only `std::alloc` touch is the `Box<AtomicPtr<u8>>` TFS handle under
//! `alloc-xthread`, installed on the bind path (outside the registry
//! bootstrap). The `HeapCore` alloc/dealloc paths are pure safe integer
//! arithmetic + the `node` seam (intrusive pointer r/w). No `std` collection
//! is reachable from here.
//!
//! ## No-panic -- how it is upheld
//!
//! A panic in `#[global_allocator]` aborts the process (§8 of `ALLOC_PLAN.md`).
//! Every entry point here returns null on failure and NEVER panics:
//! - `alloc`: `current()` → `&mut HeapCore` → `HeapCore::alloc` (returns
//! null on OOM). If `current()` itself yields the fallback (TLS teardown),
//! the fallback's `with_heap` returns `None` only on true OOM → null.
//! - `dealloc`: `current()` → `HeapCore::dealloc`. If TLS is torn down, the
//! fallback's `with_heap` deallocs under the spinlock; a torn-down-TLS
//! dealloc still routes correctly (the segment's owner routes via the
//! header). On any failure this is a no-op (the block is leaked safely).
//! - `realloc`: `alloc` + copy + `dealloc`, all null-returning.
//! - `alloc_zeroed`: `alloc` + zero-fill.
//!
//! [`current`]: super::tls_heap::current
// The crate is `#![deny(unsafe_code)]` with `alloc-global` on (see `src/lib.rs`);
// this is the documented alloc-face seam. `allow` lifts the crate-level
// deny for this file only -- `unsafe` anywhere else in the crate is a hard
// error. The ONLY `unsafe` here is the `unsafe impl GlobalAlloc` (the trait
// is `unsafe`) plus the `// SAFETY:`-annotated pointer handoff to HeapCore.
use ;
use fallback;
use current_for_alloc;
use current_for_alloc_with_config;
use CurrentHeap;
/// The drop-in `GlobalAlloc` face over the `sefer-alloc` segment substrate,
/// routed through the global heap registry (Phase 12.2) via raw-pointer TLS
/// (Phase 12.3).
///
/// Install it as your process's global allocator (simple form — uses all
/// large-cache defaults):
///
/// ```no_run
/// # #[cfg(feature = "alloc-global")]
/// # {
/// use sefer_alloc::SeferAlloc;
///
/// #[global_allocator]
/// static A: SeferAlloc = SeferAlloc::new();
/// # }
/// ```
///
/// Or configure the large-cache knobs at compile time (requires the
/// `alloc-decommit` feature):
///
/// ```no_run
/// # #[cfg(all(feature = "alloc-global", feature = "alloc-decommit"))]
/// # {
/// use sefer_alloc::{SeferAlloc, LargeCacheConfig, LargeCacheMode};
///
/// const CONFIG: LargeCacheConfig = LargeCacheConfig::new()
/// .budget_bytes(512 * 1024 * 1024)
/// .headroom_bytes(64 * 1024 * 1024)
/// .decay_interval_ms(200)
/// .decay_rate_percent(25)
/// .mode(LargeCacheMode::Lazy);
///
/// #[global_allocator]
/// static GLOBAL: SeferAlloc = SeferAlloc::with_config(CONFIG);
/// # }
/// ```
///
/// Each thread gets its own heap slot in the global registry (lazily claimed
/// on first allocation via the raw-pointer TLS binding -- no `RefCell`, no
/// reentrant-borrow failure). `alloc`/`dealloc`/`realloc`/`alloc_zeroed`
/// route through the per-thread heap's segment-centric `BinTable` free lists
/// (the Phase 12.1 hot path). With `alloc-xthread`, cross-thread `dealloc`
/// routes through the Phase 10 Treiber stack, now stamped from the
/// registry-resident heap (12.3 owner stamping).
///
/// Thread exit abandons the heap's segments back to the registry (a no-op
/// stub in 12.3 -- segments leak until 12.4 adoption; bounded and sound) and
/// recycles the slot for reuse. A primordial fallback heap (§2.3) serves
/// the pre-TLS / post-teardown windows, so the face is **never-null for a
/// serviceable request** (M10).
///
/// This is the **alloc face** of one substrate; the **handle face**
/// (`Region<T>` / `Handle<T>`) is the typed, generational view over the same
/// governed memory. See `docs/ALLOC_PLAN.md` §3 "The two faces".
// SAFETY (the trait obligation): `GlobalAlloc` requires that `alloc`/
// `alloc_zeroed`/`realloc` return valid memory for the requested `Layout`
// (or null on failure), and that `dealloc` receives a pointer previously
// returned by an allocating method. We delegate to `HeapCore::alloc`/
// `dealloc`/`realloc`/`alloc_zeroed`, which uphold M1 (validity), M3 (no
// overlap), and M4 (alignment/size fidelity) -- verified by the Phase 8/9
// differential proptests and miri. `HeapCore` returns null on OOM (never
// panics -- the substrate panic sites were hardened in Phase 11). If the
// TLS heap is unavailable (thread teardown), `current()` returns the
// process-global fallback heap (never null); `dealloc` on the fallback is
// sound under the fallback's spinlock. M10 (never-null for serviceable
// requests) is upheld: the only null return is true OOM.
unsafe