192 lines
5.1 KiB
Rust
192 lines
5.1 KiB
Rust
//! Asynchronous signals: a word of bits, a FIFO of waiters and at most one
|
|
//! bound thread. See docs/ipc.md.
|
|
|
|
use types::{
|
|
badge::Badge,
|
|
generation::Generation,
|
|
id::{NotificationId, ProcessId, ThreadId},
|
|
};
|
|
|
|
use crate::{endpoint::wait_queue::WaitQueue, generations::SlotGenerations};
|
|
|
|
#[cfg(test)]
|
|
#[path = "tests.rs"]
|
|
mod tests;
|
|
|
|
#[cfg(kani)]
|
|
#[path = "proofs.rs"]
|
|
mod proofs;
|
|
|
|
pub const MAX_NOTIFICATIONS: usize = if cfg!(kani) { 1 } else { 128 };
|
|
|
|
/// What the kernel must do after a signal.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum SignalEffect {
|
|
/// Wake this blocked `Wait`er with these bits.
|
|
WakeWaiter(ThreadId, usize),
|
|
/// Wake the bound thread out of its `Receive` with these bits.
|
|
WakeBound(ThreadId, usize),
|
|
/// Nobody to wake; the bits were stored.
|
|
Stored,
|
|
}
|
|
|
|
pub struct Notification {
|
|
id: NotificationId,
|
|
owner: ProcessId,
|
|
bits: usize,
|
|
waiters: WaitQueue,
|
|
bound: Option<ThreadId>,
|
|
}
|
|
|
|
impl Notification {
|
|
pub fn new(id: NotificationId, owner: ProcessId) -> Self {
|
|
Self {
|
|
id,
|
|
owner,
|
|
bits: 0,
|
|
waiters: WaitQueue::new(),
|
|
bound: None,
|
|
}
|
|
}
|
|
|
|
pub fn id(&self) -> NotificationId {
|
|
self.id
|
|
}
|
|
|
|
pub fn owner(&self) -> ProcessId {
|
|
self.owner
|
|
}
|
|
|
|
pub fn bound(&self) -> Option<ThreadId> {
|
|
self.bound
|
|
}
|
|
|
|
/// Delivers a signal. `bound_is_receiving` says whether the bound
|
|
/// thread is currently blocked in `Receive` and can be woken.
|
|
pub fn signal(&mut self, badge: Badge, bound_is_receiving: bool) -> SignalEffect {
|
|
let bits = if badge.is_none() { 1 } else { badge.raw() };
|
|
if let Some(waiter) = self.waiters.pop() {
|
|
return SignalEffect::WakeWaiter(waiter, bits);
|
|
}
|
|
if let (Some(bound), true) = (self.bound, bound_is_receiving) {
|
|
return SignalEffect::WakeBound(bound, bits);
|
|
}
|
|
self.bits |= bits;
|
|
SignalEffect::Stored
|
|
}
|
|
|
|
/// Returns and clears the pending bits.
|
|
pub fn take_bits(&mut self) -> usize {
|
|
core::mem::take(&mut self.bits)
|
|
}
|
|
|
|
pub fn has_bits(&self) -> bool {
|
|
self.bits != 0
|
|
}
|
|
|
|
pub fn waiters(&self) -> impl Iterator<Item = ThreadId> + '_ {
|
|
self.waiters.iter()
|
|
}
|
|
|
|
pub fn is_waiter(&self, thread: ThreadId) -> bool {
|
|
self.waiters.contains(thread)
|
|
}
|
|
|
|
pub fn add_waiter(&mut self, thread: ThreadId) -> bool {
|
|
self.waiters.push(thread).is_some()
|
|
}
|
|
|
|
pub fn bind(&mut self, thread: ThreadId) -> bool {
|
|
if self.bound.is_some() {
|
|
return false;
|
|
}
|
|
self.bound = Some(thread);
|
|
true
|
|
}
|
|
|
|
pub fn unbind(&mut self, thread: ThreadId) {
|
|
if self.bound == Some(thread) {
|
|
self.bound = None;
|
|
}
|
|
}
|
|
|
|
/// Forgets `thread` as waiter and as bound thread.
|
|
pub fn remove_thread(&mut self, thread: ThreadId) {
|
|
self.waiters.remove_thread(thread);
|
|
self.unbind(thread);
|
|
}
|
|
|
|
/// Takes `thread` out of the waiters, leaving any binding.
|
|
pub fn remove_waiter(&mut self, thread: ThreadId) {
|
|
self.waiters.remove_thread(thread);
|
|
}
|
|
|
|
/// Drains every waiter, e.g. when the notification is destroyed.
|
|
pub fn pop_waiter(&mut self) -> Option<ThreadId> {
|
|
self.waiters.pop()
|
|
}
|
|
}
|
|
|
|
pub struct NotificationTable {
|
|
slots: [Option<Notification>; MAX_NOTIFICATIONS],
|
|
generations: SlotGenerations<MAX_NOTIFICATIONS>,
|
|
}
|
|
|
|
impl NotificationTable {
|
|
pub const fn new() -> Self {
|
|
Self {
|
|
slots: [const { None }; MAX_NOTIFICATIONS],
|
|
generations: SlotGenerations::new(),
|
|
}
|
|
}
|
|
|
|
pub fn create(&mut self, owner: ProcessId) -> Option<NotificationId> {
|
|
let generations = &self.generations;
|
|
let (idx, slot) = self
|
|
.slots
|
|
.iter_mut()
|
|
.enumerate()
|
|
.find(|(i, s)| s.is_none() && generations.is_usable(*i))?;
|
|
let id = NotificationId::from_raw(idx as u16);
|
|
*slot = Some(Notification::new(id, owner));
|
|
Some(id)
|
|
}
|
|
|
|
pub fn get(&self, id: NotificationId) -> Option<&Notification> {
|
|
self.slots.get(id.raw() as usize)?.as_ref()
|
|
}
|
|
|
|
pub fn get_mut(&mut self, id: NotificationId) -> Option<&mut Notification> {
|
|
self.slots.get_mut(id.raw() as usize)?.as_mut()
|
|
}
|
|
|
|
pub fn generation(&self, id: NotificationId) -> Option<Generation> {
|
|
self.get(id)?;
|
|
self.generations.current(id.raw() as usize)
|
|
}
|
|
|
|
pub fn destroy(&mut self, id: NotificationId) -> Option<Notification> {
|
|
let n = self.slots.get_mut(id.raw() as usize)?.take()?;
|
|
self.generations.advance(id.raw() as usize);
|
|
Some(n)
|
|
}
|
|
|
|
pub fn iter(&self) -> impl Iterator<Item = &Notification> {
|
|
self.slots.iter().flatten()
|
|
}
|
|
|
|
pub fn ids_owned_by(&self, owner: ProcessId) -> impl Iterator<Item = NotificationId> + '_ {
|
|
self.slots
|
|
.iter()
|
|
.flatten()
|
|
.filter(move |n| n.owner == owner)
|
|
.map(|n| n.id)
|
|
}
|
|
}
|
|
|
|
impl Default for NotificationTable {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|