Skip to content

Using pattern match directly on LockResult causes deadlock #37612

Open
@lloydmeta

Description

@lloydmeta

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-destructorsArea: Destructors (`Drop`, …)A-lifetimesArea: Lifetimes / regionsC-bugCategory: This is a bug.T-langRelevant to the language team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions