feat: add Rust userspace support; fix TLS layout for p_align < 16; kill whole process on sibling-thread faults
This commit is contained in:
1 parent
a81298fb24
commit
f8b8f9610f
31 files changed
+1076
-38
No files matched your search
@@ -0,0 +1,148 @@
|
||||
//! rusthello - Phase 1 Rust smoke test for MontaukOS.
|
||||
//!
|
||||
//! Exercises alloc collections, formatting, argv, a #[thread_local]
|
||||
//! (PT_TLS + per-thread %fs blocks), threads, and the futex mutex.
|
||||
//! `rusthello panic` / `rusthello panic-thread` check that a panic on
|
||||
//! either thread takes the whole process down; `rusthello fault-thread`
|
||||
//! checks the same for a page fault on a worker thread.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(thread_local)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::format;
|
||||
use alloc::string::String;
|
||||
use alloc::sync::Arc;
|
||||
use alloc::vec::Vec;
|
||||
use core::cell::Cell;
|
||||
use core::time::Duration;
|
||||
|
||||
use montauk_rt::sync::Mutex;
|
||||
use montauk_rt::{entry, println, thread, time};
|
||||
|
||||
entry!(main);
|
||||
|
||||
static mut FAILURES: u32 = 0;
|
||||
|
||||
fn check(ok: bool, name: &str) {
|
||||
println!("{} {}", if ok { "PASS" } else { "FAIL" }, name);
|
||||
if !ok {
|
||||
unsafe { FAILURES += 1 };
|
||||
}
|
||||
}
|
||||
|
||||
#[thread_local]
|
||||
static TLS_COUNTER: Cell<u64> = Cell::new(7);
|
||||
|
||||
const THREADS: usize = 4;
|
||||
const ITERS: u64 = 20_000;
|
||||
|
||||
fn main() -> i32 {
|
||||
println!("rusthello: Rust on MontaukOS (no_std + alloc)");
|
||||
|
||||
let args: Vec<&str> = montauk_rt::env::args().collect();
|
||||
println!("argv = {:?}", args);
|
||||
match args.get(1).copied() {
|
||||
Some("panic") => panic!("requested panic on the main thread"),
|
||||
Some("fault-thread") => {
|
||||
// A CPU fault (not a panic) on a worker must still kill the
|
||||
// whole process, with the SIGSEGV exit code (267).
|
||||
let h = thread::spawn(|| {
|
||||
unsafe { core::ptr::write_volatile(8 as *mut u64, 0xdead) };
|
||||
0
|
||||
})
|
||||
.unwrap();
|
||||
let _ = h.join();
|
||||
thread::yield_now();
|
||||
println!("FAIL process survived a worker-thread fault");
|
||||
return 1;
|
||||
}
|
||||
Some("panic-thread") => {
|
||||
let h = thread::spawn(|| panic!("requested panic on a worker thread")).unwrap();
|
||||
let _ = h.join();
|
||||
println!("FAIL process survived a worker-thread panic");
|
||||
return 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// --- alloc ---
|
||||
let mut v: Vec<u64> = (1..=100).collect();
|
||||
v.retain(|x| x % 3 == 0);
|
||||
check(v.len() == 33 && v.iter().sum::<u64>() == 1683, "Vec collect/retain/sum");
|
||||
|
||||
let mut s = String::new();
|
||||
for word in ["Montauk", "speaks", "Rust"] {
|
||||
if !s.is_empty() {
|
||||
s.push(' ');
|
||||
}
|
||||
s.push_str(word);
|
||||
}
|
||||
check(s == "Montauk speaks Rust", "String building");
|
||||
check(format!("{:>6.2}|{:#x}|{:?}", 3.14159f64, 255, Some("x")) == " 3.14|0xff|Some(\"x\")",
|
||||
"format! with floats, hex, Debug");
|
||||
|
||||
let mut map = BTreeMap::new();
|
||||
for (i, w) in s.split(' ').enumerate() {
|
||||
map.insert(w, i);
|
||||
}
|
||||
check(map.get("Rust") == Some(&2) && map.len() == 3, "BTreeMap");
|
||||
|
||||
let big: Vec<u8> = alloc::vec![0xA5; 1 << 20];
|
||||
check(big.iter().all(|&b| b == 0xA5), "1 MiB allocation");
|
||||
|
||||
#[repr(align(4096))]
|
||||
struct PageAligned([u8; 64]);
|
||||
let page = alloc::boxed::Box::new(PageAligned([1; 64]));
|
||||
check((&*page as *const PageAligned as usize) % 4096 == 0 && page.0[63] == 1, "over-aligned Box (4096)");
|
||||
|
||||
// --- thread-local storage ---
|
||||
check(TLS_COUNTER.get() == 7, "#[thread_local] initial value on main thread");
|
||||
TLS_COUNTER.set(1000);
|
||||
let tls_seen = thread::spawn(|| {
|
||||
let initial = TLS_COUNTER.get();
|
||||
TLS_COUNTER.set(55);
|
||||
initial as i32
|
||||
})
|
||||
.unwrap()
|
||||
.join();
|
||||
check(tls_seen == Ok(7), "worker thread gets a fresh TLS block");
|
||||
check(TLS_COUNTER.get() == 1000, "main thread TLS untouched by worker");
|
||||
|
||||
// --- threads + futex mutex ---
|
||||
let counter = Arc::new(Mutex::new(0u64));
|
||||
let mut handles = Vec::new();
|
||||
for t in 0..THREADS {
|
||||
let counter = counter.clone();
|
||||
handles.push(
|
||||
thread::spawn(move || {
|
||||
for i in 0..ITERS {
|
||||
let mut g = counter.lock();
|
||||
let v = *g;
|
||||
if i % 512 == 0 {
|
||||
thread::yield_now(); // widen the race window under the lock
|
||||
}
|
||||
*g = v + 1;
|
||||
}
|
||||
t as i32 + 10
|
||||
})
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
let codes: Vec<_> = handles.into_iter().map(|h| h.join()).collect();
|
||||
check(codes.iter().enumerate().all(|(t, c)| *c == Ok(t as i32 + 10)), "thread exit codes via join");
|
||||
check(*counter.lock() == THREADS as u64 * ITERS, "futex Mutex keeps the counter exact");
|
||||
|
||||
// --- time ---
|
||||
let t0 = time::uptime_ms();
|
||||
time::sleep(Duration::from_millis(25));
|
||||
let dt = time::uptime_ms() - t0;
|
||||
check((25..1000).contains(&dt), "sleep(25 ms)");
|
||||
|
||||
let failures = unsafe { FAILURES };
|
||||
println!("rusthello: {}", if failures == 0 { "all passed" } else { "FAILURES" });
|
||||
if failures == 0 { 0 } else { 1 }
|
||||
}
|
||||
Reference in new issue
Block a user