interweave
Stateless model-checking sandbox built around Optimal DPOR. Write a handful
of processes as async Rust; explore runs them under every meaningfully distinct
schedule and returns the first interleaving that fails — replayable exactly — or
proves that none can. The same run drives an Observer, so you can watch which
interleavings the search visits and which it prunes.
Status: early-stage research sandbox (
0.x); the API is still taking shape and may change between minor releases.
Processes run on a custom single-threaded, deterministic executor — no async
runtime, because controlling the schedule is the whole point. Each primitive's
observable operations are .await points that hand control to the checker; new
ones (a lock, a barrier, another channel) plug in through the Object trait and
World::register. The strategy is Optimal DPOR (Abdulla et al., POPL'14): it
visits exactly one interleaving per Mazurkiewicz equivalence class, so the search
stays exhaustive without enumerating every ordering.
Install
Usage
Finding a bug
A producer hands a value to a consumer through a ready flag, but raises the
flag before writing the value. explore returns Ok when every interleaving
passes, or the first failing one — here the schedule where the consumer sees
ready == 1 yet reads the stale data, the race behind broken double-checked
locking:
use ;
// `()` is the no-op observer.
explore.expect_err;
Writing the value before raising the flag fixes it, and the checker then clears every interleaving.
Watching the algorithm
Pass an Observer instead of &mut () to see the search from the inside: its
step method fires at each decision, and a Maximal step marks one complete
interleaving. Here one writer races two readers on a single atomic — the two
reads commute, so of the 3! = 6 orderings Optimal DPOR visits only the four
that differ observably:
use ;
// Record every complete interleaving the search runs to the end.
;
let mut seen = default;
explore.expect;
// Four, not six: the two that only swap the order of the independent reads are
// pruned as equivalent.
assert_eq!;
Step also reports the driver's other decisions — descend, seed, race-reversal,
pop — each carrying the live wakeup tree and sleep sets.
More worked programs live in examples/: a bank ledger and the
publish unsafe-publication race (atomic bug hunts), an rpc_mux reply-misrouting
bug over an MPSC channel, a from-scratch custom Object (custom_object), and the
POPL'14 readers / lastzero / indexer benchmarks. The full API is on
docs.rs.
Contributing
Issues and pull requests are welcome. Before sending a change, run the checks CI does:
RUSTDOCFLAGS="-D warnings"
CI also checks the build on the 1.96 MSRV. Formatting requires nightly rustfmt
because rustfmt.toml enables unstable options (wrap_comments, comment_width).
References
- Abdulla et al., Optimal Dynamic Partial Order Reduction (POPL'14)
- Flanagan & Godefroid, Dynamic Partial-Order Reduction for Model Checking Software (POPL'05) — the classical DPOR this builds on
- Nidhugg, Concuerror — reference implementations