use std::thread;
use std::time::Duration;
use serde::{Deserialize, Serialize};
use nexosim::model::Model;
use nexosim::ports::EventSource;
use nexosim::simulation::{ExecutionError, Mailbox, SimInit};
use nexosim::time::{AutoSystemClock, MonotonicTime};
const MT_NUM_THREADS: usize = 4;
#[derive(Default, Deserialize, Serialize)]
struct TestModel {}
#[Model]
impl TestModel {
fn block_for(&mut self, duration: Duration) {
thread::sleep(duration);
}
}
fn clock_sync(
num_threads: usize,
block_time_ms: u64,
clock_tolerance_ms: u64,
ticks_ms: &[u64],
) -> (Duration, Result<(), ExecutionError>) {
let block_time = Duration::from_millis(block_time_ms);
let clock_tolerance = Duration::from_millis(clock_tolerance_ms);
let model = TestModel::default();
let clock = AutoSystemClock::new();
let mbox = Mailbox::new();
let mut bench = SimInit::with_num_threads(num_threads);
let block_for = EventSource::new()
.connect(TestModel::block_for, &mbox)
.register(&mut bench);
let t0 = MonotonicTime::EPOCH;
bench = bench
.add_model(model, mbox, "test")
.with_tickless_clock(clock)
.with_clock_tolerance(clock_tolerance);
let mut simu = bench.init(t0).unwrap();
let scheduler = simu.scheduler();
let mut delta = Duration::ZERO;
for tick_ms in ticks_ms {
let tick = Duration::from_millis(*tick_ms);
if tick > delta {
delta = tick;
}
scheduler
.schedule_event(tick, &block_for, block_time)
.unwrap();
}
let res = simu.step_until(delta);
let last_tick = simu.time().duration_since(t0);
(last_tick, res)
}
fn clock_sync_zero_tolerance(num_threads: usize) {
const BLOCKING_MS: u64 = 100;
const CLOCK_TOLERANCE_MS: u64 = 0;
const TICKS_MS: &[u64] = &[100, 250, 400, 450, 650];
let (last_tick, res) = clock_sync(num_threads, BLOCKING_MS, CLOCK_TOLERANCE_MS, TICKS_MS);
if let Err(ExecutionError::OutOfSync(_lag)) = res {
assert_eq!(last_tick, Duration::from_millis(TICKS_MS[3]));
} else {
panic!("loss of synchronization not observed");
}
}
fn clock_sync_with_tolerance(num_threads: usize) {
const BLOCKING_MS: u64 = 200;
const CLOCK_TOLERANCE_MS: u64 = 100;
const TICKS_MS: &[u64] = &[100, 350, 500, 800, 850, 1250];
let (last_tick, res) = clock_sync(num_threads, BLOCKING_MS, CLOCK_TOLERANCE_MS, TICKS_MS);
if let Err(ExecutionError::OutOfSync(lag)) = res {
assert_eq!(last_tick, Duration::from_millis(TICKS_MS[4]));
assert!(lag > Duration::from_millis(CLOCK_TOLERANCE_MS));
} else {
panic!("loss of synchronization not observed");
}
}
#[test]
fn clock_sync_zero_tolerance_st() {
clock_sync_zero_tolerance(1);
}
#[test]
fn clock_sync_zero_tolerance_mt() {
clock_sync_zero_tolerance(MT_NUM_THREADS);
}
#[test]
fn clock_sync_with_tolerance_st() {
clock_sync_with_tolerance(1);
}
#[test]
fn clock_sync_with_tolerance_mt() {
clock_sync_with_tolerance(MT_NUM_THREADS);
}