From abbfb64628d81cee79935a26c4f4c802185c56c1 Mon Sep 17 00:00:00 2001 From: Serge Barral Date: Fri, 8 Nov 2024 11:08:47 +0100 Subject: [PATCH] Run integration tests on both ST and MT executors --- .github/workflows/ci.yml | 4 +- .github/workflows/loom.yml | 3 +- asynchronix/Cargo.toml | 7 +- asynchronix/src/util/cached_rw_lock.rs | 2 +- asynchronix/tests/model_scheduling.rs | 92 +++++++++++++--- asynchronix/tests/simulation_deadlock.rs | 62 +++++++++-- asynchronix/tests/simulation_scheduling.rs | 122 +++++++++++++++++---- asynchronix/tests/tests.rs | 6 - 8 files changed, 232 insertions(+), 66 deletions(-) delete mode 100644 asynchronix/tests/tests.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 194809c..2cfed61 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: CI on: pull_request: push: - branches: [main, dev] + branches: [ main ] env: RUSTFLAGS: -Dwarnings @@ -69,7 +69,7 @@ jobs: uses: dtolnay/rust-toolchain@stable - name: Dry-run cargo test (Loom) - run: cargo test --no-run --tests --all-features + run: cargo test --no-run --lib --all-features env: RUSTFLAGS: --cfg asynchronix_loom diff --git a/.github/workflows/loom.yml b/.github/workflows/loom.yml index a86b5a9..3092047 100644 --- a/.github/workflows/loom.yml +++ b/.github/workflows/loom.yml @@ -1,6 +1,7 @@ name: Loom on: + workflow_dispatch: pull_request: push: branches: [ main ] @@ -30,6 +31,6 @@ jobs: uses: dtolnay/rust-toolchain@stable - name: Run cargo test (Loom) - run: cargo test --tests --release + run: cargo test --lib --release env: RUSTFLAGS: --cfg asynchronix_loom diff --git a/asynchronix/Cargo.toml b/asynchronix/Cargo.toml index 7fc0d90..df3a772 100644 --- a/asynchronix/Cargo.toml +++ b/asynchronix/Cargo.toml @@ -21,7 +21,6 @@ A high performance asychronous compute framework for system simulation. """ categories = ["simulation", "aerospace", "science"] keywords = ["simulation", "discrete-event", "systems", "cyberphysical", "real-time"] -autotests = false [features] # gRPC service. @@ -65,7 +64,7 @@ futures-executor = "0.3" tracing-subscriber = { version= "0.3.18", features=["env-filter"] } [target.'cfg(asynchronix_loom)'.dev-dependencies] -loom = "0.5" +loom = "0.7" waker-fn = "1.1" [target.'cfg(asynchronix_grpc_codegen)'.build-dependencies] @@ -79,7 +78,3 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(asynchronix_loom)', 'cfg(a [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] - -[[test]] -name = "integration" -path = "tests/tests.rs" diff --git a/asynchronix/src/util/cached_rw_lock.rs b/asynchronix/src/util/cached_rw_lock.rs index 0866050..1ba6079 100644 --- a/asynchronix/src/util/cached_rw_lock.rs +++ b/asynchronix/src/util/cached_rw_lock.rs @@ -128,7 +128,7 @@ mod tests { #[test] fn loom_cached_rw_lock_write() { - const DEFAULT_PREEMPTION_BOUND: usize = 4; + const DEFAULT_PREEMPTION_BOUND: usize = 3; const ITERATIONS_NUMBER: usize = 5; let mut builder = Builder::new(); diff --git a/asynchronix/tests/model_scheduling.rs b/asynchronix/tests/model_scheduling.rs index 76bf12c..6d841f2 100644 --- a/asynchronix/tests/model_scheduling.rs +++ b/asynchronix/tests/model_scheduling.rs @@ -7,8 +7,9 @@ use asynchronix::ports::{EventBuffer, Output}; use asynchronix::simulation::{ActionKey, Mailbox, SimInit}; use asynchronix::time::MonotonicTime; -#[test] -fn model_schedule_event() { +const MT_NUM_THREADS: usize = 4; + +fn model_schedule_event(num_threads: usize) { #[derive(Default)] struct TestModel { output: Output<()>, @@ -38,7 +39,10 @@ fn model_schedule_event() { let addr = mbox.address(); let t0 = MonotonicTime::EPOCH; - let mut simu = SimInit::new().add_model(model, mbox, "").init(t0).unwrap(); + let mut simu = SimInit::with_num_threads(num_threads) + .add_model(model, mbox, "") + .init(t0) + .unwrap(); simu.process_event(TestModel::trigger, (), addr).unwrap(); simu.step().unwrap(); @@ -48,8 +52,7 @@ fn model_schedule_event() { assert!(output.next().is_none()); } -#[test] -fn model_cancel_future_keyed_event() { +fn model_cancel_future_keyed_event(num_threads: usize) { #[derive(Default)] struct TestModel { output: Output, @@ -93,7 +96,10 @@ fn model_cancel_future_keyed_event() { let addr = mbox.address(); let t0 = MonotonicTime::EPOCH; - let mut simu = SimInit::new().add_model(model, mbox, "").init(t0).unwrap(); + let mut simu = SimInit::with_num_threads(num_threads) + .add_model(model, mbox, "") + .init(t0) + .unwrap(); simu.process_event(TestModel::trigger, (), addr).unwrap(); simu.step().unwrap(); @@ -104,8 +110,7 @@ fn model_cancel_future_keyed_event() { assert!(output.next().is_none()); } -#[test] -fn model_cancel_same_time_keyed_event() { +fn model_cancel_same_time_keyed_event(num_threads: usize) { #[derive(Default)] struct TestModel { output: Output, @@ -149,7 +154,10 @@ fn model_cancel_same_time_keyed_event() { let addr = mbox.address(); let t0 = MonotonicTime::EPOCH; - let mut simu = SimInit::new().add_model(model, mbox, "").init(t0).unwrap(); + let mut simu = SimInit::with_num_threads(num_threads) + .add_model(model, mbox, "") + .init(t0) + .unwrap(); simu.process_event(TestModel::trigger, (), addr).unwrap(); simu.step().unwrap(); @@ -160,8 +168,7 @@ fn model_cancel_same_time_keyed_event() { assert!(output.next().is_none()); } -#[test] -fn model_schedule_periodic_event() { +fn model_schedule_periodic_event(num_threads: usize) { #[derive(Default)] struct TestModel { output: Output, @@ -192,7 +199,10 @@ fn model_schedule_periodic_event() { let addr = mbox.address(); let t0 = MonotonicTime::EPOCH; - let mut simu = SimInit::new().add_model(model, mbox, "").init(t0).unwrap(); + let mut simu = SimInit::with_num_threads(num_threads) + .add_model(model, mbox, "") + .init(t0) + .unwrap(); simu.process_event(TestModel::trigger, (), addr).unwrap(); @@ -208,8 +218,7 @@ fn model_schedule_periodic_event() { } } -#[test] -fn model_cancel_periodic_event() { +fn model_cancel_periodic_event(num_threads: usize) { #[derive(Default)] struct TestModel { output: Output<()>, @@ -243,7 +252,10 @@ fn model_cancel_periodic_event() { let addr = mbox.address(); let t0 = MonotonicTime::EPOCH; - let mut simu = SimInit::new().add_model(model, mbox, "").init(t0).unwrap(); + let mut simu = SimInit::with_num_threads(num_threads) + .add_model(model, mbox, "") + .init(t0) + .unwrap(); simu.process_event(TestModel::trigger, (), addr).unwrap(); @@ -256,3 +268,53 @@ fn model_cancel_periodic_event() { assert_eq!(simu.time(), t0 + Duration::from_secs(2)); assert!(output.next().is_none()); } + +#[test] +fn model_schedule_event_st() { + model_schedule_event(1); +} + +#[test] +fn model_schedule_event_mt() { + model_schedule_event(MT_NUM_THREADS); +} + +#[test] +fn model_cancel_future_keyed_event_st() { + model_cancel_future_keyed_event(1); +} + +#[test] +fn model_cancel_future_keyed_event_mt() { + model_cancel_future_keyed_event(MT_NUM_THREADS); +} + +#[test] +fn model_cancel_same_time_keyed_event_st() { + model_cancel_same_time_keyed_event(1); +} + +#[test] +fn model_cancel_same_time_keyed_event_mt() { + model_cancel_same_time_keyed_event(MT_NUM_THREADS); +} + +#[test] +fn model_schedule_periodic_event_st() { + model_schedule_periodic_event(1); +} + +#[test] +fn model_schedule_periodic_event_mt() { + model_schedule_periodic_event(MT_NUM_THREADS); +} + +#[test] +fn model_cancel_periodic_event_st() { + model_cancel_periodic_event(1); +} + +#[test] +fn model_cancel_periodic_event_mt() { + model_cancel_periodic_event(MT_NUM_THREADS); +} diff --git a/asynchronix/tests/simulation_deadlock.rs b/asynchronix/tests/simulation_deadlock.rs index 609b1d1..2507baa 100644 --- a/asynchronix/tests/simulation_deadlock.rs +++ b/asynchronix/tests/simulation_deadlock.rs @@ -5,6 +5,8 @@ use asynchronix::ports::{Output, Requestor}; use asynchronix::simulation::{DeadlockInfo, ExecutionError, Mailbox, SimInit}; use asynchronix::time::MonotonicTime; +const MT_NUM_THREADS: usize = 4; + #[derive(Default)] struct TestModel { output: Output<()>, @@ -22,8 +24,7 @@ impl Model for TestModel {} /// Overflows a mailbox by sending 2 messages in loopback for each incoming /// message. -#[test] -fn deadlock_on_mailbox_overflow() { +fn deadlock_on_mailbox_overflow(num_threads: usize) { const MODEL_NAME: &str = "testmodel"; const MAILBOX_SIZE: usize = 5; @@ -41,7 +42,7 @@ fn deadlock_on_mailbox_overflow() { .connect(TestModel::activate_output, addr.clone()); let t0 = MonotonicTime::EPOCH; - let mut simu = SimInit::new() + let mut simu = SimInit::with_num_threads(num_threads) .add_model(model, mbox, MODEL_NAME) .init(t0) .unwrap(); @@ -64,8 +65,7 @@ fn deadlock_on_mailbox_overflow() { } /// Generates a deadlock with a query loopback. -#[test] -fn deadlock_on_query_loopback() { +fn deadlock_on_query_loopback(num_threads: usize) { const MODEL_NAME: &str = "testmodel"; let mut model = TestModel::default(); @@ -77,7 +77,7 @@ fn deadlock_on_query_loopback() { .connect(TestModel::activate_requestor, addr.clone()); let t0 = MonotonicTime::EPOCH; - let mut simu = SimInit::new() + let mut simu = SimInit::with_num_threads(num_threads) .add_model(model, mbox, MODEL_NAME) .init(t0) .unwrap(); @@ -100,8 +100,7 @@ fn deadlock_on_query_loopback() { } /// Generates a deadlock with a query loopback involving several models. -#[test] -fn deadlock_on_transitive_query_loopback() { +fn deadlock_on_transitive_query_loopback(num_threads: usize) { const MODEL1_NAME: &str = "testmodel1"; const MODEL2_NAME: &str = "testmodel2"; @@ -121,7 +120,7 @@ fn deadlock_on_transitive_query_loopback() { .connect(TestModel::activate_requestor, addr1.clone()); let t0 = MonotonicTime::EPOCH; - let mut simu = SimInit::new() + let mut simu = SimInit::with_num_threads(num_threads) .add_model(model1, mbox1, MODEL1_NAME) .add_model(model2, mbox2, MODEL2_NAME) .init(t0) @@ -145,8 +144,7 @@ fn deadlock_on_transitive_query_loopback() { } /// Generates deadlocks with query loopbacks on several models at the same time. -#[test] -fn deadlock_on_multiple_query_loopback() { +fn deadlock_on_multiple_query_loopback(num_threads: usize) { const MODEL0_NAME: &str = "testmodel0"; const MODEL1_NAME: &str = "testmodel1"; const MODEL2_NAME: &str = "testmodel2"; @@ -178,7 +176,7 @@ fn deadlock_on_multiple_query_loopback() { .connect(TestModel::activate_requestor, addr2); let t0 = MonotonicTime::EPOCH; - let mut simu = SimInit::new() + let mut simu = SimInit::with_num_threads(num_threads) .add_model(model0, mbox0, MODEL0_NAME) .add_model(model1, mbox1, MODEL1_NAME) .add_model(model2, mbox2, MODEL2_NAME) @@ -209,3 +207,43 @@ fn deadlock_on_multiple_query_loopback() { _ => panic!("deadlock not detected"), } } + +#[test] +fn deadlock_on_mailbox_overflow_st() { + deadlock_on_mailbox_overflow(1); +} + +#[test] +fn deadlock_on_mailbox_overflow_mt() { + deadlock_on_mailbox_overflow(MT_NUM_THREADS); +} + +#[test] +fn deadlock_on_query_loopback_st() { + deadlock_on_query_loopback(1); +} + +#[test] +fn deadlock_on_query_loopback_mt() { + deadlock_on_query_loopback(MT_NUM_THREADS); +} + +#[test] +fn deadlock_on_transitive_query_loopback_st() { + deadlock_on_transitive_query_loopback(1); +} + +#[test] +fn deadlock_on_transitive_query_loopback_mt() { + deadlock_on_transitive_query_loopback(MT_NUM_THREADS); +} + +#[test] +fn deadlock_on_multiple_query_loopback_st() { + deadlock_on_multiple_query_loopback(1); +} + +#[test] +fn deadlock_on_multiple_query_loopback_mt() { + deadlock_on_multiple_query_loopback(MT_NUM_THREADS); +} diff --git a/asynchronix/tests/simulation_scheduling.rs b/asynchronix/tests/simulation_scheduling.rs index cda492d..ab249fe 100644 --- a/asynchronix/tests/simulation_scheduling.rs +++ b/asynchronix/tests/simulation_scheduling.rs @@ -2,6 +2,8 @@ use std::time::Duration; +const MT_NUM_THREADS: usize = 4; + #[cfg(not(miri))] use asynchronix::model::Context; use asynchronix::model::Model; @@ -28,6 +30,7 @@ impl Model for PassThroughModel {} /// A simple bench containing a single pass-through model (input forwarded to /// output) running as fast as possible. fn passthrough_bench( + num_threads: usize, t0: MonotonicTime, ) -> (Simulation, Address>, EventBuffer) { // Bench assembly. @@ -38,15 +41,17 @@ fn passthrough_bench( model.output.connect_sink(&out_stream); let addr = mbox.address(); - let simu = SimInit::new().add_model(model, mbox, "").init(t0).unwrap(); + let simu = SimInit::with_num_threads(num_threads) + .add_model(model, mbox, "") + .init(t0) + .unwrap(); (simu, addr, out_stream) } -#[test] -fn simulation_schedule_events() { +fn simulation_schedule_events(num_threads: usize) { let t0 = MonotonicTime::EPOCH; - let (mut simu, addr, mut output) = passthrough_bench(t0); + let (mut simu, addr, mut output) = passthrough_bench(num_threads, t0); let scheduler = simu.scheduler(); @@ -85,10 +90,9 @@ fn simulation_schedule_events() { assert!(output.next().is_none()); } -#[test] -fn simulation_schedule_keyed_events() { +fn simulation_schedule_keyed_events(num_threads: usize) { let t0 = MonotonicTime::EPOCH; - let (mut simu, addr, mut output) = passthrough_bench(t0); + let (mut simu, addr, mut output) = passthrough_bench(num_threads, t0); let scheduler = simu.scheduler(); @@ -127,10 +131,9 @@ fn simulation_schedule_keyed_events() { assert!(output.next().is_none()); } -#[test] -fn simulation_schedule_periodic_events() { +fn simulation_schedule_periodic_events(num_threads: usize) { let t0 = MonotonicTime::EPOCH; - let (mut simu, addr, mut output) = passthrough_bench(t0); + let (mut simu, addr, mut output) = passthrough_bench(num_threads, t0); let scheduler = simu.scheduler(); @@ -167,10 +170,9 @@ fn simulation_schedule_periodic_events() { } } -#[test] -fn simulation_schedule_periodic_keyed_events() { +fn simulation_schedule_periodic_keyed_events(num_threads: usize) { let t0 = MonotonicTime::EPOCH; - let (mut simu, addr, mut output) = passthrough_bench(t0); + let (mut simu, addr, mut output) = passthrough_bench(num_threads, t0); let scheduler = simu.scheduler(); @@ -216,6 +218,46 @@ fn simulation_schedule_periodic_keyed_events() { } } +#[test] +fn simulation_schedule_events_st() { + simulation_schedule_events(1); +} + +#[test] +fn simulation_schedule_events_mt() { + simulation_schedule_events(MT_NUM_THREADS); +} + +#[test] +fn simulation_schedule_keyed_events_st() { + simulation_schedule_keyed_events(1); +} + +#[test] +fn simulation_schedule_keyed_events_mt() { + simulation_schedule_keyed_events(MT_NUM_THREADS); +} + +#[test] +fn simulation_schedule_periodic_events_st() { + simulation_schedule_periodic_events(1); +} + +#[test] +fn simulation_schedule_periodic_events_mt() { + simulation_schedule_periodic_events(MT_NUM_THREADS); +} + +#[test] +fn simulation_schedule_periodic_keyed_events_st() { + simulation_schedule_periodic_keyed_events(1); +} + +#[test] +fn simulation_schedule_periodic_keyed_events_mt() { + simulation_schedule_periodic_keyed_events(MT_NUM_THREADS); +} + #[cfg(not(miri))] use std::time::{Instant, SystemTime}; @@ -245,6 +287,7 @@ impl Model for TimestampModel { /// A simple bench containing a single timestamping model with a custom clock. #[cfg(not(miri))] fn timestamp_bench( + num_threads: usize, t0: MonotonicTime, clock: impl Clock + 'static, ) -> ( @@ -260,7 +303,7 @@ fn timestamp_bench( model.stamp.connect_sink(&stamp_stream); let addr = mbox.address(); - let simu = SimInit::new() + let simu = SimInit::with_num_threads(num_threads) .add_model(model, mbox, "") .set_clock(clock) .init(t0) @@ -270,8 +313,7 @@ fn timestamp_bench( } #[cfg(not(miri))] -#[test] -fn simulation_system_clock_from_instant() { +fn simulation_system_clock_from_instant(num_threads: usize) { let t0 = MonotonicTime::EPOCH; const TOLERANCE: f64 = 0.005; // [s] @@ -293,7 +335,7 @@ fn simulation_system_clock_from_instant() { let clock = SystemClock::from_instant(simulation_ref, wall_clock_ref); - let (mut simu, addr, mut stamp) = timestamp_bench(t0, clock); + let (mut simu, addr, mut stamp) = timestamp_bench(num_threads, t0, clock); let scheduler = simu.scheduler(); @@ -327,8 +369,7 @@ fn simulation_system_clock_from_instant() { } #[cfg(not(miri))] -#[test] -fn simulation_system_clock_from_system_time() { +fn simulation_system_clock_from_system_time(num_threads: usize) { let t0 = MonotonicTime::EPOCH; const TOLERANCE: f64 = 0.005; // [s] @@ -350,7 +391,7 @@ fn simulation_system_clock_from_system_time() { let clock = SystemClock::from_system_time(simulation_ref, wall_clock_ref); - let (mut simu, addr, mut stamp) = timestamp_bench(t0, clock); + let (mut simu, addr, mut stamp) = timestamp_bench(num_threads, t0, clock); let scheduler = simu.scheduler(); @@ -390,12 +431,11 @@ fn simulation_system_clock_from_system_time() { } #[cfg(not(miri))] -#[test] -fn simulation_auto_system_clock() { +fn simulation_auto_system_clock(num_threads: usize) { let t0 = MonotonicTime::EPOCH; const TOLERANCE: f64 = 0.005; // [s] - let (mut simu, addr, mut stamp) = timestamp_bench(t0, AutoSystemClock::new()); + let (mut simu, addr, mut stamp) = timestamp_bench(num_threads, t0, AutoSystemClock::new()); let instant_t0 = Instant::now(); let scheduler = simu.scheduler(); @@ -435,3 +475,39 @@ fn simulation_auto_system_clock() { simu.step().unwrap(); } } + +#[cfg(not(miri))] +#[test] +fn simulation_system_clock_from_instant_st() { + simulation_system_clock_from_instant(1); +} + +#[cfg(not(miri))] +#[test] +fn simulation_system_clock_from_instant_mt() { + simulation_system_clock_from_instant(MT_NUM_THREADS); +} + +#[cfg(not(miri))] +#[test] +fn simulation_system_clock_from_system_time_st() { + simulation_system_clock_from_system_time(1); +} + +#[cfg(not(miri))] +#[test] +fn simulation_system_clock_from_system_time_mt() { + simulation_system_clock_from_system_time(MT_NUM_THREADS); +} + +#[cfg(not(miri))] +#[test] +fn simulation_auto_system_clock_st() { + simulation_auto_system_clock(1); +} + +#[cfg(not(miri))] +#[test] +fn simulation_auto_system_clock_mt() { + simulation_auto_system_clock(MT_NUM_THREADS); +} diff --git a/asynchronix/tests/tests.rs b/asynchronix/tests/tests.rs deleted file mode 100644 index 03f88ef..0000000 --- a/asynchronix/tests/tests.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[cfg(not(asynchronix_loom))] -mod model_scheduling; -#[cfg(not(asynchronix_loom))] -mod simulation_deadlock; -#[cfg(not(asynchronix_loom))] -mod simulation_scheduling;