rustconcurrency

Why will this Rust code NOT experience a deadlock?


use std::{sync::{Arc, Mutex}, thread, time::Duration};
fn main() {
    let mut forks = Vec::with_capacity(5);
    let mut handles = vec![];
    for _ in 1..=5 {
        forks.push(Arc::new(Mutex::new(0)));
    }

    for i in 0..=4 {
        let left_fork = forks[i].clone();
        let right_fork = forks[(i + 1) % 5].clone();
        let handle = thread::spawn(move || {
            loop {
                println!("philosopher {} is thinking...", i);
                thread::sleep(Duration::from_millis(100));
                let right_fork_guard = right_fork.lock().unwrap();
                println!("philosopher {} picked up right fork", i);
                let left_fork_guard = left_fork.lock().unwrap();
                println!("philosopher {} picked up left fork and starts eating..", i);

                thread::sleep(Duration::from_millis(100));
                
                drop(left_fork_guard);
                println!("philosopher {} put down left fork.", i);
                // thread::sleep(Duration::from_millis(100));
                drop(right_fork_guard);
                println!("philosopher {} put down right fork.", i);
            }
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }
}

I made this program to simulate the Dining Philosophers Progblem. I have done NOTHING to avoid deadlock, yet this program can run normally. In fact, it indeed runs into deadlock from time to time, but in some times it just run without deadlock(I waited 5 min+, no deadlock!). And when it runs into a deadlock, it must be their first eating round. If they successfully go through first round, they will never stuck again.

Another strange thing is that, it will run into deadlock in Linux、Mac, but not in WINDOWS and WSL. HOW STRANGE! Please HELP!

At first, I didn't have the sleep function between "philosopher is thinking" and right_fork lock, but have the sleep function between the two drop, and this program just never run into deadlock! Only after I change it to this, it starts to run into deadlock sometimes.


Solution

  • You can get a systematic deadlock if you add a sleep between the two lock calls:

    use std::{sync::{Arc, Mutex}, thread, time::Duration};
    fn main() {
        let mut forks = Vec::with_capacity(5);
        let mut handles = vec![];
        for _ in 1..=5 {
            forks.push(Arc::new(Mutex::new(0)));
        }
    
        for i in 0..=4 {
            let left_fork = forks[i].clone();
            let right_fork = forks[(i + 1) % 5].clone();
            let handle = thread::spawn(move || {
                loop {
                    println!("philosopher {} is thinking...", i);
                    thread::sleep(Duration::from_millis(100));
                    let right_fork_guard = right_fork.lock().unwrap();
                    println!("philosopher {} picked up right fork", i);
                    thread::sleep(Duration::from_millis(1));
                    let left_fork_guard = left_fork.lock().unwrap();
                    println!("philosopher {} picked up left fork and starts eating..", i);
    
                    thread::sleep(Duration::from_millis(100));
                    
                    drop(left_fork_guard);
                    println!("philosopher {} put down left fork.", i);
                    // thread::sleep(Duration::from_millis(100));
                    drop(right_fork_guard);
                    println!("philosopher {} put down right fork.", i);
                }
            });
            handles.push(handle);
        }
    
        for handle in handles {
            handle.join().unwrap();
        }
    }
    

    playground

    Without that sleep, it is very likely that at least one philosopher will win the race and be able to pick up both forks before another blocks him. After which point, at least this philosopher will try to eat while his neighbours are thinking and will sleep while they are eating, meaning that there is no real reason for deadlocks.