//! MODELED_ONLY CPU/DAIF, timer and task bodies. These fixtures execute the //! source-bound frozen Table, AsyncBank and AsyncQueue APIs and real host stack //! byte owners. No assembly, physical pin or elapsed guest time is measured. #![allow(dead_code)] use std::vec::Vec; include!("queue-fixture.rs"); use turkos_os_runqueue::runqueue as rq; use turkos_os_scheduler_policy::policy as p; use turkos_os_threads::threads as th; #[derive(Default)] pub(crate) struct BodyModel { pub(crate) progress: [u64; 4], pub(crate) links: [Option; 4], pub(crate) epochs: [u64; 4], pub(crate) source: u64, pub(crate) frequency: u64, pub(crate) counter: u64, pub(crate) sequence: u64, pub(crate) consumed: u64, pub(crate) live: Option, pub(crate) fingerprint_fault: bool, pub(crate) pages_fault: bool, pub(crate) class_fault: bool, pub(crate) slice_fault: bool, pub(crate) member_fault: bool, } // Each issued subscription owns permission to inspect one already retained // private Queue membership. It cannot clone or release the Queue/Context pin. // Its destructor is inert; rejected operations return the exact subscription. pub(crate) struct Subscription { pub(crate) handle: rq::Handle, pub(crate) thread: ThreadId, pub(crate) issuer: Rc>, } pub(crate) struct Delivery { pub(crate) observation: p::TickObservation, issuer: Rc>, } pub(crate) struct PolicyDevice { queue: Rc>, pub(crate) source: Rc>, } unsafe impl p::Backend for PolicyDevice { type Member = Subscription; type Tick = Delivery; fn execution(&self) -> p::Execution { self.queue.borrow().queue().with_backend_observation(|b| { let h = b.hardware.borrow(); p::Execution { cpu: h.cpu, masked: h.daif & 128 != 0, in_irq: h.in_irq, current: h.current, } }) } fn epoch(&self) -> u64 { self.queue.borrow().queue().snapshot().epoch } fn source(&self) -> u64 { self.source.borrow().source } fn frequency(&self) -> u64 { self.source.borrow().frequency } fn counter(&self) -> u64 { self.source.borrow().counter } fn observe(&self, m: &Subscription) -> Result { if !Rc::ptr_eq(&self.source, &m.issuer) || self.source.borrow().member_fault { return Err(p::Error::Stale); } let q = self.queue.borrow(); let o = q .queue() .os_k1100_observe_member(m.handle) .map_err(|_| p::Error::Backend)?; let r = q.queue().record(m.handle).map_err(|_| p::Error::Stale)?; if o.thread.id != m.thread || r.thread != m.thread { return Err(p::Error::Stale); } let s = self.source.borrow(); let layout = o.stack.layout; Ok(p::Observation { thread: m.thread, state: o.thread.state, class: if s.class_fault { rq::Class::Low } else { r.class }, slice: if s.slice_fault { r.slice.saturating_add(1) } else { r.slice }, slices: r.slices, progress: s.progress[m.thread.slot()], wait_epoch: s.epochs[m.thread.slot()], owner: s.links[m.thread.slot()], pages: layout.bytes / 4096 + u64::from(s.pages_fault), owner_fingerprint: layout.physical ^ layout.base ^ layout.bytes ^ m.thread.generation().rotate_left(17) ^ u64::from(s.fingerprint_fault), guard_descriptor: o.stack.guard_descriptor, low_canary: o.stack.low_canary, high_canary: o.stack.high_canary, }) } fn tick(&self, t: &Delivery) -> Result { if !Rc::ptr_eq(&self.source, &t.issuer) || self.source.borrow().live != Some(t.observation) { return Err(p::Error::Tick); } Ok(t.observation) } fn consume_tick(&mut self, t: Delivery) { let mut s = self.source.borrow_mut(); assert_eq!(s.live, Some(t.observation)); s.live = None; s.consumed += 1; } } pub(crate) type Audit = p::Policy; pub(crate) struct Driver { queue: Rc>, pub(crate) handles: Vec, pub(crate) threads: Vec, pub(crate) source: Rc>, } pub(crate) fn raw() -> (PolicyDevice, Driver, Vec) { let (q, handles) = { let (q, h, _) = setup(3); (q, h) }; let threads = handles .iter() .map(|h| q.queue().record(*h).unwrap().thread) .collect::>(); let queue = Rc::new(RefCell::new(q)); let source = Rc::new(RefCell::new(BodyModel { source: 0x1100, frequency: 1000, ..BodyModel::default() })); let members = handles .iter() .zip(&threads) .map(|(h, t)| Subscription { handle: *h, thread: *t, issuer: source.clone(), }) .collect(); ( PolicyDevice { queue: queue.clone(), source: source.clone(), }, Driver { queue, handles, threads, source, }, members, ) } pub(crate) fn policy_setup() -> (Audit, Driver, Vec) { let (b, d, m) = raw(); let mut p = Audit::new(b).ok().unwrap(); let h = m.into_iter().map(|m| p.register(m).ok().unwrap()).collect(); (p, d, h) } impl Driver { pub(crate) fn owners(&self) -> (usize, usize, u64) { inventory(&self.queue.borrow()) } pub(crate) fn hardware(&self) -> Rc> { self.queue .borrow() .queue() .with_backend_observation(|b| b.hardware.clone()) } pub(crate) fn advance(&self, sleep: bool) -> rq::Handle { let result = switch( &mut self.queue.borrow_mut(), if sleep { Disposition::Sleeping } else { Disposition::Ready }, ); let q = self.queue.borrow(); let o = q.queue().os_k1100_observe_member(result).unwrap(); assert_eq!(o.thread.state, th::State::Running); self.source.borrow_mut().progress[o.thread.id.slot()] += 1; result } pub(crate) fn issue(&self, delta: u64) -> Delivery { let h = self.hardware(); let current = h.borrow().current; let epoch = self.queue.borrow().queue().snapshot().epoch; let mut s = self.source.borrow_mut(); assert!(s.live.is_none()); s.counter += delta; s.sequence += 1; let o = p::TickObservation { cpu: h.borrow().cpu, source: s.source, sequence: s.sequence, counter: s.counter, queue_epoch: epoch, current, }; s.live = Some(o); Delivery { observation: o, issuer: self.source.clone(), } } pub(crate) fn rebind_delivery(&self, t: &mut Delivery) { self.source.borrow_mut().live = Some(t.observation); } pub(crate) fn charge(&self, p: &mut Audit, delta: u64) -> p::Snapshot { p.charge(self.issue(delta)).ok().unwrap() } pub(crate) fn context(&self, f: impl FnOnce(&model::Device) -> R) -> R { self.queue .borrow() .queue() .with_backend_observation(|b| b.bank.bank().with_backend_observation(f)) } pub(crate) fn state(&self, i: usize) -> th::State { self.context(|b| { b.inner .table .borrow() .record(self.threads[i]) .unwrap() .state }) } pub(crate) fn cancel(&self, i: usize) { self.context(|b| b.inner.table.borrow_mut().cancel(self.threads[i]).unwrap()); } pub(crate) fn wake(&self, i: usize) { self.context(|b| { let w = b.waits.borrow_mut()[self.threads[i].slot()].take().unwrap(); assert_eq!( b.inner.table.borrow_mut().wake(w).ok(), Some(self.threads[i]) ); }); self.queue .borrow_mut() .quiescent() .unwrap() .requeue(self.handles[i]) .unwrap(); } pub(crate) fn link(&self, waiter: usize, owner: usize) { let mut s = self.source.borrow_mut(); s.epochs[waiter] += 1; s.links[waiter] = Some(self.threads[owner]); } pub(crate) fn clear_link(&self, i: usize) { self.source.borrow_mut().links[i] = None; } } impl PolicyDevice { pub(crate) fn same_queue(&self, d: &Driver) -> bool { Rc::ptr_eq(&self.queue, &d.queue) } } impl Driver { pub(crate) fn reject_duplicates(&self, active: rq::Handle) { let mut q = self.queue.borrow_mut(); let before = q.queue().snapshot(); let order = q.queue().ready_order(); assert!(q.quiescent().unwrap().requeue(active).is_err()); let queued = order.iter().flatten().next().copied().unwrap(); assert!(q.quiescent().unwrap().requeue(queued).is_err()); assert_eq!(q.queue().snapshot(), before); assert_eq!(q.queue().ready_order(), order); } pub(crate) fn observer_while_table_exclusive(&self) -> Result { self.context(|b| { let _exclusive = b.inner.table.borrow_mut(); self.queue .borrow() .queue() .os_k1100_observe_member(self.handles[0]) }) } } impl Driver { pub(crate) fn wake_without_enqueue(&self, i: usize) { self.context(|b| { let w = b.waits.borrow_mut()[self.threads[i].slot()].take().unwrap(); assert_eq!( b.inner.table.borrow_mut().wake(w).ok(), Some(self.threads[i]) ); }); } pub(crate) fn stack_bytes(&self) -> Vec> { self.context(|b| b.inner.stacks.iter().map(|s| s.borrow().clone()).collect()) } } impl Driver { pub(crate) fn advance_without_progress(&self) { let result = switch(&mut self.queue.borrow_mut(), Disposition::Ready); assert_eq!( self.queue .borrow() .queue() .os_k1100_observe_member(result) .unwrap() .thread .state, th::State::Running ); } pub(crate) fn hour(&self, p: &mut Audit) { for _ in 0..180 { self.advance(false); self.charge(p, 20000); } } }