//! Cores: each thread has a home core and never migrates; waking or removing a thread on //! another core sends that core an IPI when it must re-check its queue. use domain::{ scheduler::{Scheduler, SchedulerDecision}, thread::ThreadState, }; use platform::Platform; use types::{ bounded::Priority, id::{CoreId, ThreadId}, }; use crate::Kernel; impl Kernel

{ /// The core `tid` runs on. pub fn home(&self, tid: ThreadId) -> CoreId { self.homes .get(tid.raw() as usize) .copied() .unwrap_or(CoreId::from_raw(0)) } /// Homes a new thread on `core`. pub(crate) fn set_home(&mut self, tid: ThreadId, core: CoreId) { if let Some(h) = self.homes.get_mut(tid.raw() as usize) { *h = core; } } /// Homes a new thread on the core with the fewest live threads. pub(crate) fn place(&mut self, tid: ThreadId) { let cores = usize::from(self.scheduler.num_cores().max(1)); let mut load = [0usize; u8::MAX as usize + 1]; for (t, _) in self.threads.iter().filter(|(t, _)| *t != tid) { if let Some(n) = load.get_mut(usize::from(self.home(t).raw())) { *n += 1; } } let best = (0..cores) .min_by_key(|c| load.get(*c).copied().unwrap_or(usize::MAX)) .and_then(|c| u8::try_from(c).ok()) .map_or(CoreId::from_raw(0), CoreId::from_raw); self.set_home(tid, best); } /// Queues a ready thread on its home core, and kicks that core if it /// is another one that should look at its queue now. pub(crate) fn make_ready(&mut self, tid: ThreadId, priority: Priority) { let home = self.home(tid); self.scheduler.add(tid, priority, home); if home == self.platform.core_id() { return; } let outranks = match self.scheduler.current(home) { None => true, Some(running) => self .threads .get(running) .is_ok_and(|t| priority > t.priority()), }; if outranks { self.platform.send_ipi(home); } } /// Before `tid` is destroyed: if another core is running it, that core /// must stop and pick something else. pub(crate) fn evict(&mut self, tid: ThreadId) { if let Ok(ThreadState::Running(core)) = self.threads.get(tid).map(|t| *t.state()) && core != self.platform.core_id() { self.platform.send_ipi(core); } } /// The highest priority waiting in `core`'s run queue. pub fn highest_waiting(&self, core: CoreId) -> Option { self.scheduler.highest_waiting(core) } pub fn thread_priority(&self, tid: ThreadId) -> Option { self.threads.get(tid).ok().map(|t| t.priority()) } /// What `core` does after an IPI: keep its thread or switch to a higher-priority one; /// with no thread the caller picks. pub fn reschedule(&mut self, core: CoreId) -> SchedulerDecision { let Some(current) = self.scheduler.current(core) else { return SchedulerDecision::Continue; }; let Ok(thread) = self.threads.get(current) else { return SchedulerDecision::Continue; }; let priority = thread.priority(); if self .scheduler .highest_waiting(core) .is_none_or(|p| p <= priority) { return SchedulerDecision::Continue; } let Some(next) = self.scheduler.next(core) else { return SchedulerDecision::Continue; }; if self .threads .get_mut(next) .and_then(|t| t.set_running(core)) .is_err() { return SchedulerDecision::Continue; } if let Ok(t) = self.threads.get_mut(current) && t.preempt().is_ok() { self.scheduler.add(current, priority, core); } SchedulerDecision::Preempt(next) } }