Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does 'if let' block the execution with usage of Mutex?

I have bumped into this deadlock scenario while working with Mutex

The struct that contains a field of Mutex type is as follows:

struct MyStruct {
    inner_map: Arc<Mutex<HashMap<i32, Vec<i32>>>>,
}

I have accessed this inner map via lock on Mutex:

impl MyStruct {
    fn test_lock(&self, key: &i32) {
        let my_option = self.inner_map.lock().unwrap().remove(key);
        if let Some(my_vec) = my_option {
            for _inner_val in my_vec {
                self.inner_map.lock().unwrap();
                println!("Passed test_lock1");
            }
        }
    }
}

This is working fine without a deadlock since I removed the value from HashMap and get the ownership out of the HashMap


Very similar function to test_lock with only difference instead of declaring removed value to my_option variable used it on the fly if let call and it is causing deadlock in this case:

impl MyStruct{
    // Why this function goes to deadlock since remove gets the ownership of the data?
    fn test_lock2(&self, key: &i32) {
        if let Some(my_vec) = self.inner_map.lock().unwrap().remove(key) {
            for _inner_val in my_vec {
                self.inner_map.lock().unwrap();
                println!("Passed test_lock2");
            }
        }
    }
}

What is the main reason why declaring a variable changes that kind of behavior?

Playground

like image 878
Akiner Alkan Avatar asked Jun 20 '19 08:06

Akiner Alkan


People also ask

Is a mutex blocking?

It is blocked. It will not proceed until able. You don't say which language you're using, but most languages/libraries have lock objects where you can "attempt" to take the lock and then carry on and do something different depending on whether you succeeded or not.

Does mutex block the thread?

If the mutex is unlocked, the subroutine locks it. If the mutex is already locked by another thread, the subroutine blocks the calling thread until the mutex is unlocked. If the mutex is already locked by the calling thread, the subroutine might block forever or return an error depending on the type of mutex.

What does sync mutex lock while it is locked?

Mutex. Lock locks the mutex, nothing else. It is up to you to use it correctly to synchronize access.

What is mutex rust?

A mutual exclusion primitive useful for protecting shared data. This mutex will block threads waiting for the lock to become available. The mutex can be created via a new constructor. Each mutex has a type parameter which represents the data that it is protecting.


1 Answers

The lock is released when the LockResult goes out of scope.

Now we have to go deeper in scope rules regarding this temporary value.

The scope of a temporary value is the enclosing statement.

In the first snippet, it means the lock goes out of scope before entering the if/let construct. There's no deadlock.

But the scope of the temporary value in the if let condition is the whole if/let construct:

the lifetime of temporary values is typically

  • the innermost enclosing statement; the tail expression of a block is considered part of the statement that encloses the block, or
  • the condition expression or the loop conditional expression if the temporary is created in the condition expression of an if or in the loop conditional expression of a while expression.

When a temporary value expression is being created that is assigned into a let declaration, however, the temporary is created with the lifetime of the enclosing block instead, as using the enclosing let declaration would be a guaranteed error (since a pointer to the temporary would be stored into a variable, but the temporary would be freed before the variable could be used)

In the second snippet, the lock's scope thus covers the whole if/let construct.

This explains why the first lock is still active when you try to lock again in the loop.

like image 96
Denys Séguret Avatar answered Oct 13 '22 02:10

Denys Séguret