v0.1: green-thread actors, supervision, channels, benchmark
Hand-rolled context switching on mmap'd stacks with guard pages, allocator-driven RDTSC preemption, unbounded MPSC channels, supervision via per-slot Signal mailboxes, root supervisor as sentinel PID. Lib + tests + benches clean check/clippy. All 29 tests pass. Bench: smarm 3.4% over serial baseline, within 160us of tokio current-thread on prime-counting fan-out.
This commit is contained in:
123
tests/stack.rs
Normal file
123
tests/stack.rs
Normal file
@@ -0,0 +1,123 @@
|
||||
//! Stack allocator tests.
|
||||
//!
|
||||
//! Covers allocation, alignment, read/write across the usable region, and
|
||||
//! (via subprocess) that the guard page actually SIGSEGVs.
|
||||
|
||||
use smarm::stack::Stack;
|
||||
|
||||
#[test]
|
||||
fn top_is_16_byte_aligned() {
|
||||
let s = Stack::new(64 * 1024).unwrap();
|
||||
assert_eq!(s.top() as usize % 16, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_is_within_allocation() {
|
||||
let s = Stack::new(64 * 1024).unwrap();
|
||||
let top = s.top() as usize;
|
||||
let base = s.usable_base() as usize;
|
||||
assert!(top > base);
|
||||
assert!(top <= base + s.stack_size());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_and_read_top_of_stack() {
|
||||
let s = Stack::new(64 * 1024).unwrap();
|
||||
let sentinel: u64 = 0xDEAD_BEEF_CAFE_1234;
|
||||
unsafe {
|
||||
let ptr = s.top().sub(8) as *mut u64;
|
||||
ptr.write_volatile(sentinel);
|
||||
assert_eq!(ptr.read_volatile(), sentinel);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_and_read_bottom_of_usable_region() {
|
||||
let s = Stack::new(64 * 1024).unwrap();
|
||||
let sentinel: u64 = 0x0102_0304_0506_0708;
|
||||
unsafe {
|
||||
let ptr = s.usable_base() as *mut u64;
|
||||
ptr.write_volatile(sentinel);
|
||||
assert_eq!(ptr.read_volatile(), sentinel);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn small_stack_allocates() {
|
||||
assert!(Stack::new(4096).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn large_stack_allocates() {
|
||||
assert!(Stack::new(8 * 1024 * 1024).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stack_size_at_least_requested() {
|
||||
let s = Stack::new(64 * 1024).unwrap();
|
||||
assert!(s.stack_size() >= 64 * 1024);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Guard page SIGSEGV tests — subprocess-based.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
use std::env;
|
||||
use std::process::Command;
|
||||
|
||||
fn run_as_child_if_requested() {
|
||||
match env::var("SMARM_SUBTEST").as_deref() {
|
||||
Ok("guard_page_direct") => {
|
||||
let s = Stack::new(64 * 1024).unwrap();
|
||||
unsafe {
|
||||
let guard_ptr = s.usable_base().sub(1);
|
||||
guard_ptr.write_volatile(0xAB);
|
||||
}
|
||||
std::process::exit(0);
|
||||
}
|
||||
Ok("stack_overflow") => {
|
||||
let s = Stack::new(64 * 1024).unwrap();
|
||||
unsafe {
|
||||
let mut ptr = s.top().sub(1);
|
||||
let stop = s.usable_base().sub(1);
|
||||
while ptr >= stop {
|
||||
ptr.write_volatile(0xFF);
|
||||
ptr = ptr.sub(1);
|
||||
}
|
||||
}
|
||||
std::process::exit(0);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_subtest(name: &str) -> std::process::ExitStatus {
|
||||
let exe = env::current_exe().unwrap();
|
||||
Command::new(exe)
|
||||
.env("SMARM_SUBTEST", name)
|
||||
.args(["--test-threads=1", "--quiet"])
|
||||
.status()
|
||||
.expect("failed to spawn subprocess")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn guard_page_causes_sigsegv() {
|
||||
run_as_child_if_requested();
|
||||
let status = spawn_subtest("guard_page_direct");
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::process::ExitStatusExt;
|
||||
assert_eq!(status.signal(), Some(11), "expected SIGSEGV, got: {:?}", status);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stack_overflow_causes_sigsegv() {
|
||||
run_as_child_if_requested();
|
||||
let status = spawn_subtest("stack_overflow");
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::process::ExitStatusExt;
|
||||
assert_eq!(status.signal(), Some(11), "expected SIGSEGV, got: {:?}", status);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user