Files
k-os/crates/kernel/src/tests/events.rs

182 lines
6.4 KiB
Rust

//! Death watches and timers: the kernel's two events, both delivered as
//! notification signals.
use super::helpers::CHILD_LIMITS;
use super::*;
use crate::{SyscallOutcome, SyscallResult};
use domain::fault::Fault;
use domain::resource::Resource;
use domain::syscall::Syscall;
use types::address::DataRef;
use types::bounded::{CapSlot, Priority};
use types::time::{Ticks, TimeSlice};
fn child(k: &mut Kernel<MockPlatform>) -> CapSlot {
match k.handle_syscall(Syscall::CreateProcess {
create_cap: CapSlot::new(0).unwrap(),
image: DataRef::empty(),
limits: CHILD_LIMITS,
}) {
SyscallOutcome::Complete(SyscallResult::Slot(s)) => s,
_ => panic!("create process"),
}
}
/// Init with a child it watches, and the notification it watches with.
fn watching() -> (Kernel<MockPlatform>, ProcessId, CapSlot, CapSlot) {
let (mut k, init) = kernel_with_process();
let proc_cap = child(&mut k);
let ntfn = k.create_notification_cap(init).unwrap();
k.watch_death(init, proc_cap, ntfn).unwrap();
(k, init, proc_cap, ntfn)
}
#[test]
fn a_watcher_hears_a_kill() {
let (mut k, init, proc_cap, ntfn) = watching();
assert_eq!(k.poll(init, ntfn).unwrap(), 0);
k.handle_syscall(Syscall::Kill { process: proc_cap });
assert_eq!(k.poll(init, ntfn).unwrap(), 1);
}
#[test]
fn a_watcher_hears_an_exit_or_a_fault() {
let (mut k, init, proc_cap, ntfn) = watching();
let pid = k.process_of_cap_for_test(proc_cap);
k.destroy_process(pid);
assert_eq!(k.poll(init, ntfn).unwrap(), 1);
let proc_cap = child(&mut k);
k.watch_death(init, proc_cap, ntfn).unwrap();
let pid = k.process_of_cap_for_test(proc_cap);
let tid = k.spawn_thread_for_test(pid, Priority::DEFAULT, TimeSlice::DEFAULT);
k.handle_fault(tid, Fault::IllegalInstruction, 0);
assert_eq!(k.poll(init, ntfn).unwrap(), 1);
}
#[test]
fn watching_needs_a_process_cap_and_a_notification_to_signal() {
let (mut k, init) = kernel_with_process();
let proc_cap = child(&mut k);
let ntfn = k.create_notification_cap(init).unwrap();
assert!(k.watch_death(init, ntfn, ntfn).is_err());
assert!(k.watch_death(init, proc_cap, proc_cap).is_err());
k.handle_syscall(Syscall::Kill { process: proc_cap });
assert_eq!(k.poll(init, ntfn).unwrap(), 0);
}
#[test]
fn a_dead_notification_is_never_signalled() {
let (mut k, init) = kernel_with_process();
let watched = child(&mut k);
let watcher = child(&mut k);
let watcher_pid = k.process_of_cap_for_test(watcher);
// The watcher's own notification, which dies with it.
let ntfn = k.create_notification_cap(watcher_pid).unwrap();
let theirs = watched_in(&mut k, watched, watcher_pid);
k.watch_death(watcher_pid, theirs, ntfn).unwrap();
k.destroy_process(watcher_pid);
// A new notification may now reuse the id; the old watch must not hit it.
let fresh = k.create_notification_cap(init).unwrap();
k.handle_syscall(Syscall::Kill { process: watched });
assert_eq!(k.poll(init, fresh).unwrap(), 0);
}
/// A copy of init's `slot` in `to`'s table.
fn watched_in(k: &mut Kernel<MockPlatform>, slot: CapSlot, to: ProcessId) -> CapSlot {
let pid = k.process_of_cap_for_test(slot);
k.grant_cap_for_test(
to,
Resource::Process(pid),
types::permission::PermissionSet::empty().with(types::permission::Permission::Execute),
)
}
#[test]
fn a_timer_fires_at_its_deadline_and_not_before() {
let (mut k, init) = kernel_with_process();
let ntfn = k.create_notification_cap(init).unwrap();
k.platform_mut().set_ticks(Ticks::from_raw(10));
k.set_timer(init, ntfn, Ticks::from_raw(100)).unwrap();
assert_eq!(k.platform_mut().wake_at, Some(Ticks::from_raw(100)));
k.platform_mut().set_ticks(Ticks::from_raw(99));
k.fire_timers();
assert_eq!(k.poll(init, ntfn).unwrap(), 0);
k.platform_mut().set_ticks(Ticks::from_raw(100));
k.fire_timers();
assert_eq!(k.poll(init, ntfn).unwrap(), 1);
// Once only.
k.fire_timers();
assert_eq!(k.poll(init, ntfn).unwrap(), 0);
}
#[test]
fn a_past_deadline_fires_at_once_and_zero_cancels() {
let (mut k, init) = kernel_with_process();
let ntfn = k.create_notification_cap(init).unwrap();
k.platform_mut().set_ticks(Ticks::from_raw(50));
k.set_timer(init, ntfn, Ticks::from_raw(20)).unwrap();
assert_eq!(k.poll(init, ntfn).unwrap(), 1);
k.set_timer(init, ntfn, Ticks::from_raw(60)).unwrap();
k.set_timer(init, ntfn, Ticks::from_raw(0)).unwrap();
k.platform_mut().set_ticks(Ticks::from_raw(70));
k.fire_timers();
assert_eq!(k.poll(init, ntfn).unwrap(), 0);
}
#[test]
fn a_timer_dies_with_its_process() {
let (mut k, init) = kernel_with_process();
let ntfn = k.create_notification_cap(init).unwrap();
let proc_cap = child(&mut k);
let pid = k.process_of_cap_for_test(proc_cap);
let theirs = k.grant_cap_for_test(
pid,
Resource::Notification(notification_id(&k, init, ntfn)),
types::permission::PermissionSet::empty().with(types::permission::Permission::Write),
);
k.platform_mut().set_ticks(Ticks::from_raw(0));
k.set_timer(pid, theirs, Ticks::from_raw(10)).unwrap();
k.destroy_process(pid);
k.platform_mut().set_ticks(Ticks::from_raw(20));
k.fire_timers();
assert_eq!(k.poll(init, ntfn).unwrap(), 0);
}
fn notification_id(
k: &Kernel<MockPlatform>,
pid: ProcessId,
slot: CapSlot,
) -> types::id::NotificationId {
match k
.processes
.get(pid)
.unwrap()
.capabilities()
.get(slot)
.unwrap()
.resource()
{
Resource::Notification(id) => *id,
_ => panic!("not a notification"),
}
}
#[test]
fn every_watcher_hears_and_there_is_room_for_a_few() {
let (mut k, init, proc_cap, first) = watching();
let second = k.create_notification_cap(init).unwrap();
k.watch_death(init, proc_cap, second).unwrap();
// The same notification again takes no new place.
k.watch_death(init, proc_cap, second).unwrap();
for _ in 2..domain::timer::MAX_WATCHERS {
let n = k.create_notification_cap(init).unwrap();
k.watch_death(init, proc_cap, n).unwrap();
}
let extra = k.create_notification_cap(init).unwrap();
assert!(k.watch_death(init, proc_cap, extra).is_err());
k.handle_syscall(Syscall::Kill { process: proc_cap });
assert_eq!(k.poll(init, first).unwrap(), 1);
assert_eq!(k.poll(init, second).unwrap(), 1);
}