feat(stack,runtime): per-shape actor stacks — Stack::new(reserve, guard), Config knobs, pool rule (RFC 019 §1)
Stack takes an explicit (reserve, guard) shape, both page-rounded and
stored; usable_base derives from the stored guard. Guard default raised
4 KiB -> 64 KiB (DEFAULT_STACK_GUARD): probestack makes one page enough
for Rust frames, but an unprobed C frame can leap a page in one sub rsp
— the motivating SQLite segfault. Reserve default stays 64 KiB
(DEFAULT_STACK_RESERVE); ACTOR_STACK_SIZE retired.
Config::{stack_reserve, stack_guard} thread the runtime defaults into
RuntimeInner pre-rounded. All acquisition/recycling now goes through
acquire_stack/recycle_stack carrying the pool rule: only default-shaped
stacks are pooled (pooled ⇒ default-shaped by induction); custom shapes
mmap fresh and munmap at death. Pool lock still dropped before any mmap.
No public spawn API change (SpawnOpts is the next commit).
Tests: shape rounding + accessors, wide-guard faults at both ends
(subprocess), Config::stack_reserve permits >64 KiB recursion that
previously could only segfault.
This commit is contained in:
@@ -517,3 +517,35 @@ fn runtime_reusable_after_root_panic() {
|
||||
r.run(move || ran_t.store(true, Ordering::Relaxed));
|
||||
assert!(ran.load(Ordering::Relaxed), "runtime unusable after root panic");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// RFC 019 — Config stack knobs
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Burn ~`frames` × 4 KiB of stack; probestack touches pages in order so
|
||||
/// exceeding the reserve would hit the guard and SIGSEGV the process.
|
||||
#[inline(never)]
|
||||
fn burn_stack(frames: usize) -> u64 {
|
||||
let mut local = [0u8; 4096];
|
||||
local[0] = frames as u8;
|
||||
let below = if frames == 0 { 0 } else { burn_stack(frames - 1) };
|
||||
std::hint::black_box(&mut local);
|
||||
below.wrapping_add(local[0] as u64)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_stack_reserve_permits_deep_recursion() {
|
||||
// ~256 KiB of frames: four times the old fixed 64 KiB reserve. With
|
||||
// Config::stack_reserve raised this must complete; before RFC 019 it
|
||||
// could only segfault.
|
||||
let rt = smarm::runtime::init(Config::exact(1).stack_reserve(1024 * 1024));
|
||||
let done = Arc::new(AtomicBool::new(false));
|
||||
let done2 = done.clone();
|
||||
rt.run(move || {
|
||||
spawn(move || {
|
||||
std::hint::black_box(burn_stack(64));
|
||||
done2.store(true, Ordering::SeqCst);
|
||||
}).join();
|
||||
});
|
||||
assert!(done.load(Ordering::SeqCst));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user