//! K318: scheduler wait cancellation exercised in a real AArch64 guest. //! This probes EL1 state transitions; it does not claim an EL0 cancel ABI. use super::*; use turkos_kernel::task::{ChildWait, Error, Scheduler, State, WakeCause}; fn fail(reason: &str) -> ! { write_str("TURKOS/TASK-CANCEL-ERROR: "); write_line(reason); park() } pub fn smoke_test() -> ! { let mut scheduler = Scheduler::new(); let parent = scheduler.spawn().unwrap_or_else(|_| fail("parent spawn")); if scheduler.run_next() != Ok(Some(parent)) { fail("parent start"); } let child = scheduler .spawn_child(parent) .unwrap_or_else(|_| fail("child spawn")); if scheduler.wait_child(parent, child) != Ok(ChildWait::Blocked(Some(child))) || scheduler.state(parent) != Ok(State::WaitingChild { child }) || scheduler.ready_len() != 0 { fail("exact wait did not block"); } write_str("TURKOS/TASK-CANCEL-BLOCK: PARENT=0x"); write_hex(parent.slot as usize); write_str("/0x"); write_hex(parent.generation as usize); write_str(" CHILD=0x"); write_hex(child.slot as usize); write_str("/0x"); write_hex(child.generation as usize); write_str(" READY=0x"); write_hex(scheduler.ready_len()); write_str("\r\n"); if scheduler.cancel_child_wait(parent, child) != Ok(()) || scheduler.state(parent) != Ok(State::Ready) || scheduler.ready_len() != 1 || scheduler.state(child) != Ok(State::Running) { fail("cancel failed or child died"); } write_line( "TURKOS/TASK-CANCEL-WAKE: CAUSE=WAIT-CANCELLED READY=0x0000000000000001 CHILD=RUNNING", ); if scheduler.cancel_child_wait(parent, child) != Err(Error::InvalidTransition) || scheduler.ready_len() != 1 { fail("duplicate cancel inserted parent twice"); } write_line("TURKOS/TASK-CANCEL-DUP: RESULT=INVALID-TRANSITION READY=0x0000000000000001"); if scheduler.exit_current(42) != Ok(Some(parent)) || scheduler.ready_len() != 0 || scheduler.take_wake_cause(parent) != Ok(Some(WakeCause::WaitCancelled)) || scheduler.take_wake_cause(parent) != Ok(None) || scheduler.wait_child(parent, child) != Ok(ChildWait::Exited(42)) || scheduler.wait_child(parent, child) != Err(Error::NotChild) { fail("exit re-woke or exit code was not single-use"); } write_line( "TURKOS/TASK-CANCEL-EXIT: CODE=0x000000000000002a READY=0x0000000000000000 WAKE=WAIT-CANCELLED REAPED=0x1", ); if scheduler.exit_current(0) != Ok(None) || scheduler.running().is_some() || scheduler.ready_len() != 0 { fail("idle after parent exit"); } write_line("TURKOS/TASK-CANCEL: PASS"); park() }