We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Fearless Concurrency: Threads, Channels, Shared State step 13 of 24
Poisoning: what a panic does to a lock
Apply a list of operations to a shared counter, one thread per op, and report the state of the lock after each.
pub fn poison_report(ops: Vec<i64>) -> Vec<String>
The counter is an Arc<Mutex<i64>> starting at 0. For each op, in order:
spawn a thread, join it, then report.
The worker locks the mutex, adds op to the counter, and — if op is
negative — panics while still holding the guard.
The report, one entry per op:
-
lock healthy →
"ok:<value>"; -
lock poisoned → recover the value, clear the poison, and push
"poisoned:<value>".
[1, 2, -1, 3] -> ["ok:1", "ok:3", "poisoned:2", "ok:5"]
[-5, 2] -> ["poisoned:-5", "ok:-3"]
Note the negative op’s contribution is in the total: the worker updated the counter and then panicked. That is exactly the situation poisoning exists to warn you about, and here it happens to be harmless.
Spawn-and-join each op before starting the next. Yes, that makes the concurrency purely illustrative — the point is that a panicking thread’s effect on the lock is observable and deterministic, and overlapping the ops would make the poisoning point scheduler-dependent.
Why lock() returns a Result
You have written .unwrap() after every lock() in this track. Here is what
it is for.
If a thread panics while holding a MutexGuard, unwinding drops the guard and
releases the lock — but the data may be halfway through an update. Half a
transaction applied. An invariant temporarily broken and never restored.
Mutex records that fact. Every subsequent lock() returns
Err(PoisonError<MutexGuard<T>>), forever, until someone clears it. The
message is not “you cannot have this data”; it is “this data may be
inconsistent, and someone should decide what to do about that.”
So .unwrap() after lock() is a real decision: if a previous holder
panicked, take this thread down too. Often that is the right call. It is
still a decision, and this problem asks you to make the other one.
Recovering
The error carries the guard, so the data is not lost:
match shared.lock() {
Ok(guard) => { /* healthy */ }
Err(poisoned) => {
let guard = poisoned.into_inner(); // the MutexGuard, poison and all
// ... inspect or repair the value ...
shared.clear_poison(); // stable since 1.77
}
}
PoisonError::into_inner() hands you the guard. Mutex::clear_poison()
resets the flag so later lock() calls succeed again. Together they are
“I have looked at the damage and I accept responsibility for it”.
Mind the guard’s lifetime: read the value out and let the guard drop
before you call clear_poison, or you are holding the lock while asking
the mutex to do something.
The rules for the rest of the family
| type | poisons? |
|---|---|
Mutex<T> |
yes, on any panic while the guard is held |
RwLock<T> |
only on a writer panic. A reader panic cannot break an invariant, because readers cannot write. |
OnceLock<T> |
never |
LazyLock<T, F> |
yes, and irrecoverably — a panic in the initialiser leaves it permanently unusable |
That OnceLock/LazyLock asymmetry catches people out; they look like
siblings and behave nothing alike under panic.
Poisoning is advisory, and contested
Be precise about what the guarantee is worth. The std docs say poisoning is advisory: it can be missed during a double panic, and unsafe code must never rely on it for soundness. It is a debugging aid with a strong opinion, not a safety mechanism.
And the design is genuinely disputed. The cost is .unwrap() on every single
lock in every program, which trains people to ignore the Result — the exact
opposite of what error handling is for. parking_lot::Mutex has no poisoning
at all, and that is the most-cited reason people leave std for it.
The direction of travel is public: std::sync::nonpoison exists on nightly
(sync_nonpoison, tracking issue #134645), and there is an open proposal
(rust-lang/rust#149359) to make non-poisoning locks the Edition 2027 default.
Learn the mechanism, and know it is a live argument rather than settled law.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.