use std::cmp::Reverse;
use crate::{
Jiffies, ProcessHandle,
events::{Event, RankHandlerEvent, TimedEvent},
global::{
configuration::setup_local_configuration,
local_access::{self, setup_local_access},
},
network::Network,
random::Seed,
runners::{RunStatus, SimulationRunner, common::RunnerCore, task::TaskResult},
};
pub(crate) struct SimpleRunner {
core: RunnerCore,
procs: Vec<Box<dyn ProcessHandle>>,
}
impl SimpleRunner {
pub(crate) fn new(
network: Network,
time_budget: Jiffies,
procs: Vec<Box<dyn ProcessHandle>>,
seed: Seed,
) -> Self {
for id in 0..procs.len() {
setup_local_configuration(id, seed);
}
let (tx, _rx) = crossbeam_channel::unbounded::<TaskResult>();
setup_local_access(seed, tx);
Self {
core: RunnerCore::new(network, time_budget, procs.len()),
procs,
}
}
pub(crate) fn seed_events(&mut self, events: Vec<Reverse<TimedEvent>>) {
self.core.seed_events(events);
}
fn ensure_started(&mut self) {
if self.core.mark_started() {
for rank in 0..self.procs.len() {
self.core.event_queue.push(Reverse(TimedEvent {
invocation_time: Jiffies(0),
event: Event::Handler(RankHandlerEvent::Start { rank }),
}));
}
}
}
fn run_loop(&mut self, deadline: Jiffies, max_steps: Option<usize>) -> RunStatus {
self.ensure_started();
let mut steps = 0;
loop {
if let Some(k) = max_steps {
if steps >= k {
return RunStatus::Completed { steps };
}
}
if self.core.clock.now() >= self.core.time_budget {
return RunStatus::BudgetExhausted { steps };
}
if self.core.clock.now() >= deadline {
return RunStatus::Completed { steps };
}
match self.core.event_queue.pop() {
None => return RunStatus::NoMoreEvents { steps },
Some(event) => {
steps += self.step(event.0);
}
}
}
}
fn step(&mut self, timed_event: TimedEvent) -> usize {
self.core.advance_time(timed_event.invocation_time);
match timed_event.event {
Event::Fault(event) => {
self.core.handle_fault_event(event);
0 }
Event::Handler(event) => {
if let Some(ready) = self
.core
.handle_rank_handler_event(timed_event.invocation_time, event)
{
self.dispatch(ready);
1
} else {
0
}
}
}
}
fn dispatch(&mut self, event: RankHandlerEvent) {
local_access::prepare(event.target_rank(), self.core.clock.now());
match event {
RankHandlerEvent::Start { rank } => {
self.procs[rank].on_start();
}
RankHandlerEvent::Network {
source,
target,
message,
..
} => {
self.procs[target].on_message(source, message);
}
RankHandlerEvent::Timer { rank, id } => {
self.procs[rank].on_timer(id);
}
}
self.core.resolve_events(local_access::take_events());
}
}
impl SimulationRunner for SimpleRunner {
fn run_full_budget(&mut self) -> RunStatus {
let status = self.run_loop(self.core.time_budget, None);
self.core.progress_bar.finish();
status
}
fn run_steps(&mut self, k: usize) -> RunStatus {
self.run_loop(self.core.time_budget, Some(k))
}
fn run_sub_budget(&mut self, sub_budget: Jiffies) -> RunStatus {
let deadline = self.core.clock.now() + sub_budget;
self.run_loop(deadline, None)
}
}