Closed
Description
I have checked #21114 #37612 #60107 and #78390 .
I tried this code:
use std::sync::Mutex;
fn main() {
dead_lock_2();
}
fn dead_lock_1() {
let vec_mutex = Mutex::new(vec![1,2,3]);
// It's known that the temporary MutexGuard will be dropped at the end of while let, and will cause a dead lock.
while let Some(num) = vec_mutex.lock().unwrap().pop() {
if num == 2 {
vec_mutex.lock().unwrap().push(4);
}
println!("got {}", num);
}
}
fn dead_lock_2() {
let vec_mutex = Mutex::new(vec![1,2,3]);
// I add a scope, and expect the temporary MutexGuard dropped at the end of the scope. It does not work as I expect.
while let Some(num) = {
vec_mutex.lock().unwrap().pop()
} {
if num == 2 {
vec_mutex.lock().unwrap().push(4);
}
println!("got {}", num);
}
}
fn no_dead_lock() {
let vec_mutex = Mutex::new(vec![1,2,3]);
// It works.
while let Some(num) = {
let num = vec_mutex.lock().unwrap().pop();
num
} {
if num == 2 {
vec_mutex.lock().unwrap().push(4);
}
println!("got {}", num);
}
}
I expected to see this happen: the dead_lock_2 function got outputs as expect.
got 3
got 2
got 4
got 1
Instead, this happened: the dead_lock_2 function got deadlock unexpected.
got 3
<--dead lock occurs-->
Meta
rustc --version --verbose
:
rustc 1.52.0-nightly (36f1f04f1 2021-03-17)
binary: rustc
commit-hash: 36f1f04f18b89ba4a999bcfd6584663fd6fc1c5d
commit-date: 2021-03-17
host: x86_64-pc-windows-msvc
release: 1.52.0-nightly
LLVM version: 12.0.0
I have got the answer: #37612 (comment)
The drop scope of the temporary is usually the end of the enclosing statement.
But a block expression is not a statement, so { vec_mutex.lock().unwrap().pop() }
will not drop the temporary.
It's a pitfall, so I think it should be:
- Add it clearly in document: https://doc.rust-lang.org/stable/reference/expressions.html#temporaries : Temporaries created in a block expression will not be dropped at the end of the block.
- Or change the behavior: The end of a block expression is also the drop scope of the temporary created in the block.