feat(init): supervise servers+session progs: restart on death, same endpoint, backoff 0/1/5/30s then give up, restarts_clients cascade; release grants/spawn at stop (stale-slot safe); storage death fatal; init output via printer thread (never blocks on log); terminal Ctrl+C via CONTROL-badged copy, latest wins; flaky test server + spawntest restart/backoff check
This commit is contained in:
102
crates/init/src/output.rs
Normal file
102
crates/init/src/output.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
//! What init prints never waits on the log server: init restarts the log
|
||||
//! server, so a blocking call to it could wait on itself. Text goes into a
|
||||
//! ring; a thread of init's own takes it to the log and waits there when
|
||||
//! the log is down, instead of init. Until that thread runs (early boot),
|
||||
//! init prints to the console directly.
|
||||
|
||||
use core::fmt;
|
||||
use core::sync::atomic::{AtomicU8, AtomicUsize, Ordering};
|
||||
use libkos::syscall;
|
||||
use types::bounded::CapSlot;
|
||||
|
||||
const SIZE: usize = 4096;
|
||||
/// Bytes waiting: written at `TAIL` by init's thread, read at `HEAD` by
|
||||
/// the printer.
|
||||
static RING: [AtomicU8; SIZE] = [const { AtomicU8::new(0) }; SIZE];
|
||||
static HEAD: AtomicUsize = AtomicUsize::new(0);
|
||||
static TAIL: AtomicUsize = AtomicUsize::new(0);
|
||||
/// Bytes dropped because the ring was full: the log was down a long time.
|
||||
static LOST: AtomicUsize = AtomicUsize::new(0);
|
||||
/// The printer's notification, once it runs.
|
||||
static WAKE: AtomicUsize = AtomicUsize::new(usize::MAX);
|
||||
|
||||
fn wake() -> Option<CapSlot> {
|
||||
u8::try_from(WAKE.load(Ordering::Acquire)).ok().and_then(CapSlot::new)
|
||||
}
|
||||
|
||||
pub(crate) fn write(bytes: &[u8]) {
|
||||
let Some(wake) = wake() else {
|
||||
libkos::io::write_bytes(bytes);
|
||||
return;
|
||||
};
|
||||
let tail = TAIL.load(Ordering::Relaxed);
|
||||
let room = SIZE - (tail - HEAD.load(Ordering::Acquire));
|
||||
let n = bytes.len().min(room);
|
||||
for (i, b) in bytes[..n].iter().enumerate() {
|
||||
RING[(tail + i) % SIZE].store(*b, Ordering::Relaxed);
|
||||
}
|
||||
TAIL.store(tail + n, Ordering::Release);
|
||||
LOST.fetch_add(bytes.len() - n, Ordering::Relaxed);
|
||||
let _ = syscall::signal(wake);
|
||||
}
|
||||
|
||||
struct Ring;
|
||||
|
||||
impl fmt::Write for Ring {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
write(s.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print(args: fmt::Arguments) {
|
||||
let _ = fmt::Write::write_fmt(&mut Ring, args);
|
||||
}
|
||||
|
||||
/// Starts the printer; from here on init's output goes through the ring.
|
||||
pub(crate) fn start() -> bool {
|
||||
let Ok(wake) = syscall::create_notification() else { return false };
|
||||
WAKE.store(wake.value() as usize, Ordering::Release);
|
||||
if libkos::thread::spawn(&WAKE, printer) {
|
||||
return true;
|
||||
}
|
||||
WAKE.store(usize::MAX, Ordering::Release);
|
||||
false
|
||||
}
|
||||
|
||||
fn printer(_: &'static AtomicUsize) -> ! {
|
||||
let mut chunk = [0u8; 256];
|
||||
loop {
|
||||
if let Some(wake) = wake() {
|
||||
let _ = syscall::wait(wake);
|
||||
}
|
||||
loop {
|
||||
let (head, tail) = (HEAD.load(Ordering::Relaxed), TAIL.load(Ordering::Acquire));
|
||||
if head == tail {
|
||||
break;
|
||||
}
|
||||
let n = (tail - head).min(chunk.len());
|
||||
for (i, b) in chunk[..n].iter_mut().enumerate() {
|
||||
*b = RING[(head + i) % SIZE].load(Ordering::Relaxed);
|
||||
}
|
||||
HEAD.store(head + n, Ordering::Release);
|
||||
// Blocks while the log server is down; init goes on.
|
||||
libkos::io::write_bytes(&chunk[..n]);
|
||||
}
|
||||
let lost = LOST.swap(0, Ordering::Relaxed);
|
||||
if lost > 0 {
|
||||
libkos::println!("init: {} bytes of output lost while the log was down", lost);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! out {
|
||||
($($arg:tt)*) => ($crate::output::print(format_args!($($arg)*)));
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! outln {
|
||||
() => ($crate::out!("\n"));
|
||||
($($arg:tt)*) => ($crate::out!("{}\n", format_args!($($arg)*)));
|
||||
}
|
||||
Reference in New Issue
Block a user