1
0
forked from ROMEO/nexosim

Make execution failible, impl deadlock detection

TODO: return the list of models involved in a deadlock.

Note that Many execution errors are not implemented at all at the
moment and will need separate PRs, namely:
- Terminated
- ModelError
- Panic
This commit is contained in:
Serge Barral
2024-10-20 12:35:44 +02:00
parent e7889c8e9b
commit 1cfaa00f9e
22 changed files with 556 additions and 223 deletions

View File

@ -25,7 +25,7 @@ use std::time::Duration;
use asynchronix::model::{Model, SetupContext};
use asynchronix::ports::{EventBuffer, Output};
use asynchronix::simulation::{Mailbox, SimInit};
use asynchronix::simulation::{Mailbox, SimInit, SimulationError};
use asynchronix::time::MonotonicTime;
mod stepper_motor;
@ -84,7 +84,7 @@ impl Model for MotorAssembly {
}
}
fn main() {
fn main() -> Result<(), SimulationError> {
// ---------------
// Bench assembly.
// ---------------
@ -107,7 +107,7 @@ fn main() {
// Assembly and initialization.
let mut simu = SimInit::new()
.add_model(assembly, assembly_mbox, "assembly")
.init(t0);
.init(t0)?;
let scheduler = simu.scheduler();
@ -132,10 +132,10 @@ fn main() {
.unwrap();
// Advance simulation time to two next events.
simu.step();
simu.step()?;
t += Duration::new(2, 0);
assert_eq!(simu.time(), t);
simu.step();
simu.step()?;
t += Duration::new(0, 100_000_000);
assert_eq!(simu.time(), t);
@ -147,7 +147,7 @@ fn main() {
// Advance simulation time by 0.9s, which with a 10Hz PPS should correspond to
// 9 position increments.
simu.step_by(Duration::new(0, 900_000_000));
simu.step_by(Duration::new(0, 900_000_000))?;
t += Duration::new(0, 900_000_000);
assert_eq!(simu.time(), t);
for _ in 0..9 {
@ -155,4 +155,6 @@ fn main() {
assert_eq!(position.next(), Some(pos));
}
assert!(position.next().is_none());
Ok(())
}

View File

@ -35,7 +35,7 @@ use std::time::Duration;
use asynchronix::model::{Context, InitializedModel, Model};
use asynchronix::ports::{EventSlot, Output};
use asynchronix::simulation::{ActionKey, Mailbox, SimInit};
use asynchronix::simulation::{ActionKey, Mailbox, SimInit, SimulationError};
use asynchronix::time::MonotonicTime;
/// Water pump.
@ -332,7 +332,7 @@ pub enum WaterSenseState {
NotEmpty,
}
fn main() {
fn main() -> Result<(), SimulationError> {
// ---------------
// Bench assembly.
// ---------------
@ -375,7 +375,7 @@ fn main() {
.add_model(controller, controller_mbox, "controller")
.add_model(pump, pump_mbox, "pump")
.add_model(tank, tank_mbox, "tank")
.init(t0);
.init(t0)?;
let scheduler = simu.scheduler();
@ -388,10 +388,10 @@ fn main() {
assert_eq!(simu.time(), t);
// Brew one espresso shot with the default brew time.
simu.process_event(Controller::brew_cmd, (), &controller_addr);
simu.process_event(Controller::brew_cmd, (), &controller_addr)?;
assert_eq!(flow_rate.next(), Some(pump_flow_rate));
simu.step();
simu.step()?;
t += Controller::DEFAULT_BREW_TIME;
assert_eq!(simu.time(), t);
assert_eq!(flow_rate.next(), Some(0.0));
@ -400,33 +400,33 @@ fn main() {
let volume_per_shot = pump_flow_rate * Controller::DEFAULT_BREW_TIME.as_secs_f64();
let shots_per_tank = (init_tank_volume / volume_per_shot) as u64; // YOLO--who cares about floating-point rounding errors?
for _ in 0..(shots_per_tank - 1) {
simu.process_event(Controller::brew_cmd, (), &controller_addr);
simu.process_event(Controller::brew_cmd, (), &controller_addr)?;
assert_eq!(flow_rate.next(), Some(pump_flow_rate));
simu.step();
simu.step()?;
t += Controller::DEFAULT_BREW_TIME;
assert_eq!(simu.time(), t);
assert_eq!(flow_rate.next(), Some(0.0));
}
// Check that the tank becomes empty before the completion of the next shot.
simu.process_event(Controller::brew_cmd, (), &controller_addr);
simu.step();
simu.process_event(Controller::brew_cmd, (), &controller_addr)?;
simu.step()?;
assert!(simu.time() < t + Controller::DEFAULT_BREW_TIME);
t = simu.time();
assert_eq!(flow_rate.next(), Some(0.0));
// Try to brew another shot while the tank is still empty.
simu.process_event(Controller::brew_cmd, (), &controller_addr);
simu.process_event(Controller::brew_cmd, (), &controller_addr)?;
assert!(flow_rate.next().is_none());
// Change the brew time and fill up the tank.
let brew_time = Duration::new(30, 0);
simu.process_event(Controller::brew_time, brew_time, &controller_addr);
simu.process_event(Tank::fill, 1.0e-3, tank_addr);
simu.process_event(Controller::brew_cmd, (), &controller_addr);
simu.process_event(Controller::brew_time, brew_time, &controller_addr)?;
simu.process_event(Tank::fill, 1.0e-3, tank_addr)?;
simu.process_event(Controller::brew_cmd, (), &controller_addr)?;
assert_eq!(flow_rate.next(), Some(pump_flow_rate));
simu.step();
simu.step()?;
t += brew_time;
assert_eq!(simu.time(), t);
assert_eq!(flow_rate.next(), Some(0.0));
@ -440,11 +440,13 @@ fn main() {
&controller_addr,
)
.unwrap();
simu.process_event(Controller::brew_cmd, (), &controller_addr);
simu.process_event(Controller::brew_cmd, (), &controller_addr)?;
assert_eq!(flow_rate.next(), Some(pump_flow_rate));
simu.step();
simu.step()?;
t += Duration::from_secs(15);
assert_eq!(simu.time(), t);
assert_eq!(flow_rate.next(), Some(0.0));
Ok(())
}

View File

@ -32,7 +32,7 @@ use mio::{Events, Interest, Poll, Token};
use asynchronix::model::{Context, InitializedModel, Model, SetupContext};
use asynchronix::ports::{EventBuffer, Output};
use asynchronix::simulation::{Mailbox, SimInit};
use asynchronix::simulation::{Mailbox, SimInit, SimulationError};
use asynchronix::time::{AutoSystemClock, MonotonicTime};
const DELTA: Duration = Duration::from_millis(2);
@ -184,7 +184,7 @@ impl Drop for Listener {
}
}
fn main() {
fn main() -> Result<(), SimulationError> {
// ---------------
// Bench assembly.
// ---------------
@ -210,7 +210,7 @@ fn main() {
let mut simu = SimInit::new()
.add_model(listener, listener_mbox, "listener")
.set_clock(AutoSystemClock::new())
.init(t0);
.init(t0)?;
// ----------
// Simulation.
@ -231,7 +231,7 @@ fn main() {
});
// Advance simulation, external messages will be collected.
simu.step_by(Duration::from_secs(2));
simu.step_by(Duration::from_secs(2))?;
// Check collected external messages.
let mut packets = 0_u32;
@ -244,4 +244,6 @@ fn main() {
assert_eq!(message.next(), None);
sender_handle.join().unwrap();
Ok(())
}

View File

@ -28,7 +28,7 @@
//! ```
use asynchronix::model::Model;
use asynchronix::ports::{EventSlot, Output, Requestor};
use asynchronix::simulation::{Mailbox, SimInit};
use asynchronix::simulation::{Mailbox, SimInit, SimulationError};
use asynchronix::time::MonotonicTime;
/// Power supply.
@ -99,7 +99,7 @@ impl Load {
impl Model for Load {}
fn main() {
fn main() -> Result<(), SimulationError> {
// ---------------
// Bench assembly.
// ---------------
@ -144,7 +144,7 @@ fn main() {
.add_model(load1, load1_mbox, "load1")
.add_model(load2, load2_mbox, "load2")
.add_model(load3, load3_mbox, "load3")
.init(t0);
.init(t0)?;
// ----------
// Simulation.
@ -158,7 +158,7 @@ fn main() {
// Vary the supply voltage, check the load and power supply consumptions.
for voltage in [10.0, 15.0, 20.0] {
simu.process_event(PowerSupply::voltage_setting, voltage, &psu_addr);
simu.process_event(PowerSupply::voltage_setting, voltage, &psu_addr)?;
let v_square = voltage * voltage;
assert!(same_power(load1_power.next().unwrap(), v_square / r1));
@ -169,4 +169,6 @@ fn main() {
v_square * (1.0 / r1 + 1.0 / r2 + 1.0 / r3)
));
}
Ok(())
}

View File

@ -205,7 +205,7 @@ impl Driver {
impl Model for Driver {}
#[allow(dead_code)]
fn main() {
fn main() -> Result<(), asynchronix::simulation::SimulationError> {
// ---------------
// Bench assembly.
// ---------------
@ -235,7 +235,7 @@ fn main() {
let mut simu = SimInit::new()
.add_model(driver, driver_mbox, "driver")
.add_model(motor, motor_mbox, "motor")
.init(t0);
.init(t0)?;
let scheduler = simu.scheduler();
@ -260,10 +260,10 @@ fn main() {
.unwrap();
// Advance simulation time to two next events.
simu.step();
simu.step()?;
t += Duration::new(2, 0);
assert_eq!(simu.time(), t);
simu.step();
simu.step()?;
t += Duration::new(0, 100_000_000);
assert_eq!(simu.time(), t);
@ -275,7 +275,7 @@ fn main() {
// Advance simulation time by 0.9s, which with a 10Hz PPS should correspond to
// 9 position increments.
simu.step_by(Duration::new(0, 900_000_000));
simu.step_by(Duration::new(0, 900_000_000))?;
t += Duration::new(0, 900_000_000);
assert_eq!(simu.time(), t);
for _ in 0..9 {
@ -285,24 +285,24 @@ fn main() {
assert!(position.next().is_none());
// Increase the load beyond the torque limit for a 1A driver current.
simu.process_event(Motor::load, 2.0, &motor_addr);
simu.process_event(Motor::load, 2.0, &motor_addr)?;
// Advance simulation time and check that the motor is blocked.
simu.step();
simu.step()?;
t += Duration::new(0, 100_000_000);
assert_eq!(simu.time(), t);
assert!(position.next().is_none());
// Do it again.
simu.step();
simu.step()?;
t += Duration::new(0, 100_000_000);
assert_eq!(simu.time(), t);
assert!(position.next().is_none());
// Decrease the load below the torque limit for a 1A driver current and
// advance simulation time.
simu.process_event(Motor::load, 0.5, &motor_addr);
simu.step();
simu.process_event(Motor::load, 0.5, &motor_addr)?;
simu.step()?;
t += Duration::new(0, 100_000_000);
// The motor should start moving again, but since the phase was incremented
@ -314,7 +314,7 @@ fn main() {
// Advance simulation time by 0.7s, which with a 10Hz PPS should correspond to
// 7 position increments.
simu.step_by(Duration::new(0, 700_000_000));
simu.step_by(Duration::new(0, 700_000_000))?;
t += Duration::new(0, 700_000_000);
assert_eq!(simu.time(), t);
for _ in 0..7 {
@ -325,8 +325,8 @@ fn main() {
// Now make the motor rotate in the opposite direction. Note that this
// driver only accounts for a new PPS at the next pulse.
simu.process_event(Driver::pulse_rate, -10.0, &driver_addr);
simu.step();
simu.process_event(Driver::pulse_rate, -10.0, &driver_addr)?;
simu.step()?;
t += Duration::new(0, 100_000_000);
assert_eq!(simu.time(), t);
pos = (pos + 1) % Motor::STEPS_PER_REV;
@ -334,9 +334,11 @@ fn main() {
// Advance simulation time by 1.9s, which with a -10Hz PPS should correspond
// to 19 position decrements.
simu.step_by(Duration::new(1, 900_000_000));
simu.step_by(Duration::new(1, 900_000_000))?;
t += Duration::new(1, 900_000_000);
assert_eq!(simu.time(), t);
pos = (pos + Motor::STEPS_PER_REV - 19) % Motor::STEPS_PER_REV;
assert_eq!(position.by_ref().last(), Some(pos));
Ok(())
}