Skip to content

Instantly share code, notes, and snippets.

@wuct
Created June 29, 2020 11:49
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wuct/b98f42dba91a8c08cc6ba2d820d94c1c to your computer and use it in GitHub Desktop.
Save wuct/b98f42dba91a8c08cc6ba2d820d94c1c to your computer and use it in GitHub Desktop.
A deadlock example in Rust with threads and `Mutex`
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
Copy link

marcbrevoort-cyberhive commented Jul 4, 2023

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.

            let delay = std::time::Duration::from_millis(1000);
            std::thread::sleep(delay); 

@wuct
Copy link
Author

wuct commented Aug 14, 2023

@marcbrevoort-cyberhive I think you’re right! Thanks for noticing.

@Jekahome
Copy link

Jekahome commented Apr 9, 2024

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;
}

@Jekahome
Copy link

Jekahome commented Apr 9, 2024

#![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