1
0
forked from ROMEO/nexosim
nexosim/asynchronix/tests/simulation_deadlock.rs
Serge Barral 1cfaa00f9e 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
2024-10-20 12:35:44 +02:00

99 lines
2.7 KiB
Rust

//! Deadlock-detection for model loops.
use asynchronix::model::Model;
use asynchronix::ports::{Output, Requestor};
use asynchronix::simulation::{ExecutionError, Mailbox, SimInit};
use asynchronix::time::MonotonicTime;
#[derive(Default)]
struct TestModel {
output: Output<()>,
requestor: Requestor<(), ()>,
}
impl TestModel {
async fn activate_output(&mut self) {
self.output.send(()).await;
}
async fn activate_requestor(&mut self) {
let _ = self.requestor.send(()).await;
}
}
impl Model for TestModel {}
/// Overflows a mailbox by sending 2 messages in loopback for each incoming
/// message.
#[test]
fn deadlock_on_mailbox_overflow() {
let mut model = TestModel::default();
let mbox = Mailbox::new();
let addr = mbox.address();
// Make two self-connections so that each outgoing message generates two
// incoming messages.
model
.output
.connect(TestModel::activate_output, addr.clone());
model
.output
.connect(TestModel::activate_output, addr.clone());
let t0 = MonotonicTime::EPOCH;
let mut simu = SimInit::new().add_model(model, mbox, "").init(t0).unwrap();
assert!(matches!(
simu.process_event(TestModel::activate_output, (), addr),
Err(ExecutionError::Deadlock(_))
));
}
/// Generates a deadlock with a query loopback.
#[test]
fn deadlock_on_query_loopback() {
let mut model = TestModel::default();
let mbox = Mailbox::new();
let addr = mbox.address();
model
.requestor
.connect(TestModel::activate_requestor, addr.clone());
let t0 = MonotonicTime::EPOCH;
let mut simu = SimInit::new().add_model(model, mbox, "").init(t0).unwrap();
assert!(matches!(
simu.process_event(TestModel::activate_requestor, (), addr),
Err(ExecutionError::Deadlock(_))
));
}
/// Generates a deadlock with a query loopback involving several models.
#[test]
fn deadlock_on_transitive_query_loopback() {
let mut model1 = TestModel::default();
let mut model2 = TestModel::default();
let mbox1 = Mailbox::new();
let mbox2 = Mailbox::new();
let addr1 = mbox1.address();
let addr2 = mbox2.address();
model1
.requestor
.connect(TestModel::activate_requestor, addr2.clone());
model2
.requestor
.connect(TestModel::activate_requestor, addr1.clone());
let t0 = MonotonicTime::EPOCH;
let mut simu = SimInit::new()
.add_model(model1, mbox1, "")
.add_model(model2, mbox2, "")
.init(t0)
.unwrap();
assert!(matches!(
simu.process_event(TestModel::activate_requestor, (), addr1),
Err(ExecutionError::Deadlock(_))
));
}