///|
pub(all) enum ProcessStatus {
Ready
Running
Suspended
Terminated
} derive(Debug, Eq)
///|
pub impl Show for ProcessStatus with fn output(self, logger) {
match self {
Ready => logger.write_string("Ready")
Running => logger.write_string("Running")
Suspended => logger.write_string("Suspended")
Terminated => logger.write_string("Terminated")
}
}
///|
pub(all) struct Process {
id : Int
name : String
mut step_idx : Int
steps : Array[(ProcessContext) -> Double?]
mut status : ProcessStatus
}
///|
pub impl Show for Process with fn output(self, logger) {
logger.write_string(
"Process(\{self.id}, \{self.name}, status=\{self.status})",
)
}
///|
pub(all) struct ProcessContext {
manager : EventManager
clock : @clock.Clock
proc_id : Int
}
///|
pub(all) struct EventManager {
clock : @clock.Clock
priv actions : Map[Int, () -> Unit]
priv processes : Map[Int, Process]
mut next_action_id : Int
mut next_proc_id : Int
}
///|
pub fn EventManager::new(clock : @clock.Clock) -> EventManager {
{
clock,
actions: Map::from_array([]),
processes: Map::from_array([]),
next_action_id: 1,
next_proc_id: 1,
}
}
///|
pub fn EventManager::clock(self : EventManager) -> @clock.Clock {
self.clock
}
///|
pub fn EventManager::schedule_action(
self : EventManager,
delta_seconds : Double,
priority : Int,
action : () -> Unit,
) -> @clock.EventId {
let action_id = self.next_action_id
self.next_action_id = self.next_action_id + 1
self.actions.set(action_id, action)
self.clock.schedule(delta_seconds, priority, action_id)
}
///|
pub fn EventManager::register_process(
self : EventManager,
name : String,
steps : Array[(ProcessContext) -> Double?],
) -> Int {
let proc_id = self.next_proc_id
self.next_proc_id = self.next_proc_id + 1
let proc = { id: proc_id, name, step_idx: 0, steps, status: Ready }
self.processes.set(proc_id, proc)
proc_id
}
///|
pub fn EventManager::start_process(
self : EventManager,
proc_id : Int,
delay : Double,
priority : Int,
) -> @clock.EventId? {
match self.processes.get(proc_id) {
None => None
Some(proc) => {
if proc.status == Terminated {
return None
}
proc.status = Running
let payload_id = -proc_id
Some(self.clock.schedule(delay, priority, payload_id))
}
}
}
///|
pub fn EventManager::step(self : EventManager) -> Bool {
match self.clock.next_event() {
None => false
Some(ev) => {
if ev.payload_id > 0 {
match self.actions.get(ev.payload_id) {
Some(action) => {
action()
self.actions.remove(ev.payload_id)
}
None => ()
}
} else if ev.payload_id < 0 {
let proc_id = -ev.payload_id
match self.processes.get(proc_id) {
Some(proc) =>
if proc.status != Terminated && !proc.steps.is_empty() {
if proc.step_idx >= proc.steps.length() {
proc.step_idx = 0
}
let ctx = { manager: self, clock: self.clock, proc_id }
let current_step = proc.steps[proc.step_idx]
proc.step_idx = proc.step_idx + 1
match current_step(ctx) {
Some(next_delay) => {
if proc.step_idx >= proc.steps.length() {
proc.step_idx = 0
}
proc.status = Suspended
let _ = self.clock.schedule(next_delay, ev.priority, -proc_id)
}
None => proc.status = Terminated
}
} else {
proc.status = Terminated
}
None => ()
}
}
true
}
}
}
///|
pub fn EventManager::run_until(
self : EventManager,
target_seconds : Double,
) -> Unit {
self.clock.set_target_time(Some(target_seconds))
while self.step() {
}
}
///|
pub fn EventManager::run(self : EventManager) -> Unit {
self.clock.set_target_time(None)
while self.step() {
}
}
///|
pub fn EventManager::get_process_status(
self : EventManager,
proc_id : Int,
) -> ProcessStatus? {
match self.processes.get(proc_id) {
None => None
Some(p) => Some(p.status)
}
}