Expand description
The Mutable abstraction over the single-threaded and the multi-threaded backend, the
handful of traits that are the only sanctioned way to reach through it, and the rules a
callback has to follow.
§Rule 1: the guard must not outlive the operation
Refer to this case: https://stackoverflow.com/q/79621758/9315497 And this case:
let lock = Mutex::new("My String".to_owned());
// let equals = { lock.lock().unwrap().clone() } == { lock.lock().unwrap().clone() }; // No deadlock
let equals = lock.lock().unwrap().clone() == lock.lock().unwrap().clone(); // DeadlockBoth guards are temporaries of the same statement, so the first one is still alive when the
second is taken. MutableHelper::with_mut and with_ref rule
this out structurally: they hand the callback a &mut T / &T, keep the guard as a temporary
of their own body, and release it before returning. A guard can no longer be named, stored or
compared.
§Rule 2: the callback must not run anything that can take the same lock again
This is the rule the type system cannot enforce, and it is the one that bites. Dropping a value
counts as running code: the obvious clear on a collection of subscriptions
self.subscriptions.with_mut(Vec::clear) // Deadlock: dropping a Subscription disposes it,
// and disposing it takes this very lock again.is a deadlock in the Disposable of merge_all. So is calling an Observer or a Disposable
from inside the callback, because both are user code that can re-enter the operator.
The fix is always the same shape — take the value out under the lock, act on it afterwards:
MutableExt::take_valueinstead ofclear, andMutableExt::replace_valueinstead of an assignment: both return the value they displaced, so the caller acts on it — and drops it — outside the lock.- When the operator has to decide what to do while holding the lock, let the callback compute
an action and return it, and run that action after
with_muthas returned. This is whatref_count,unicast_subject,ambandserialized_deliverydo.
In debug builds this rule is checked at runtime: taking a lock the current thread already holds panics at the offending call site instead of deadlocking.
Traits§
- Mutable
Bool Helper - Mutable
Ext - The handful of one-shot operations that cover most uses of a
Mutable. - Mutable
Helper - The single entry point to a
Mutable, for both the single-threaded and the multi-threaded backend.