//! Fixed-capacity waitset registration. See os-waitset/CONTRACT.md. //! A kernel adapter must validate the live source handle and bind `P` to a //! live process generation before calling register. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct RegistrationId(u64); impl RegistrationId { pub const fn from_raw(raw: u64) -> Self { Self(raw) } pub const fn raw(self) -> u64 { self.0 } pub const fn generation(self) -> u32 { (self.0 >> 32) as u32 } pub const fn slot(self) -> u32 { self.0 as u32 } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Kind { File, Channel, Task } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct ObjectKey { pub kind: Kind, pub id: u64 } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Interest(u8); impl Interest { pub const READ: Self = Self(1); pub const WRITE: Self = Self(2); pub const CLOSED: Self = Self(4); pub const READ_CLOSED: Self = Self(5); pub const fn from_bits(bits: u8) -> Self { Self(bits) } pub const fn bits(self) -> u8 { self.0 } pub const fn valid(self) -> bool { self.0 != 0 && self.0 & !7 == 0 } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { BadFd, Access, Invalid, Full, Exists, Overflow } impl Error { pub const fn errno(self) -> u64 { match self { Self::BadFd => 9, Self::Access => 13, Self::Invalid => 22, Self::Full => 24, Self::Exists => 17, Self::Overflow => 75, } } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct Registration { id: RegistrationId, object: ObjectKey, interest: Interest } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Snapshot { pub generations: [u32; N], pub entries: [Option<(u64, ObjectKey, Interest)>; N], } pub struct Waitset { owner: P, entries: [Option; N], generations: [u32; N], } impl Waitset { pub const fn new(owner: P) -> Self { Self {owner, entries: [None; N], generations: [0; N]} } /// Kernel-only deterministic seed for isolated generation overflow tests. pub const fn with_generation_seed(owner: P, generations: [u32; N]) -> Self { Self {owner, entries: [None; N], generations} } pub fn register(&mut self, caller: P, object: ObjectKey, interest: Interest) -> Result { if caller != self.owner { return Err(Error::Access); } if object.id == 0 || !interest.valid() { return Err(Error::Invalid); } if self.entries.iter().flatten().any(|entry| entry.object == object) { return Err(Error::Exists); } let slot = self.entries.iter().position(Option::is_none).ok_or(Error::Full)?; let slot_id = u32::try_from(slot).ok() .and_then(|value| value.checked_add(1)).ok_or(Error::Overflow)?; let generation = self.generations[slot] .checked_add(1).ok_or(Error::Overflow)?; let id = RegistrationId((u64::from(generation) << 32) | u64::from(slot_id)); self.entries[slot] = Some(Registration {id, object, interest}); self.generations[slot] = generation; Ok(id) } pub fn query(&self, caller: P, id: RegistrationId) -> Result<(ObjectKey, Interest), Error> { if caller != self.owner { return Err(Error::Access); } let slot = id.slot().checked_sub(1) .and_then(|value| usize::try_from(value).ok()).ok_or(Error::BadFd)?; let entry = self.entries.get(slot).copied().flatten() .filter(|entry| entry.id == id).ok_or(Error::BadFd)?; Ok((entry.object, entry.interest)) } pub fn unregister(&mut self, caller: P, id: RegistrationId) -> Result<(), Error> { self.query(caller, id)?; let slot = usize::try_from(id.slot() - 1).map_err(|_| Error::BadFd)?; self.entries[slot] = None; Ok(()) } pub fn len(&self) -> usize { self.entries.iter().flatten().count() } pub fn snapshot(&self) -> Snapshot { Snapshot {generations: self.generations, entries: self.entries.map(|item| item.map(|item| (item.id.raw(), item.object, item.interest)))} } }