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
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.
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.
Mutex. Lock locks the mutex, nothing else. It is up to you to use it correctly to synchronize access.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With