Search code examples
rustmutex

Mutex lock not released in Drop::drop?


This program hangs, and I suspect it does not release the mutex lock as it is supposed to. I must be doing something wrong here, but I cannot put my finger on it.

use std::any::TypeId;
use std::collections::HashSet;
use std::marker::PhantomData;
use std::sync::{LazyLock, Mutex};

use unique::Unique;

static ID_SET: LazyLock<Mutex<HashSet<TypeId>>> = LazyLock::new(|| Mutex::new(HashSet::new()));

mod unique {
    use super::*;

    // Due to its private data member, outside this module,
    // this struct can only be created using `new`.
    pub struct Unique<O: 'static>(PhantomData<O>);

    impl<O: 'static> Unique<O> {
        pub fn id() -> TypeId {
            TypeId::of::<O>()
        }

        pub fn new() -> Option<Self> {
            let mut set = ID_SET.lock().unwrap();
            dbg!(set.insert(Self::id())).then_some(Self(PhantomData))
        }
    }

    impl<O: 'static> Drop for Unique<O> {
        fn drop(&mut self) {
            let mut set = ID_SET.lock().unwrap();
            (!set.remove(&Self::id())).then(|| panic!("duplicity detected"));
        }
    }
}

fn main() {
    struct TheOneRing;
    let the_one_ring = Unique::<TheOneRing>::new().unwrap();
    let the_two_ring = Unique::<TheOneRing>::new().unwrap();
    panic!();
}

prints:

[src/main.rs:24:13] set.insert(Self::id()) = true
[src/main.rs:24:13] set.insert(Self::id()) = false

but then hangs...


Solution

  • I must be doing something wrong here, but I cannot put my finger on it.

        pub fn new() -> Option<Self> {
            let mut set = ID_SET.lock().unwrap();
            dbg!(set.insert(Self::id())).then_some(Self(PhantomData))
        }
    

    then_some is eager, so this effectively is

        pub fn new() -> Option<Self> {
            let mut set = ID_SET.lock().unwrap();
            let v = Self(PhantomData);
            dbg!(set.insert(Self::id())).then_some(v);
        }
    

    meaning if set.insert returns false, v is dropped, which tries to acquire the lock, but the lock is already held by new, thus deadlock.