rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.63k stars 12.74k forks source link

TRPL Dining philosophers should all eat together when threaded #26779

Closed vincentbernat closed 9 years ago

vincentbernat commented 9 years ago

The first output of the threaded example is:

Gilles Deleuze is eating.
Gilles Deleuze is done eating.
Friedrich Nietzsche is eating.
Friedrich Nietzsche is done eating.
Michel Foucault is eating.
Baruch Spinoza is eating.
Baruch Spinoza is done eating.
Karl Marx is eating.
Karl Marx is done eating.
Michel Foucault is done eating.

And there is a remark about eating out of order. I would have expected an output like this instead:

Karl Marx is eating.
Baruch Spinoza is eating.
Gilles Deleuze is eating.
Michel Foucault is eating.
Friedrich Nietzsche is eating.

Karl Marx is done eating.
Baruch Spinoza is done eating.
Friedrich Nietzsche is done eating.
Gilles Deleuze is done eating.
Michel Foucault is done eating.

(blank line has been added for clarity)

steveklabnik commented 9 years ago

The output is nondeterministic, so it depends on your system.

Are you on a multicore system? Last time I heard of someone getting sequential output, they were in a VM with only one core....

vincentbernat commented 9 years ago

If we do the timeline of the original events, we get:

Gilles Deleuze is eating.
(1 whole second)
Gilles Deleuze is done eating.
Friedrich Nietzsche is eating.
(1 whole second)
Friedrich Nietzsche is done eating.
Michel Foucault is eating.
Baruch Spinoza is eating.
(1 whole second)
Baruch Spinoza is done eating.
Karl Marx is eating.
(1 whole second)
Karl Marx is done eating.
Michel Foucault is done eating.

So, the initial example takes at least 4 seconds to execute. I am talking about the example without the forks. Unless the threads are cooperative (and thread::sleep_ms isn't yielding which would be odd), this is quite improbable to find a system where Karl Marx is eating after at least 3 seconds. Also, if the threads were cooperative Michel Foucault and Baruch Spinoza should not eat together.

I think that the output was generated at a time where there was no thread::sleep_ms call.

steveklabnik commented 9 years ago

Are you sure you have the exact same code? There's a possibility you're actually joining sequentially....

vincentbernat commented 9 years ago

I am talking about this code (unfortunately, I don't know how to point to a line number in a markdown document):

use std::thread;

struct Philosopher {
    name: String,
}   

impl Philosopher { 
    fn new(name: &str) -> Philosopher {
        Philosopher {
            name: name.to_string(),
        }
    }

    fn eat(&self) {
        println!("{} is eating.", self.name);

        thread::sleep_ms(1000);

        println!("{} is done eating.", self.name);
    }
}

fn main() {
    let philosophers = vec![
        Philosopher::new("Judith Butler"),
        Philosopher::new("Gilles Deleuze"),
        Philosopher::new("Karl Marx"),
        Philosopher::new("Emma Goldman"),
        Philosopher::new("Michel Foucault"),
    ];

    let handles: Vec<_> = philosophers.into_iter().map(|p| {
        thread::spawn(move || {
            p.eat();
        })
    }).collect();

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

So, yes, join is done sequentially, but join is just about waiting for threads to end. All threads should (and they do) execute in parallel. The output should be 5 lines of philosophers starting to eat and 5 lines of philosophers done eating. Any other output would show a flaw in threading.

steveklabnik commented 9 years ago

Sorry, I did a poor job of articulating my concern here. It looks fine.

vincentbernat commented 9 years ago

I am confused. Do you want me to do a PR or do you disagree?

vincentbernat commented 9 years ago

It has been fixed in #26990. Closing.