//! 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) -> 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, 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, 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, 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); }