use super::{NanoAgent, NanoBus, TickResult, NanoMetrics, SchedulerTopology, rdtsc, spin};
use std::time::{Instant, Duration};
use std::collections::VecDeque;
pub struct SchedulerConfig {
pub topology: SchedulerTopology,
pub run_duration_ns: u128,
pub tick_duration_ns: u128,
pub max_agents: usize,
pub bus_capacity: usize,
pub enable_tracing: bool,
}
impl Default for SchedulerConfig {
fn default() -> Self {
Self {
topology: SchedulerTopology::RoundRobin,
run_duration_ns: 1_000_000_000, tick_duration_ns: 1_000_000, max_agents: 1024,
bus_capacity: 65536,
enable_tracing: false,
}
}
}
struct AgentSlot {
agent: Box<dyn NanoAgent>,
budget_ns: u128,
priority: u32,
metrics: NanoMetrics,
}
pub struct NanoScheduler {
agents: Vec<AgentSlot>,
bus: NanoBus,
config: SchedulerConfig,
start_time: Instant,
traces: VecDeque<AgentTrace>,
}
#[derive(Debug, Clone)]
pub struct AgentTrace {
pub agent_name: &'static str,
pub timestamp_ns: u128,
pub result: TickResult,
}
impl NanoScheduler {
pub fn new(config: SchedulerConfig) -> Self {
let bus = NanoBus::new(config.bus_capacity);
Self {
agents: Vec::with_capacity(config.max_agents),
bus,
config,
start_time: Instant::now(),
traces: VecDeque::with_capacity(10000),
}
}
pub fn register<A: NanoAgent + 'static>(&mut self, agent: A) {
let budget_ns = agent.budget_ns();
let slot = AgentSlot {
agent: Box::new(agent),
budget_ns,
priority: 0,
metrics: NanoMetrics::new(),
};
self.agents.push(slot);
}
pub fn register_with_priority<A: NanoAgent + 'static>(
&mut self,
agent: A,
priority: u32,
) {
let budget_ns = agent.budget_ns();
let slot = AgentSlot {
agent: Box::new(agent),
budget_ns,
priority,
metrics: NanoMetrics::new(),
};
self.agents.push(slot);
}
pub fn agent_count(&self) -> usize {
self.agents.len()
}
pub fn add_agent(&mut self, agent: Box<dyn NanoAgent>) {
let budget_ns = agent.budget_ns();
let slot = AgentSlot {
agent,
budget_ns,
priority: 0,
metrics: NanoMetrics::new(),
};
self.agents.push(slot);
}
pub fn run(mut self) -> SchedulerStats {
let start = Instant::now();
let mut now_ns = || start.elapsed().as_nanos();
let run_budget = self.config.run_duration_ns;
if matches!(self.config.topology, SchedulerTopology::Priority) {
self.agents.sort_by_key(|a| std::cmp::Reverse(a.priority));
}
let mut total_ticks = 0u64;
let mut total_cycles = 0u64;
let mut budget_violations = 0u64;
while now_ns() < run_budget {
let macro_tick_start = now_ns();
match self.config.topology {
SchedulerTopology::RoundRobin => {
self.execute_round_robin(|| now_ns(), &mut total_ticks, &mut total_cycles, &mut budget_violations);
}
SchedulerTopology::Priority => {
self.execute_priority(|| now_ns(), &mut total_ticks, &mut total_cycles, &mut budget_violations);
}
SchedulerTopology::Hierarchical => {
self.execute_hierarchical(|| now_ns(), &mut total_ticks, &mut total_cycles, &mut budget_violations);
}
SchedulerTopology::Mesh => {
self.execute_mesh(|| now_ns(), &mut total_ticks, &mut total_cycles, &mut budget_violations);
}
SchedulerTopology::Quantum => {
self.execute_quantum(|| now_ns(), &mut total_ticks, &mut total_cycles, &mut budget_violations);
}
}
while now_ns() - macro_tick_start < self.config.tick_duration_ns {
spin();
}
}
SchedulerStats {
total_ticks,
total_cycles,
budget_violations,
runtime_ns: start.elapsed().as_nanos(),
agent_count: self.agents.len(),
traces: if self.config.enable_tracing {
self.traces.into()
} else {
Vec::new()
},
}
}
fn execute_round_robin(
&mut self,
mut now_ns: impl FnMut() -> u128,
total_ticks: &mut u64,
total_cycles: &mut u64,
budget_violations: &mut u64,
) {
for slot in &mut self.agents {
let tick_start_ns = now_ns();
let tick_start_tsc = rdtsc();
let bus = self.bus.clone_bus();
let result = slot.agent.tick(tick_start_ns, &bus);
let tick_cycles = rdtsc() - tick_start_tsc;
let tick_duration_ns = now_ns() - tick_start_ns;
if tick_duration_ns > slot.budget_ns {
*budget_violations += 1;
slot.metrics.budget_violations.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
}
slot.metrics.record_tick(&result);
*total_ticks += 1;
*total_cycles += tick_cycles;
if self.config.enable_tracing {
self.traces.push_back(AgentTrace {
agent_name: slot.agent.name(),
timestamp_ns: tick_start_ns,
result,
});
if self.traces.len() > 100000 {
self.traces.pop_front();
}
}
while now_ns() - tick_start_ns < slot.budget_ns {
spin();
}
}
}
fn execute_priority(
&mut self,
mut now_ns: impl FnMut() -> u128,
total_ticks: &mut u64,
total_cycles: &mut u64,
budget_violations: &mut u64,
) {
self.execute_round_robin(now_ns, total_ticks, total_cycles, budget_violations);
}
fn execute_hierarchical(
&mut self,
mut now_ns: impl FnMut() -> u128,
total_ticks: &mut u64,
total_cycles: &mut u64,
budget_violations: &mut u64,
) {
self.execute_round_robin(now_ns, total_ticks, total_cycles, budget_violations);
}
fn execute_mesh(
&mut self,
mut now_ns: impl FnMut() -> u128,
total_ticks: &mut u64,
total_cycles: &mut u64,
budget_violations: &mut u64,
) {
self.execute_round_robin(now_ns, total_ticks, total_cycles, budget_violations);
}
fn execute_quantum(
&mut self,
mut now_ns: impl FnMut() -> u128,
total_ticks: &mut u64,
total_cycles: &mut u64,
budget_violations: &mut u64,
) {
self.execute_round_robin(now_ns, total_ticks, total_cycles, budget_violations);
}
}
#[derive(Debug)]
pub struct SchedulerStats {
pub total_ticks: u64,
pub total_cycles: u64,
pub budget_violations: u64,
pub runtime_ns: u128,
pub agent_count: usize,
pub traces: Vec<AgentTrace>,
}
impl SchedulerStats {
pub fn avg_ns_per_tick(&self) -> f64 {
if self.total_ticks == 0 {
0.0
} else {
self.runtime_ns as f64 / self.total_ticks as f64
}
}
pub fn avg_cycles_per_tick(&self) -> f64 {
if self.total_ticks == 0 {
0.0
} else {
self.total_cycles as f64 / self.total_ticks as f64
}
}
pub fn violation_rate(&self) -> f64 {
if self.total_ticks == 0 {
0.0
} else {
self.budget_violations as f64 / self.total_ticks as f64
}
}
}