1
0
forked from ROMEO/nexosim

Report deadlocked models and their malbox size

This commit is contained in:
Serge Barral
2024-10-28 11:15:47 +01:00
parent 1cfaa00f9e
commit e7b64524e0
8 changed files with 304 additions and 53 deletions

View File

@ -2,7 +2,7 @@
use asynchronix::model::Model;
use asynchronix::ports::{Output, Requestor};
use asynchronix::simulation::{ExecutionError, Mailbox, SimInit};
use asynchronix::simulation::{DeadlockInfo, ExecutionError, Mailbox, SimInit};
use asynchronix::time::MonotonicTime;
#[derive(Default)]
@ -24,8 +24,11 @@ impl Model for TestModel {}
/// message.
#[test]
fn deadlock_on_mailbox_overflow() {
const MODEL_NAME: &str = "testmodel";
const MAILBOX_SIZE: usize = 5;
let mut model = TestModel::default();
let mbox = Mailbox::new();
let mbox = Mailbox::with_capacity(MAILBOX_SIZE);
let addr = mbox.address();
// Make two self-connections so that each outgoing message generates two
@ -38,17 +41,33 @@ fn deadlock_on_mailbox_overflow() {
.connect(TestModel::activate_output, addr.clone());
let t0 = MonotonicTime::EPOCH;
let mut simu = SimInit::new().add_model(model, mbox, "").init(t0).unwrap();
let mut simu = SimInit::new()
.add_model(model, mbox, MODEL_NAME)
.init(t0)
.unwrap();
assert!(matches!(
simu.process_event(TestModel::activate_output, (), addr),
Err(ExecutionError::Deadlock(_))
));
match simu.process_event(TestModel::activate_output, (), addr) {
Err(ExecutionError::Deadlock(deadlock_info)) => {
// We expect only 1 deadlocked model.
assert_eq!(deadlock_info.len(), 1);
// We expect the mailbox to be full.
assert_eq!(
deadlock_info[0],
DeadlockInfo {
model_name: MODEL_NAME.into(),
mailbox_size: MAILBOX_SIZE
}
)
}
_ => panic!("deadlock not detected"),
}
}
/// Generates a deadlock with a query loopback.
#[test]
fn deadlock_on_query_loopback() {
const MODEL_NAME: &str = "testmodel";
let mut model = TestModel::default();
let mbox = Mailbox::new();
let addr = mbox.address();
@ -58,17 +77,34 @@ fn deadlock_on_query_loopback() {
.connect(TestModel::activate_requestor, addr.clone());
let t0 = MonotonicTime::EPOCH;
let mut simu = SimInit::new().add_model(model, mbox, "").init(t0).unwrap();
let mut simu = SimInit::new()
.add_model(model, mbox, MODEL_NAME)
.init(t0)
.unwrap();
assert!(matches!(
simu.process_event(TestModel::activate_requestor, (), addr),
Err(ExecutionError::Deadlock(_))
));
match simu.process_query(TestModel::activate_requestor, (), addr) {
Err(ExecutionError::Deadlock(deadlock_info)) => {
// We expect only 1 deadlocked model.
assert_eq!(deadlock_info.len(), 1);
// We expect the mailbox to have a single query.
assert_eq!(
deadlock_info[0],
DeadlockInfo {
model_name: MODEL_NAME.into(),
mailbox_size: 1,
}
);
}
_ => panic!("deadlock not detected"),
}
}
/// Generates a deadlock with a query loopback involving several models.
#[test]
fn deadlock_on_transitive_query_loopback() {
const MODEL1_NAME: &str = "testmodel1";
const MODEL2_NAME: &str = "testmodel2";
let mut model1 = TestModel::default();
let mut model2 = TestModel::default();
let mbox1 = Mailbox::new();
@ -78,7 +114,7 @@ fn deadlock_on_transitive_query_loopback() {
model1
.requestor
.connect(TestModel::activate_requestor, addr2.clone());
.connect(TestModel::activate_requestor, addr2);
model2
.requestor
@ -86,13 +122,90 @@ fn deadlock_on_transitive_query_loopback() {
let t0 = MonotonicTime::EPOCH;
let mut simu = SimInit::new()
.add_model(model1, mbox1, "")
.add_model(model2, mbox2, "")
.add_model(model1, mbox1, MODEL1_NAME)
.add_model(model2, mbox2, MODEL2_NAME)
.init(t0)
.unwrap();
assert!(matches!(
simu.process_event(TestModel::activate_requestor, (), addr1),
Err(ExecutionError::Deadlock(_))
));
match simu.process_query(TestModel::activate_requestor, (), addr1) {
Err(ExecutionError::Deadlock(deadlock_info)) => {
// We expect only 1 deadlocked model.
assert_eq!(deadlock_info.len(), 1);
// We expect the mailbox of this model to have a single query.
assert_eq!(
deadlock_info[0],
DeadlockInfo {
model_name: MODEL1_NAME.into(),
mailbox_size: 1,
}
);
}
_ => panic!("deadlock not detected"),
}
}
/// Generates deadlocks with query loopbacks on several models at the same time.
#[test]
fn deadlock_on_multiple_query_loopback() {
const MODEL0_NAME: &str = "testmodel0";
const MODEL1_NAME: &str = "testmodel1";
const MODEL2_NAME: &str = "testmodel2";
let mut model0 = TestModel::default();
let mut model1 = TestModel::default();
let mut model2 = TestModel::default();
let mbox0 = Mailbox::new();
let mbox1 = Mailbox::new();
let mbox2 = Mailbox::new();
let addr0 = mbox0.address();
let addr1 = mbox1.address();
let addr2 = mbox2.address();
model0
.requestor
.connect(TestModel::activate_requestor, addr1.clone());
model0
.requestor
.connect(TestModel::activate_requestor, addr2.clone());
model1
.requestor
.connect(TestModel::activate_requestor, addr1);
model2
.requestor
.connect(TestModel::activate_requestor, addr2);
let t0 = MonotonicTime::EPOCH;
let mut simu = SimInit::new()
.add_model(model0, mbox0, MODEL0_NAME)
.add_model(model1, mbox1, MODEL1_NAME)
.add_model(model2, mbox2, MODEL2_NAME)
.init(t0)
.unwrap();
match simu.process_query(TestModel::activate_requestor, (), addr0) {
Err(ExecutionError::Deadlock(deadlock_info)) => {
// We expect 2 deadlocked models.
assert_eq!(deadlock_info.len(), 2);
// We expect the mailbox of each deadlocked model to have a single
// query.
assert_eq!(
deadlock_info[0],
DeadlockInfo {
model_name: MODEL1_NAME.into(),
mailbox_size: 1,
}
);
assert_eq!(
deadlock_info[1],
DeadlockInfo {
model_name: MODEL2_NAME.into(),
mailbox_size: 1,
}
);
}
_ => panic!("deadlock not detected"),
}
}