use std::{cmp::Reverse, hint};
use crate::{
Jiffies, Pid, Process,
events::{Event, PidEvent, TimedEvent},
process::ProcessPtr,
random::Seed,
runners::{CompleteStatus, SimulationRunner, core::RunnerCore},
services::process_access::{self, setup_process_access},
};
pub(crate) struct SeqRunner {
core: RunnerCore,
procs: Vec<ProcessPtr>,
}
impl SeqRunner {
pub(crate) fn new(core: RunnerCore, procs: Vec<ProcessPtr>, seed: Seed) -> Self {
setup_process_access(seed, None);
Self { core, procs }
}
fn ensure_started(&mut self) {
if self.core.mark_started() {
for pid in 0..self.procs.len() {
self.core.event_queue.push(Reverse(TimedEvent {
invocation_time: Jiffies(0),
event: Event::Pid {
pid,
event: PidEvent::Start {
base_seed: self.core.seed,
},
},
}));
}
}
}
fn run_loop(&mut self, deadline: Jiffies, max_steps: Option<usize>) -> CompleteStatus {
self.ensure_started();
self.core.steps_made = 0;
self.core.max_steps = max_steps;
self.core.deadline = deadline;
loop {
if let Some(complete_status) = self.core.check_deadline() {
return complete_status;
}
if let Some(complete_status) = self.core.check_steps() {
return complete_status;
}
match self.core.event_queue.pop() {
None => {
hint::cold_path();
return CompleteStatus::NoMoreEvents {
steps: self.core.steps_made,
};
}
Some(event) => {
self.step(event.0);
}
}
}
}
fn step(&mut self, timed_event: TimedEvent) {
self.core.advance_time(timed_event.invocation_time);
match timed_event.event {
Event::Fault(event) => {
hint::cold_path();
self.core.handle_fault_event(event);
}
Event::Pid { pid, event } => {
if let Some(ready) =
self.core
.handle_pid_event(timed_event.invocation_time, pid, event)
{
self.execute(pid, ready);
self.core.steps_made += 1;
}
}
}
}
fn execute(&mut self, pid: Pid, event: PidEvent) {
let now = self.core.clock.now();
process_access::prepare(pid, now);
match event {
PidEvent::Start { base_seed } => {
hint::cold_path();
self.procs[pid].on_start(base_seed + pid as u64); }
PidEvent::Message {
source, message, ..
} => {
self.procs[pid].on_message(source, message);
}
PidEvent::Timer { id } => {
self.procs[pid].on_timer(id);
}
}
self.core
.resolve_events(now, pid, process_access::take_events());
}
}
impl SimulationRunner for SeqRunner {
fn run_full_budget(&mut self) -> CompleteStatus {
self.run_loop(self.core.time_budget, None)
}
fn run_steps(&mut self, k: usize) -> CompleteStatus {
self.run_loop(self.core.time_budget, Some(k))
}
fn run_sub_budget(&mut self, sub_budget: Jiffies) -> CompleteStatus {
let deadline = std::cmp::min(self.core.clock.now() + sub_budget, self.core.time_budget);
self.run_loop(deadline, None)
}
}