fail_closed/fail_closed.rs
1//! Sample binary demonstrating fail-closed-on-cap behaviour
2//! (REQ_0301). Registers a `BoundedAllocator<MAX_BLOCKS, 4096>` as
3//! `#[global_allocator]`. The arena is sized so that:
4//!
5//! * Rust runtime + stdlib startup fits inside the first few
6//! blocks (Rust's stdlib allocates a few hundred bytes before
7//! `main` runs).
8//! * The demo loop then `Box::new`s steadily-growing allocations
9//! until the cap is hit; the over-cap allocation returns null,
10//! Rust's default `alloc_error_handler` aborts the process.
11//!
12//! Run with:
13//!
14//! ```sh
15//! cargo run -p taktora-bounded-alloc --example fail_closed
16//! echo $? # expect 134 (SIGABRT) or similar non-zero
17//! ```
18//!
19//! Expected output: a handful of "iter N: allocated; …" lines
20//! showing live block count climbing toward `MAX_BLOCKS`, then
21//! `memory allocation of N bytes failed` followed by abort.
22
23#![allow(missing_docs)]
24#![allow(clippy::doc_markdown)]
25
26use taktora_bounded_alloc::declare_global_allocator;
27
28const MAX_BLOCKS: usize = 16;
29const BLOCK_SIZE: usize = 4096;
30
31declare_global_allocator!(ALLOC, MAX_BLOCKS, BLOCK_SIZE);
32
33fn main() {
34 println!("taktora-bounded-alloc fail_closed demo");
35 println!("arena: {MAX_BLOCKS} blocks × {BLOCK_SIZE} bytes");
36 println!(
37 "blocks already in use after Rust runtime startup: {}",
38 ALLOC.live_blocks()
39 );
40 println!();
41
42 let mut held: Vec<Box<[u8; 1024]>> = Vec::new();
43 for i in 0_u32..64 {
44 let b: Box<[u8; 1024]> = Box::new([(i & 0xff) as u8; 1024]);
45 held.push(b);
46 println!(
47 "iter {i}: alloc_count={}, live={}, peak={}, held={}",
48 ALLOC.alloc_count(),
49 ALLOC.live_blocks(),
50 ALLOC.peak_blocks_used(),
51 held.len()
52 );
53 }
54
55 // Unreachable: an allocation past the cap returns null, and
56 // Rust's default alloc_error_handler aborts the process.
57 println!("unexpectedly reached end of main (cap was not exhausted)");
58}