You have now written two dozen concurrent programs and every one of them passed. This page is about what that does and does not mean, because a course that pretends its grader is a correctness oracle produces overconfident engineers, and in concurrent code that is the most dangerous kind.
What a passing tick actually certifies
This program compiled, satisfied
clippy -D warnings, and produced the expected output on this machine, on this run.
That is the entire claim. It is a real claim — deterministic assertions over genuinely racy executions caught a great many mistakes along the way — and it stops well short of “this program is correct”.
The bugs that are invisible here
Memory-ordering mistakes. Weaken the Release in the SPSC ring buffer to
Relaxed and every test still passes, because on x86-64 the two compile to
the same instruction. On aarch64 the program becomes wrong occasionally.
Nothing in a single-file rustc -O build can distinguish a correct ordering
from a lucky one.
The ABA problem. A CAS loop reads A, another thread changes it to B
and back to A, and your compare_exchange succeeds against a value that is
numerically identical and semantically different. Lock-free stacks lose nodes
this way. It needs a specific interleaving, and this grader runs one
interleaving — whichever the OS picked.
A wrong unsafe impl Send. Get the bound wrong on your wrapper and an
Rc escapes to another thread. The refcount races, the value is freed while
live, and the program keeps running on freed memory. There is a decent chance
it prints the right answer while doing it.
Almost all lock-free unsoundness, for the same reason: the failure needs a window of a few instructions to line up, and it will not on the run that counts.
Lock ordering. The transfers problem hung reliably — but only because it was designed to, with ten thousand crossing transfers. Real deadlocks appear under production load and not in tests.
The drop(tx) hang. Compiles clean, passes clippy clean, hangs forever.
Zero diagnostics from anything.
An honest scorecard for clippy
From this course’s own testing, on clippy 0.1.95:
What it caught:
-
manual_div_ceil— an overflow bug dressed as a style nit. -
let_underscore_lock— deny-by-default in rustc itself, a genuine hard error on a lock that guards nothing. -
missing_spin_loop— but only when the atomic is a local; it did not fire through anArcor a reference parameter. -
rc_clone_in_vec_init,declare_interior_mutable_constand rustc’sconst_item_interior_mutations— theconstversusstaticmistake, flagged three separate ways. -
invalid_atomic_ordering— deny-by-default, catching the syntactically impossible orderings for free.
What it caught nothing for:
-
the
drop(tx)hang; - the lock-ordering deadlock;
- a wrong memory ordering;
- holding a guard across expensive work;
- a spin loop that serialises a whole worker pool.
Do not oversell the tool. Clippy is excellent at local, syntactic, known mistakes and has essentially nothing to say about global, temporal, emergent ones. The bugs in the second list are the expensive ones.
The three tools that do find them
Miri — an interpreter for Rust’s MIR that executes your program under a
model of the abstract machine rather than on hardware. It detects undefined
behaviour: out-of-bounds accesses, use-after-free, invalid values, violations
of the aliasing model (Stacked/Tree Borrows) — and, with
-Zmiri-preemption-rate, data races. It is how you check an
unsafe impl Send or a hand-written UnsafeCell wrapper.
rustup +nightly component add miri
cargo +nightly miri test
Needs nightly and cargo, so it cannot run here. It is also slow — orders of magnitude — which is fine, because you point it at the unsafe core, not at the whole program.
loom — exhaustive interleaving exploration under the C11 relaxed memory
model. You write your test against loom::sync::atomic instead of
std::sync::atomic, and loom runs it for every legal interleaving and every
legal ordering outcome, including ones your hardware would never produce.
loom::model(|| {
// your lock-free structure, checked under every schedule
});
This is the tool that would catch a wrong Release in the ring buffer. It is
an external crate, so it cannot exist in a no-dependency single-file build.
It also needs a small state space — you shrink the test to two threads and
three operations, and that is the skill.
ThreadSanitizer — RUSTFLAGS="-Zsanitizer=thread", nightly only.
Instruments memory accesses and detects data races that actually happen at
run time. Cheaper than Miri, less thorough than loom, and useful on real
workloads including across FFI boundaries.
Also absent, for the same structural reason: cargo test --test-threads=1,
#[should_panic], #[bench], and property-testing crates like proptest —
all of them need cargo.
💡Your lock-free queue passes its tests, passes Miri, and passes loom. Is it correct? click to reveal
Closer than almost any code you will write, and still not proven.
Miri checks the executions it runs, under one preemption schedule unless you vary it. It finds UB it observes; it does not prove the absence of UB in executions it did not take.
loom is exhaustive over the interleavings of the test you wrote. If your test uses two threads and your bug needs three, loom will not find it. Its exhaustiveness is over schedules, not over inputs or shapes.
Neither says anything about whether the algorithm is right — whether your queue is actually FIFO, whether it can lose an element under a sequence you never modelled, whether the API’s safety contract is stateable.
Which is why the answer for production code is: use std’s primitives, or crossbeam’s, both of which have had years of adversarial attention. Write your own lock-free structure to learn how they work — as you did in this track — and reach for one you did not write when correctness matters more than the education.
What std still does not give you
Worth knowing so you recognise the gap when you hit it:
-
std::sync::mpmc— a genuine multi-consumer channel. Unstable (mpmc_channel, tracking issue #126840). This is why every std worker pool is written asArc<Mutex<Receiver<T>>>. -
std::sync::nonpoison— locks without poisoning. Unstable (sync_nonpoison, #134645), with an open proposal (rust-lang/rust#149359) to make them the Edition 2027 default. -
std::cell::SyncUnsafeCell— still unstable, which is why people write the two-line wrapper by hand. - No reentrant mutex, no lock hierarchy checker, no deadlock detector.
And the crates that fill the gaps, so you know what to reach for:
| crate | what it gives you |
|---|---|
| crossbeam | mpmc channels, epoch-based reclamation, better sync primitives |
| rayon |
data parallelism — par_iter, work stealing, the safe version of everything in this track |
| parking_lot | smaller, faster, non-poisoning locks, optional deadlock detection |
| dashmap |
a sharded concurrent map, so you stop writing Arc<Mutex<HashMap>> |
| loom | exhaustive interleaving testing |
The disposition to leave with
Rust’s guarantee is genuine and it is narrow: safe Rust eliminates data races. That is an enormous, unusual achievement, and it is not the same as eliminating concurrency bugs.
Deadlocks, livelocks, lost updates, missed wakeups, lost wakeups, wrong memory orderings, ABA, unbounded queues, false sharing, and every ordering assumption you made without writing it down — all of those survive the borrow checker intact, and this course’s grader saw only the ones that happened to bite on one machine on one run.
So: prefer designs where the bug cannot be expressed. Partition instead of sharing. Use a channel when the work has a direction. Take std’s primitives over your own. Write down the ordering you assumed, because nothing else in your toolchain will record it. And treat a green tick as evidence, not proof.