diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f96772..99a82d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,11 +92,36 @@ jobs: - name: Run basic tests run: cargo test - - name: Run concurrent tests and benchmarks + - name: Run concurrent tests # FIXME: macOS runners are too overloaded for concurrent testing if: runner.os != 'macOS' - run: cargo test --release -- --ignored --nocapture --test-threads=1 + run: cargo test --release -- --ignored --test-threads=1 + + + # Run the tests in Miri for more thorough runtime checks + # + # Since Miri is an abstract machine and this crate contains no OS-specific + # code, testing on one single OS is sufficient. Per Miri documentation, Linux + # gets the highest-quality support, so that's our obvious pick. + miri: + # Only run on "pull_request" event for external PRs. This is to avoid + # duplicate builds for PRs created from internal branches. + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository || github.event_name == 'schedule' + + runs-on: ubuntu-latest + + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + - name: Set up toolchain + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + components: miri,rust-src + toolchain: nightly + - name: Run tests + run: cargo +nightly miri test --features=miri -- --include-ignored # Check compatibility with newer Rust/deps versions (scheduled CI) # @@ -134,4 +159,4 @@ jobs: - name: Run concurrent tests and benchmarks # FIXME: macOS runners are too overloaded for concurrent testing if: runner.os != 'macOS' - run: cargo test --release -- --ignored --nocapture --test-threads=1 + run: cargo test --release -- --ignored --test-threads=1 diff --git a/Cargo.toml b/Cargo.toml index c3cadaf..9697c49 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,11 @@ rust-version = "1.70" [badges] maintenance = { status = "passively-maintained" } +[features] +# Adjust the test configuration (e.g. reduce problem sizes) so the tests can run +# under Miri within a reasonable time budget. +miri = [] + [dependencies] crossbeam-utils = { version = "0.8.11", default-features = false } diff --git a/src/lib.rs b/src/lib.rs index 632fbfb..da87bcf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -763,7 +763,10 @@ mod tests { fn contended_concurrent_read_write() { // We will stress the infrastructure by performing this many writes // as a reader continuously reads the latest value + #[cfg(not(feature = "miri"))] const TEST_WRITE_COUNT: usize = 100_000_000; + #[cfg(feature = "miri")] + const TEST_WRITE_COUNT: usize = 3_000; // This is the buffer that our reader and writer will share let buf = TripleBuffer::new(&RaceCell::new(0)); @@ -809,7 +812,10 @@ mod tests { fn uncontended_concurrent_read_write() { // We will stress the infrastructure by performing this many writes // as a reader continuously reads the latest value + #[cfg(not(feature = "miri"))] const TEST_WRITE_COUNT: usize = 625; + #[cfg(feature = "miri")] + const TEST_WRITE_COUNT: usize = 200; // This is the buffer that our reader and writer will share let buf = TripleBuffer::new(&RaceCell::new(0)); @@ -852,7 +858,10 @@ mod tests { fn concurrent_bidirectional_exchange() { // We will stress the infrastructure by performing this many writes // as a reader continuously reads the latest value + #[cfg(not(feature = "miri"))] const TEST_WRITE_COUNT: usize = 100_000_000; + #[cfg(feature = "miri")] + const TEST_WRITE_COUNT: usize = 3_000; // This is the buffer that our reader and writer will share let buf = TripleBuffer::new(&RaceCell::new(0)); @@ -898,7 +907,7 @@ mod tests { /// Range check for triple buffer indexes #[allow(unused_comparisons)] fn index_in_range(idx: BufferIndex) -> bool { - (idx >= 0) & (idx <= 2) + (0..=2).contains(&idx) } /// Get a pointer to the target of some reference (e.g. an &, an Arc...)