feat(kernel): WatchDeath + SetTimer events as notification signals; adapter wakes at earliest deadline
This commit is contained in:
@@ -32,3 +32,4 @@ pub mod resource;
|
||||
pub mod scheduler;
|
||||
pub mod syscall;
|
||||
pub mod thread;
|
||||
pub mod timer;
|
||||
|
||||
@@ -4,6 +4,7 @@ use types::{
|
||||
bounded::{CapSlot, PageCount},
|
||||
mapping::PagePerms,
|
||||
permission::PermissionSet,
|
||||
time::Ticks,
|
||||
};
|
||||
|
||||
use crate::message::Message;
|
||||
@@ -91,6 +92,18 @@ pub enum Syscall {
|
||||
RegionAddress {
|
||||
region: CapSlot,
|
||||
},
|
||||
/// Signals `notification` when the process behind `process` dies, by
|
||||
/// exit, fault or kill. One watcher per process; a new one replaces it.
|
||||
WatchDeath {
|
||||
process: CapSlot,
|
||||
notification: CapSlot,
|
||||
},
|
||||
/// Signals `notification` once `GetTime` reaches `deadline`; replaces
|
||||
/// the caller's timer, and a zero deadline cancels it.
|
||||
SetTimer {
|
||||
notification: CapSlot,
|
||||
deadline: Ticks,
|
||||
},
|
||||
/// Removes a capability from the caller's own table.
|
||||
DropCap {
|
||||
slot: CapSlot,
|
||||
|
||||
67
crates/domain/src/timer.rs
Normal file
67
crates/domain/src/timer.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
//! Events the kernel reports by signalling a notification: a process's
|
||||
//! death, a deadline passing. One timer per process.
|
||||
|
||||
use types::{badge::Badge, generation::Generation, id::NotificationId, id::ProcessId, time::Ticks};
|
||||
|
||||
use crate::process::MAX_PROCESSES;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "timer_tests.rs"]
|
||||
mod tests;
|
||||
|
||||
/// A notification as a capability named it; once the notification is
|
||||
/// destroyed (its generation moves on) the target is dead.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Target {
|
||||
pub notification: NotificationId,
|
||||
pub generation: Generation,
|
||||
pub badge: Badge,
|
||||
}
|
||||
|
||||
pub struct Timers {
|
||||
deadlines: [Option<(Ticks, Target)>; MAX_PROCESSES],
|
||||
}
|
||||
|
||||
impl Timers {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
deadlines: [None; MAX_PROCESSES],
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces `owner`'s timer.
|
||||
pub fn set(&mut self, owner: ProcessId, deadline: Ticks, target: Target) -> bool {
|
||||
match self.deadlines.get_mut(owner.raw() as usize) {
|
||||
Some(slot) => {
|
||||
*slot = Some((deadline, target));
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self, owner: ProcessId) {
|
||||
if let Some(slot) = self.deadlines.get_mut(owner.raw() as usize) {
|
||||
*slot = None;
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes and returns one timer whose deadline is at or before `now`.
|
||||
pub fn take_expired(&mut self, now: Ticks) -> Option<Target> {
|
||||
let slot = self
|
||||
.deadlines
|
||||
.iter_mut()
|
||||
.find(|s| s.is_some_and(|(d, _)| d <= now))?;
|
||||
slot.take().map(|(_, t)| t)
|
||||
}
|
||||
|
||||
pub fn earliest(&self) -> Option<Ticks> {
|
||||
self.deadlines.iter().flatten().map(|(d, _)| *d).min()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Timers {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
37
crates/domain/src/timer_tests.rs
Normal file
37
crates/domain/src/timer_tests.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use super::{Target, Timers};
|
||||
use types::{badge::Badge, generation::Generation, id::NotificationId, id::ProcessId, time::Ticks};
|
||||
|
||||
fn target(n: u16) -> Target {
|
||||
Target {
|
||||
notification: NotificationId::from_raw(n),
|
||||
generation: Generation::initial(),
|
||||
badge: Badge::NONE,
|
||||
}
|
||||
}
|
||||
|
||||
fn pid(n: u16) -> ProcessId {
|
||||
ProcessId::from_raw(n)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_expired_timers_fire_and_each_fires_once() {
|
||||
let mut t = Timers::new();
|
||||
t.set(pid(0), Ticks::from_raw(10), target(0));
|
||||
t.set(pid(1), Ticks::from_raw(30), target(1));
|
||||
assert_eq!(t.earliest(), Some(Ticks::from_raw(10)));
|
||||
assert_eq!(t.take_expired(Ticks::from_raw(9)), None);
|
||||
assert_eq!(t.take_expired(Ticks::from_raw(10)), Some(target(0)));
|
||||
assert_eq!(t.take_expired(Ticks::from_raw(29)), None);
|
||||
assert_eq!(t.earliest(), Some(Ticks::from_raw(30)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn setting_again_replaces_and_clear_cancels() {
|
||||
let mut t = Timers::new();
|
||||
t.set(pid(2), Ticks::from_raw(10), target(0));
|
||||
t.set(pid(2), Ticks::from_raw(50), target(1));
|
||||
assert_eq!(t.take_expired(Ticks::from_raw(20)), None);
|
||||
t.clear(pid(2));
|
||||
assert_eq!(t.earliest(), None);
|
||||
assert!(!t.set(pid(500), Ticks::from_raw(1), target(0)));
|
||||
}
|
||||
Reference in New Issue
Block a user