Description
Came across this as I was playing around with implementing memoisation via double-checked-locking with sync::RWLock
:
The following, which puts the .read()
result into a temporary binding works fine:
// ...
{
let lock_result = self.cache.read(); // <-- temporary binding
match lock_result {
Ok(ref data) if data.len() > to => Some(data[to].clone()),
_ => None,
}
}
.unwrap_or_else(|| {
// We need to write now, so get a write lock
let mut data = self.cache.write().unwrap();
// ...
The following, which directly matches on the result of .read()
, dies in a deadlock:
// ...
{
match self.cache.read() { // <-- direct pattern matching
Ok(ref data) if data.len() > to => Some(data[to].clone()),
_ => None,
}
}
.unwrap_or_else(|| {
// We need to write now, so get a write lock
let mut data = self.cache.write().unwrap();
// ...
I'm guessing the direct pattern match is compiled/desugared in a way that the RwLockReadGuard
never goes out of scope and thus the read lock is never released, but I'm not sure if this is the expected behaviour. It certainly surprised me, especially since the entire "read" block is scoped by its own set of braces.
For extra context, here is the relevant part in my scratchpad project. I have a concurrency test for it that fails to finish if the pattern matching section is changed to match the latter case.