Created
June 29, 2020 11:49
-
-
Save wuct/b98f42dba91a8c08cc6ba2d820d94c1c to your computer and use it in GitHub Desktop.
A deadlock example in Rust with threads and `Mutex`
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::sync::{Arc, Mutex}; | |
use std::thread; | |
fn main() { | |
let a = Arc::new(Mutex::new(0)); | |
let b = Arc::new(Mutex::new(0)); | |
let mut handles = vec![]; | |
{ | |
let a = Arc::clone(&a); | |
let b = Arc::clone(&b); | |
let handle = thread::spawn(move || { | |
let mut a_num = a.lock().unwrap(); | |
*a_num += 1; | |
println!("Thread 1 holds a lock and starts waiting b lock"); | |
let mut b_num = b.lock().unwrap(); | |
*b_num += 1; | |
}); | |
handles.push(handle); | |
} | |
{ | |
let a = Arc::clone(&a); | |
let b = Arc::clone(&b); | |
let handle = thread::spawn(move || { | |
let mut b_num = b.lock().unwrap(); | |
*b_num += 1; | |
println!("Thread 2 holds b lock and starts waiting a lock"); | |
let mut a_num = a.lock().unwrap(); | |
*a_num += 1; | |
println!("Thread 2"); | |
}); | |
handles.push(handle); | |
} | |
for handle in handles { | |
handle.join().unwrap(); | |
} | |
println!("Done {}", *a.lock().unwrap()); // never reach here | |
} |
@marcbrevoort-cyberhive I think you’re right! Thanks for noticing.
You must use this. To allow at least one thread to release the lock.
...
--- 1 thread ----
//let mut b_num = b.lock().unwrap();
if let Ok(ref mut b_num) = b.try_lock(){
**b_num += 1;
}
...
--- 2 thread ----
//let mut a_num = a.lock().unwrap();
if let Ok(ref mut a_num) = a.try_lock(){
**a_num += 1;
}
#![feature(mutex_unlock)]
use std::thread;
use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
fn main(){
let a = Arc::new(Mutex::new(0));
let b = Arc::new(Mutex::new(0));
let mut handles = vec![];
{
let a = Arc::clone(&a);
let b = Arc::clone(&b);
let handle = thread::spawn(move || {
let mut a_num = a.lock().unwrap();
*a_num += 1;
println!("Thread 1 holds a lock and starts waiting b lock");
//let mut b_num = b.lock().unwrap();
if let Ok(ref mut b_num) = b.try_lock(){
**b_num += 1;
}else{
Mutex::unlock(a_num);
}
});
handles.push(handle);
}
{
let a = Arc::clone(&a);
let b = Arc::clone(&b);
let handle = thread::spawn(move || {
let mut b_num = b.lock().unwrap();
*b_num += 1;
println!("Thread 2 holds b lock and starts waiting a lock");
//let mut a_num = a.lock().unwrap();
if let Ok(ref mut a_num) = a.try_lock(){
**a_num += 1;
}else{
Mutex::unlock(b_num);
}
println!("Thread 2");
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Done {}", *a.lock().unwrap()); // never reach here
}
cargo +nightly run
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This may never deadlock if Thread 1 can capture and release the lock before Thread 2 has started up.
A deadlock can be forced by in this case by giving it more time: Simply add this before the first print statement in either thread.