19 releases

0.8.0 Oct 12, 2024
0.7.1 May 31, 2024
0.7.0 Mar 7, 2024
0.6.1 May 23, 2023
0.0.0 Oct 4, 2020

#41 in Concurrency

Download history 19654/week @ 2024-08-23 15400/week @ 2024-08-30 18737/week @ 2024-09-06 10597/week @ 2024-09-13 16272/week @ 2024-09-20 14258/week @ 2024-09-27 17719/week @ 2024-10-04 21396/week @ 2024-10-11 21703/week @ 2024-10-18 21574/week @ 2024-10-25 22041/week @ 2024-11-01 19301/week @ 2024-11-08 20748/week @ 2024-11-15 21617/week @ 2024-11-22 32506/week @ 2024-11-29 25392/week @ 2024-12-06

104,886 downloads per month
Used in 19 crates (16 directly)

Apache-2.0

480KB
9K SLoC

Rust 6K SLoC // 0.1% comments TypeScript 2.5K SLoC // 0.1% comments JavaScript 72 SLoC // 0.1% comments

Shuttle

crates.io docs.rs Tests

Shuttle is a library for testing concurrent Rust code. It is an implementation of a number of randomized concurrency testing techniques, including A Randomized Scheduler with Probabilistic Guarantees of Finding Bugs.

Getting started

Consider this simple piece of concurrent code:

use std::sync::{Arc, Mutex};
use std::thread;

let lock = Arc::new(Mutex::new(0u64));
let lock2 = lock.clone();

thread::spawn(move || {
    *lock.lock().unwrap() = 1;
});

assert_eq!(0, *lock2.lock().unwrap());

There is an obvious race condition here: if the spawned thread runs before the assertion, the assertion will fail. But writing a unit test that finds this execution is tricky. We could run the test many times and try to "get lucky" by finding a failing execution, but that's not a very reliable testing approach. Even if the test does fail, it will be difficult to debug: we won't be able to easily catch the failure in a debugger, and every time we make a change, we will need to run the test many times to decide whether we fixed the issue.

Randomly testing concurrent code with Shuttle

Shuttle avoids this issue by controlling the scheduling of each thread in the program, and scheduling those threads randomly. By controlling the scheduling, Shuttle allows us to reproduce failing tests deterministically. By using random scheduling, with appropriate heuristics, Shuttle can still catch most (non-adversarial) concurrency bugs even though it is not an exhaustive checker.

A Shuttle version of the above test just wraps the test body in a call to Shuttle's check_random function, and replaces the concurrency-related imports from std with imports from shuttle:

use shuttle::sync::{Arc, Mutex};
use shuttle::thread;

shuttle::check_random(|| {
    let lock = Arc::new(Mutex::new(0u64));
    let lock2 = lock.clone();

    thread::spawn(move || {
        *lock.lock().unwrap() = 1;
    });

    assert_eq!(0, *lock2.lock().unwrap());
}, 100);

This test detects the assertion failure with extremely high probability (over 99.9999%).

Shuttle is inspired by the Loom library for testing concurrent Rust code. Shuttle focuses on randomized testing, rather than the exhaustive testing that Loom offers. This is a soundness—scalability trade-off: Shuttle is not sound (a passing Shuttle test does not prove the code is correct), but it scales to much larger test cases than Loom. Empirically, randomized testing is successful at finding most concurrency bugs, which tend not to be adversarial.

License

This project is licensed under the Apache-2.0 License.

Security

See CONTRIBUTING for more information.

Dependencies

~2–26MB
~372K SLoC