1
0
forked from ROMEO/nexosim

Report panics as errors + identify panicking model

The build context is now passed as a mutable reference due to the need
to mutate data when adding a model.

Contains small unrelated cleanups and documentation improvements too.
This commit is contained in:
Serge Barral
2024-11-13 19:21:54 +01:00
parent e6f77ea8e5
commit ba1e668447
21 changed files with 437 additions and 229 deletions

View File

@ -5,6 +5,7 @@ mod model_scheduling;
#[cfg(not(miri))]
mod simulation_clock_sync;
mod simulation_deadlock;
mod simulation_panic;
mod simulation_scheduling;
#[cfg(not(miri))]
mod simulation_timeout;

View File

@ -55,7 +55,7 @@ fn deadlock_on_mailbox_overflow(num_threads: usize) {
assert_eq!(
deadlock_info[0],
DeadlockInfo {
model_name: MODEL_NAME.into(),
model: MODEL_NAME.into(),
mailbox_size: MAILBOX_SIZE
}
)
@ -90,7 +90,7 @@ fn deadlock_on_query_loopback(num_threads: usize) {
assert_eq!(
deadlock_info[0],
DeadlockInfo {
model_name: MODEL_NAME.into(),
model: MODEL_NAME.into(),
mailbox_size: 1,
}
);
@ -134,7 +134,7 @@ fn deadlock_on_transitive_query_loopback(num_threads: usize) {
assert_eq!(
deadlock_info[0],
DeadlockInfo {
model_name: MODEL1_NAME.into(),
model: MODEL1_NAME.into(),
mailbox_size: 1,
}
);
@ -192,14 +192,14 @@ fn deadlock_on_multiple_query_loopback(num_threads: usize) {
assert_eq!(
deadlock_info[0],
DeadlockInfo {
model_name: MODEL1_NAME.into(),
model: MODEL1_NAME.into(),
mailbox_size: 1,
}
);
assert_eq!(
deadlock_info[1],
DeadlockInfo {
model_name: MODEL2_NAME.into(),
model: MODEL2_NAME.into(),
mailbox_size: 1,
}
);

View File

@ -0,0 +1,73 @@
//! Model panic reporting.
use asynchronix::model::Model;
use asynchronix::ports::Output;
use asynchronix::simulation::{ExecutionError, Mailbox, SimInit};
use asynchronix::time::MonotonicTime;
const MT_NUM_THREADS: usize = 4;
#[derive(Default)]
struct TestModel {
countdown_out: Output<usize>,
}
impl TestModel {
async fn countdown_in(&mut self, count: usize) {
if count == 0 {
panic!("test message");
}
self.countdown_out.send(count - 1).await;
}
}
impl Model for TestModel {}
/// Pass a counter around several models and decrement it each time, panicking
/// when it becomes zero.
fn model_panic(num_threads: usize) {
const MODEL_COUNT: usize = 5;
const INIT_COUNTDOWN: usize = 9;
// Connect all models in a cycle graph.
let mut model0 = TestModel::default();
let mbox0 = Mailbox::new();
let addr0 = mbox0.address();
let mut siminit = SimInit::with_num_threads(num_threads);
let mut addr = mbox0.address();
for model_id in (1..MODEL_COUNT).rev() {
let mut model = TestModel::default();
let mbox = Mailbox::new();
model.countdown_out.connect(TestModel::countdown_in, addr);
addr = mbox.address();
siminit = siminit.add_model(model, mbox, model_id.to_string());
}
model0.countdown_out.connect(TestModel::countdown_in, addr);
siminit = siminit.add_model(model0, mbox0, 0.to_string());
// Run the simulation.
let t0 = MonotonicTime::EPOCH;
let mut simu = siminit.init(t0).unwrap();
match simu.process_event(TestModel::countdown_in, INIT_COUNTDOWN, addr0) {
Err(ExecutionError::Panic { model, payload }) => {
let msg = payload.downcast_ref::<&str>().unwrap();
let panicking_model_id = INIT_COUNTDOWN % MODEL_COUNT;
assert_eq!(model, panicking_model_id.to_string());
assert_eq!(*msg, "test message");
}
_ => panic!("panic not detected"),
}
}
#[test]
fn model_panic_st() {
model_panic(1);
}
#[test]
fn model_panic_mt() {
model_panic(MT_NUM_THREADS);
}