feat(domain): add MultiCoreScheduler with placement tracking, priority preemption, and load balancing

This commit is contained in:
2026-10-17 23:31:22 +02:00
parent 14b18f7406
commit edb924c27a
5 changed files with 253 additions and 3 deletions

View File

@@ -0,0 +1,106 @@
use types::{
bounded::Priority,
id::{CoreId, ThreadId},
};
use super::{per_core::PerCoreScheduler, Scheduler, SchedulerDecision};
const MAX_CORES: usize = 8;
const MAX_SCHEDULED_THREADS: usize = 256;
const BALANCE_THRESHOLD: usize = 2;
struct ThreadPlacement {
core: CoreId,
priority: Priority,
}
pub struct MultiCoreScheduler {
cores: [PerCoreScheduler; MAX_CORES],
num_cores: u8,
placements: [Option<ThreadPlacement>; MAX_SCHEDULED_THREADS],
}
impl MultiCoreScheduler {
pub fn new(num_cores: u8) -> Self {
let cores = core::array::from_fn(|i| PerCoreScheduler::new(CoreId::from_raw(i as u8)));
Self {
cores,
num_cores,
placements: [const { None }; MAX_SCHEDULED_THREADS],
}
}
fn thread_count_on(&self, core: CoreId) -> usize {
self.placements
.iter()
.filter(|p| p.as_ref().is_some_and(|tp| tp.core == core))
.count()
}
fn least_loaded_core(&self) -> CoreId {
(0..self.num_cores)
.map(|i| CoreId::from_raw(i))
.min_by_key(|&c| self.thread_count_on(c))
.unwrap()
}
fn most_loaded_core(&self) -> CoreId {
(0..self.num_cores)
.map(|i| CoreId::from_raw(i))
.max_by_key(|&c| self.thread_count_on(c))
.unwrap()
}
}
impl Scheduler for MultiCoreScheduler {
fn add(&mut self, thread: ThreadId, priority: Priority, core: CoreId) {
let idx = thread.raw() as usize;
self.placements[idx] = Some(ThreadPlacement { core, priority });
self.cores[core.raw() as usize].add(thread, priority);
}
fn remove(&mut self, thread: ThreadId) {
let idx = thread.raw() as usize;
if let Some(placement) = self.placements[idx].take() {
self.cores[placement.core.raw() as usize].remove(thread, placement.priority);
}
}
fn next(&mut self, core: CoreId) -> Option<ThreadId> {
self.cores[core.raw() as usize].pick_next()
}
fn tick(&mut self, core: CoreId, current: ThreadId) -> SchedulerDecision {
let idx = current.raw() as usize;
if let Some(placement) = &self.placements[idx] {
let current_priority = placement.priority;
let core_idx = core.raw() as usize;
if let Some(highest) = self.cores[core_idx].highest_waiting_priority() {
if highest > current_priority {
let next = self.cores[core_idx].pick_next().unwrap();
self.cores[core_idx].add(current, current_priority);
return SchedulerDecision::Preempt(next);
}
}
}
SchedulerDecision::Continue
}
fn balance(&mut self) {
let busiest = self.most_loaded_core();
let lightest = self.least_loaded_core();
if self.thread_count_on(busiest) > self.thread_count_on(lightest) + BALANCE_THRESHOLD {
let busiest_idx = busiest.raw() as usize;
if let Some((thread, priority)) = self.cores[busiest_idx].steal_lowest() {
let lightest_idx = lightest.raw() as usize;
self.cores[lightest_idx].add(thread, priority);
let tid = thread.raw() as usize;
self.placements[tid] = Some(ThreadPlacement {
core: lightest,
priority,
});
}
}
}
}