sefer_alloc/lib.rs
1//! # sefer-alloc
2//!
3//! A safe, *handle-addressed* region store. Instead of handing out raw
4//! pointers, a [`Region<T>`] hands out small generational [`Handle<T>`]
5//! values; the bytes live in a dense, cache-friendly backing store that the
6//! region is free to move. A stale handle never resolves to a live value — it
7//! returns `None`, never undefined behaviour.
8//!
9//! ## The engine
10//!
11//! The single-threaded core is a thin **typed membrane** over
12//! [`slotmap`](https://crates.io/crates/slotmap): [`Region<T>`] wraps a
13//! `slotmap::SlotMap<DefaultKey, T>` and [`Handle<T>`] is a newtype over a
14//! `DefaultKey` plus `PhantomData<fn() -> T>`, so handles stay generic-over-`T`
15//! and typed (which raw slotmap keys are not). `slotmap`'s audited `unsafe`
16//! owns the dense generational layout — the free list, generation bump on
17//! remove, and version-saturation retirement; this crate adds the typed
18//! boundary and stays `#![forbid(unsafe_code)]`.
19//!
20//! [`Region`], [`Handle`], and [`SyncRegion`] now live in the `sefer-region`
21//! crate alongside `aligned-vmem` / `numa-shim` / `malloc-bench-rs`. They are
22//! re-exported here for backward compatibility.
23//!
24//! ## Scope (honest)
25//!
26//! This is an *application-level* store, not a drop-in global allocator. For a
27//! process-wide allocator, use `SeferAlloc` (opt-in `production` feature) or
28//! reach for `mimalloc`.
29//!
30//! See `docs/INVARIANTS.md` for the safety invariants this crate upholds and
31//! `docs/DESIGN.md` for the architecture.
32//!
33//! ## Example
34//!
35//! ```
36//! use sefer_alloc::Region;
37//!
38//! let mut region = Region::new();
39//! let a = region.insert("alpha");
40//! let b = region.insert("beta");
41//!
42//! assert_eq!(region.get(a), Some(&"alpha"));
43//!
44//! region.remove(a);
45//! assert_eq!(region.get(a), None); // stale handle → None, never UB
46//! assert_eq!(region.get(b), Some(&"beta")); // others stay valid
47//! ```
48
49// ── Workspace: four independently-publishable companion crates ────────────────
50//
51// The workspace extracted four building blocks that can also be used standalone:
52//
53// sefer-region (crates/region) — typed handle store (this re-export)
54// aligned-vmem (crates/vmem) — OS virtual-memory aperture
55// numa-shim (crates/numa) — NUMA detection + binding
56// malloc-bench-rs (crates/malloc-bench) — portable GlobalAlloc bench harness
57//
58// ── Unsafe inventory — the complete, verifiable picture ───────────────────────
59//
60// Source of truth: `grep -rln 'allow(unsafe_code)' src/ crates/`
61//
62// EXTERNAL publishable crates (each independently auditable):
63//
64// aligned-vmem (crates/vmem/src/lib.rs) — #![allow(unsafe_code)]
65// Sole purpose: SEGMENT-aligned mmap/VirtualAlloc + page decommit/recommit.
66// Entire crate = OS aperture. Small, single-responsibility. Audit in isolation.
67//
68// numa-shim (crates/numa/src/lib.rs) — #![allow(unsafe_code)]
69// Sole purpose: Linux mbind(2) via syscall(2), Windows VirtualAllocExNuma.
70// No libnuma dep. Small, single-responsibility. Audit in isolation.
71//
72// malloc-bench-rs (crates/malloc-bench/src/lib.rs) — #![allow(unsafe_code)]
73// Confined to alloc_block / free_block / drain_mailbox helpers only;
74// every unsafe call carries a // SAFETY: comment. Bench harness, not runtime.
75//
76// sefer-region (crates/region/src/lib.rs) — #![forbid(unsafe_code)]
77// Handle<T> / Region<T> / SyncRegion<T>. Zero own unsafe; slotmap's
78// audited unsafe owns the generational layout.
79//
80// INTERNAL sefer-alloc seams (compiler-enforced — a stray `unsafe` outside
81// these named modules is a hard compile error in every configuration):
82//
83// With NO features (or only `std`): `#![forbid(unsafe_code)]` — no `unsafe`
84// is possible anywhere in the crate.
85//
86// With `experimental` (3b-II `crossbeam-epoch` tier) and/or `alloc-core`
87// (Phase 8 self-hosted segment substrate) and/or `alloc-global`
88// (Phase 11 `SeferAlloc` `GlobalAlloc` face): the crate is
89// `#![deny(unsafe_code)]` (any `unsafe` outside an allowed module is a hard
90// error), and the confined modules lift this with `#![allow(unsafe_code)]`:
91//
92// Production path (`production` = alloc-global + alloc-xthread + alloc-decommit):
93// * `alloc_core::os` — thin interop wrapper around aligned-vmem; any
94// additional unsafe blocks carry `// SAFETY:` proof.
95// (under `alloc-core`)
96// * `alloc_core::node` — intrusive free-list node r/w through raw pointers;
97// the generalized `hand` discipline. (under `alloc-core`)
98// * `global::sefer_alloc` — the `unsafe impl GlobalAlloc` alloc-face seam
99// (trait obligation + pointer handoff). (under `alloc-global`)
100// * `global::tls_heap` — raw-pointer TLS binding + `AbandonGuard` seam.
101// (under `alloc-global`)
102// * `global::fallback` — primordial fallback heap seam —
103// `static mut MaybeUninit<HeapCore>` + atomic-init
104// state-machine + spinlock-guarded `&mut` handout.
105// (under `alloc-global`)
106// * `registry::heap_slot` — `Sync`/`Send` impls + `UnsafeCell` hand-off.
107// (under `alloc-global`)
108// * `registry::heap_registry` — `*mut HeapCore` pointer handoff out of a slot.
109// (under `alloc-global`)
110//
111// Optional `numa-aware` path:
112// * `alloc_core::numa` — thin interop wrapper around numa-shim; any
113// additional unsafe blocks carry `// SAFETY:` proof.
114// (under `numa-aware`)
115//
116// Research / older tiers (not in production build):
117// * `concurrent::hand` — epoch-tier AtomicSlot<T>. (under `experimental`, legacy/research-tier)
118//
119// So "the `unsafe` lives in named modules" is enforced by the compiler in
120// EVERY configuration. Verifiable: `grep -rln 'allow(unsafe_code)' src/ crates/`
121#![cfg_attr(
122 not(any(feature = "experimental", feature = "alloc-core")),
123 forbid(unsafe_code)
124)]
125#![deny(unsafe_code)]
126// The single-threaded core (`Region<T>` / `Handle<T>`) needs only `alloc` and
127// builds under `no_std`. Disable the default `std` feature to drop `SyncRegion`
128// and the concurrent/byte tiers and get the bare `no_std` + `alloc` core.
129#![cfg_attr(not(feature = "std"), no_std)]
130extern crate alloc;
131
132// Phase 1: typed handle store, extracted to `sefer-region`. Re-exported here
133// for backward compatibility — existing users of `sefer_alloc::{Region, Handle,
134// SyncRegion}` continue to work unchanged. New consumers who want ONLY the
135// handle store (no allocator stack) should depend on `sefer-region` directly.
136
137#[cfg(feature = "experimental")]
138mod concurrent;
139
140// `alloc_core` is the Phase 8+ segment substrate. Its public surface is
141// `AllocCore` / `SegmentLayout` (re-exported below). The module itself is also
142// `#[doc(hidden)] pub` so the isolated ring test (`tests/remote_ring_unit.rs`)
143// can reach `alloc_core::remote_free_ring::RemoteFreeRing`'s `#[doc(hidden)]`
144// test surface — this is the established test-only export pattern (see
145// `registry` below). Nothing in `alloc_core` is stable public API.
146#[cfg(feature = "alloc-core")]
147#[doc(hidden)]
148pub mod alloc_core;
149
150#[cfg(feature = "alloc")]
151mod heap;
152
153#[cfg(feature = "alloc-global")]
154mod global;
155
156#[cfg(feature = "alloc-global")]
157#[doc(hidden)]
158pub mod registry;
159
160pub use sefer_region::{Handle, Region};
161
162#[cfg(feature = "std")]
163pub use sefer_region::SyncRegion;
164
165#[cfg(feature = "experimental")]
166#[allow(deprecated)]
167pub use concurrent::{
168 EpochHandle, EpochRegion, LockFreeHandle, LockFreeRegion, ShardedHandle, ShardedRegion,
169};
170
171#[cfg(feature = "pinning")]
172pub use concurrent::PinnedRunner;
173
174#[cfg(all(feature = "alloc-core", feature = "alloc-decommit"))]
175pub use alloc_core::LargeCacheConfig;
176#[cfg(all(feature = "alloc-core", feature = "alloc-decommit"))]
177pub use alloc_core::LargeCacheMode;
178#[cfg(feature = "alloc-core")]
179pub use alloc_core::{AllocCore, SegmentLayout};
180
181#[cfg(feature = "alloc")]
182pub use heap::{with_heap, Heap};
183
184#[cfg(feature = "alloc-global")]
185pub use global::SeferAlloc;