Shuttle
Shuttle
Shuttle is a deterministic concurrency testing library for Rust, built by AWS. It finds race conditions, deadlocks, and ordering bugs by systematically exploring all possible thread interleavings — bugs that normal tests miss because they depend on timing.
What Shuttle Does
Normal concurrent tests run once with whatever thread scheduling the OS picks. Shuttle runs your code thousands of times with different schedules, finding the one interleaving that triggers a bug:
use shuttle::sync::{Arc, Mutex};
use shuttle::thread;
#[test]
fn test_concurrent_counter() {
shuttle::check_random(|| {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..4 {
let c = counter.clone();
handles.push(thread::spawn(move || {
let mut val = c.lock().unwrap();
*val += 1;
}));
}
for h in handles {
h.join().unwrap();
}
assert_eq!(*counter.lock().unwrap(), 4);
}, 10_000); // explore 10,000 different schedules
}
What It Catches
- Race conditions — data corruption from unsynchronized access
- Deadlocks — circular lock dependencies that freeze your app
- Ordering bugs — logic that accidentally depends on thread timing
- Livelock — threads that keep retrying without making progress
- Atomicity violations — check-then-act patterns that break under concurrency
How to Use
1. Add Dependency
[dev-dependencies]
shuttle = "0.x"
2. Replace std Primitives
In your test code, use Shuttle's drop-in replacements:
// Instead of:
use std::sync::{Arc, Mutex};
use std::thread;
// Use:
use shuttle::sync::{Arc, Mutex};
use shuttle::thread;
3. Wrap in check_random or check_dfs
#[test]
fn test_my_concurrent_code() {
// Random exploration — fast, good for most cases
shuttle::check_random(|| {
// your concurrent code here
}, 10_000);
}
#[test]
fn test_exhaustive() {
// Depth-first search — slower, explores ALL interleavings
shuttle::check_dfs(|| {
// your concurrent code here
});
}
4. Run with Normal Cargo
cargo test
Shuttle runs as part of your normal test suite — no special toolchain needed.
When to Use Shuttle
- Connection pool implementations
- Cache invalidation logic
- Background job workers with shared state
- Any handler that touches shared mutable state
- Database transaction managers
- Lock-free data structures
- Message passing between async tasks
Kani + Shuttle Together
Kani and Shuttle are complementary:
| | Kani | Shuttle | |---|---|---| | Checks | Logic correctness | Concurrency safety | | Explores | All possible values | All possible schedules | | Finds | Panics, overflows, assertion failures | Races, deadlocks, ordering bugs | | How | Model checking (SMT solver) | Schedule exploration |
Use both for maximum coverage — Kani proves your logic, Shuttle proves your concurrency.