forked from ROMEO/nexosim

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
99 lines
2.7 KiB
Rust
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(_))
|
|
));
|
|
}
|