#![no_std] //! Fixed-capacity INIT child bookkeeping with retained ownership on wait errors. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Pid(pub u64); #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Outcome { Exited(i32), Fault(u64) } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Cause { Busy, Injected, Stale } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { InvalidPid, Duplicate, Full, Unknown, AlreadyObserved, Wait(Cause), } pub trait WaitAdapter { /// A failed wait must leave the child owned and retriable. fn wait_child(&mut self, pid: Pid) -> Result; } #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct Entry { pid: Pid, exited: bool } #[must_use] pub struct Reaper { entries: [Option; N], } impl Default for Reaper { fn default() -> Self { Self::new() } } impl Reaper { pub const fn new() -> Self { Self { entries: [None; N] } } pub fn owned(&self) -> usize { self.entries.iter().filter(|entry| entry.is_some()).count() } pub fn ready(&self) -> usize { self.entries.iter().flatten().filter(|entry| entry.exited).count() } pub fn contains(&self, pid: Pid) -> bool { self.entries.iter().flatten().any(|entry| entry.pid == pid) } pub fn track(&mut self, pid: Pid) -> Result<(), Error> { if pid.0 == 0 { return Err(Error::InvalidPid); } if self.contains(pid) { return Err(Error::Duplicate); } let Some(slot) = self.entries.iter_mut().find(|entry| entry.is_none()) else { return Err(Error::Full); }; *slot = Some(Entry {pid, exited: false}); Ok(()) } pub fn observe_exit(&mut self, pid: Pid) -> Result<(), Error> { let Some(entry) = self.entries.iter_mut().flatten().find(|entry| entry.pid == pid) else { return Err(Error::Unknown); }; if entry.exited { return Err(Error::AlreadyObserved); } entry.exited = true; Ok(()) } pub fn reap_next(&mut self, adapter: &mut A) -> Result, Error> { let Some(slot) = self.entries.iter_mut().find(|entry| entry.as_ref().is_some_and(|entry| entry.exited)) else { return Ok(None); }; let pid = slot.as_ref().expect("observed child").pid; let outcome = adapter.wait_child(pid).map_err(Error::Wait)?; *slot = None; Ok(Some((pid, outcome))) } }