mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-05-16 07:36:05 +02:00
Use chopsticks and limit philosophers. (#2655)
Use chopstick to explain why 2 are needed to eat. Limit async to 2 philosophers so they can deadlock in tokio. (Tested with [3, 4, 5] philosophers and they all were able to run without deadlock with lock ordering disabled.) --------- Co-authored-by: Sterling Stein <scubed2+git@gmail.com>
This commit is contained in:
parent
f531d4dfd7
commit
32a8b4bf13
@ -16,8 +16,8 @@ code below to a file called `src/main.rs`, fill out the blanks, and test that
|
|||||||
|
|
||||||
```rust,compile_fail
|
```rust,compile_fail
|
||||||
{{#include dining-philosophers.rs:Philosopher}}
|
{{#include dining-philosophers.rs:Philosopher}}
|
||||||
// left_fork: ...
|
// left_chopstick: ...
|
||||||
// right_fork: ...
|
// right_chopstick: ...
|
||||||
// thoughts: ...
|
// thoughts: ...
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ code below to a file called `src/main.rs`, fill out the blanks, and test that
|
|||||||
{{#include dining-philosophers.rs:Philosopher-eat}}
|
{{#include dining-philosophers.rs:Philosopher-eat}}
|
||||||
{{#include dining-philosophers.rs:Philosopher-eat-body}}
|
{{#include dining-philosophers.rs:Philosopher-eat-body}}
|
||||||
{{#include dining-philosophers.rs:Philosopher-eat-end}}
|
{{#include dining-philosophers.rs:Philosopher-eat-end}}
|
||||||
// Create forks
|
// Create chopsticks
|
||||||
|
|
||||||
// Create philosophers
|
// Create philosophers
|
||||||
|
|
||||||
|
@ -18,13 +18,13 @@ use std::sync::Arc;
|
|||||||
use tokio::sync::{mpsc, Mutex};
|
use tokio::sync::{mpsc, Mutex};
|
||||||
use tokio::time;
|
use tokio::time;
|
||||||
|
|
||||||
struct Fork;
|
struct Chopstick;
|
||||||
|
|
||||||
struct Philosopher {
|
struct Philosopher {
|
||||||
name: String,
|
name: String,
|
||||||
// ANCHOR_END: Philosopher
|
// ANCHOR_END: Philosopher
|
||||||
left_fork: Arc<Mutex<Fork>>,
|
left_chopstick: Arc<Mutex<Chopstick>>,
|
||||||
right_fork: Arc<Mutex<Fork>>,
|
right_chopstick: Arc<Mutex<Chopstick>>,
|
||||||
thoughts: mpsc::Sender<String>,
|
thoughts: mpsc::Sender<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,11 +40,11 @@ impl Philosopher {
|
|||||||
|
|
||||||
// ANCHOR: Philosopher-eat
|
// ANCHOR: Philosopher-eat
|
||||||
async fn eat(&self) {
|
async fn eat(&self) {
|
||||||
// Keep trying until we have both forks
|
// Keep trying until we have both chopsticks
|
||||||
// ANCHOR_END: Philosopher-eat
|
// ANCHOR_END: Philosopher-eat
|
||||||
// Pick up forks...
|
// Pick up chopsticks...
|
||||||
let _left_fork = self.left_fork.lock().await;
|
let _left_chopstick = self.left_chopstick.lock().await;
|
||||||
let _right_fork = self.right_fork.lock().await;
|
let _right_chopstick = self.right_chopstick.lock().await;
|
||||||
|
|
||||||
// ANCHOR: Philosopher-eat-body
|
// ANCHOR: Philosopher-eat-body
|
||||||
println!("{} is eating...", &self.name);
|
println!("{} is eating...", &self.name);
|
||||||
@ -56,30 +56,33 @@ impl Philosopher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PHILOSOPHERS: &[&str] =
|
// tokio scheduler doesn't deadlock with 5 philosophers, so have 2.
|
||||||
&["Socrates", "Hypatia", "Plato", "Aristotle", "Pythagoras"];
|
static PHILOSOPHERS: &[&str] = &["Socrates", "Hypatia"];
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
// ANCHOR_END: Philosopher-eat-end
|
// ANCHOR_END: Philosopher-eat-end
|
||||||
// Create forks
|
// Create chopsticks
|
||||||
let mut forks = vec![];
|
let mut chopsticks = vec![];
|
||||||
(0..PHILOSOPHERS.len()).for_each(|_| forks.push(Arc::new(Mutex::new(Fork))));
|
PHILOSOPHERS
|
||||||
|
.iter()
|
||||||
|
.for_each(|_| chopsticks.push(Arc::new(Mutex::new(Chopstick))));
|
||||||
|
|
||||||
// Create philosophers
|
// Create philosophers
|
||||||
let (philosophers, mut rx) = {
|
let (philosophers, mut rx) = {
|
||||||
let mut philosophers = vec![];
|
let mut philosophers = vec![];
|
||||||
let (tx, rx) = mpsc::channel(10);
|
let (tx, rx) = mpsc::channel(10);
|
||||||
for (i, name) in PHILOSOPHERS.iter().enumerate() {
|
for (i, name) in PHILOSOPHERS.iter().enumerate() {
|
||||||
let mut left_fork = Arc::clone(&forks[i]);
|
let mut left_chopstick = Arc::clone(&chopsticks[i]);
|
||||||
let mut right_fork = Arc::clone(&forks[(i + 1) % PHILOSOPHERS.len()]);
|
let mut right_chopstick =
|
||||||
|
Arc::clone(&chopsticks[(i + 1) % PHILOSOPHERS.len()]);
|
||||||
if i == PHILOSOPHERS.len() - 1 {
|
if i == PHILOSOPHERS.len() - 1 {
|
||||||
std::mem::swap(&mut left_fork, &mut right_fork);
|
std::mem::swap(&mut left_chopstick, &mut right_chopstick);
|
||||||
}
|
}
|
||||||
philosophers.push(Philosopher {
|
philosophers.push(Philosopher {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
left_fork,
|
left_chopstick,
|
||||||
right_fork,
|
right_chopstick,
|
||||||
thoughts: tx.clone(),
|
thoughts: tx.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,13 @@ minutes: 20
|
|||||||
The dining philosophers problem is a classic problem in concurrency:
|
The dining philosophers problem is a classic problem in concurrency:
|
||||||
|
|
||||||
> Five philosophers dine together at the same table. Each philosopher has their
|
> Five philosophers dine together at the same table. Each philosopher has their
|
||||||
> own place at the table. There is a fork between each plate. The dish served is
|
> own place at the table. There is a chopstick between each plate. The dish
|
||||||
> a kind of spaghetti which has to be eaten with two forks. Each philosopher can
|
> served is spaghetti which requires two chopsticks to eat. Each philosopher can
|
||||||
> only alternately think and eat. Moreover, a philosopher can only eat their
|
> only alternately think and eat. Moreover, a philosopher can only eat their
|
||||||
> spaghetti when they have both a left and right fork. Thus two forks will only
|
> spaghetti when they have both a left and right chopstick. Thus two chopsticks
|
||||||
> be available when their two nearest neighbors are thinking, not eating. After
|
> will only be available when their two nearest neighbors are thinking, not
|
||||||
> an individual philosopher finishes eating, they will put down both forks.
|
> eating. After an individual philosopher finishes eating, they will put down
|
||||||
|
> both chopsticks.
|
||||||
|
|
||||||
You will need a local [Cargo installation](../../cargo/running-locally.md) for
|
You will need a local [Cargo installation](../../cargo/running-locally.md) for
|
||||||
this exercise. Copy the code below to a file called `src/main.rs`, fill out the
|
this exercise. Copy the code below to a file called `src/main.rs`, fill out the
|
||||||
@ -22,17 +23,17 @@ blanks, and test that `cargo run` does not deadlock:
|
|||||||
|
|
||||||
```rust,compile_fail
|
```rust,compile_fail
|
||||||
{{#include dining-philosophers.rs:Philosopher}}
|
{{#include dining-philosophers.rs:Philosopher}}
|
||||||
// left_fork: ...
|
// left_chopstick: ...
|
||||||
// right_fork: ...
|
// right_chopstick: ...
|
||||||
// thoughts: ...
|
// thoughts: ...
|
||||||
}
|
}
|
||||||
|
|
||||||
{{#include dining-philosophers.rs:Philosopher-think}}
|
{{#include dining-philosophers.rs:Philosopher-think}}
|
||||||
|
|
||||||
{{#include dining-philosophers.rs:Philosopher-eat}}
|
{{#include dining-philosophers.rs:Philosopher-eat}}
|
||||||
// Pick up forks...
|
// Pick up chopsticks...
|
||||||
{{#include dining-philosophers.rs:Philosopher-eat-end}}
|
{{#include dining-philosophers.rs:Philosopher-eat-end}}
|
||||||
// Create forks
|
// Create chopsticks
|
||||||
|
|
||||||
// Create philosophers
|
// Create philosophers
|
||||||
|
|
||||||
|
@ -18,13 +18,13 @@ use std::sync::{mpsc, Arc, Mutex};
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
struct Fork;
|
struct Chopstick;
|
||||||
|
|
||||||
struct Philosopher {
|
struct Philosopher {
|
||||||
name: String,
|
name: String,
|
||||||
// ANCHOR_END: Philosopher
|
// ANCHOR_END: Philosopher
|
||||||
left_fork: Arc<Mutex<Fork>>,
|
left_chopstick: Arc<Mutex<Chopstick>>,
|
||||||
right_fork: Arc<Mutex<Fork>>,
|
right_chopstick: Arc<Mutex<Chopstick>>,
|
||||||
thoughts: mpsc::SyncSender<String>,
|
thoughts: mpsc::SyncSender<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,8 +41,8 @@ impl Philosopher {
|
|||||||
fn eat(&self) {
|
fn eat(&self) {
|
||||||
// ANCHOR_END: Philosopher-eat
|
// ANCHOR_END: Philosopher-eat
|
||||||
println!("{} is trying to eat", &self.name);
|
println!("{} is trying to eat", &self.name);
|
||||||
let _left = self.left_fork.lock().unwrap();
|
let _left = self.left_chopstick.lock().unwrap();
|
||||||
let _right = self.right_fork.lock().unwrap();
|
let _right = self.right_chopstick.lock().unwrap();
|
||||||
|
|
||||||
// ANCHOR: Philosopher-eat-end
|
// ANCHOR: Philosopher-eat-end
|
||||||
println!("{} is eating...", &self.name);
|
println!("{} is eating...", &self.name);
|
||||||
@ -57,27 +57,29 @@ fn main() {
|
|||||||
// ANCHOR_END: Philosopher-eat-end
|
// ANCHOR_END: Philosopher-eat-end
|
||||||
let (tx, rx) = mpsc::sync_channel(10);
|
let (tx, rx) = mpsc::sync_channel(10);
|
||||||
|
|
||||||
let forks = (0..PHILOSOPHERS.len())
|
let chopsticks = PHILOSOPHERS
|
||||||
.map(|_| Arc::new(Mutex::new(Fork)))
|
.iter()
|
||||||
|
.map(|_| Arc::new(Mutex::new(Chopstick)))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
for i in 0..forks.len() {
|
for i in 0..chopsticks.len() {
|
||||||
let tx = tx.clone();
|
let tx = tx.clone();
|
||||||
let mut left_fork = Arc::clone(&forks[i]);
|
let mut left_chopstick = Arc::clone(&chopsticks[i]);
|
||||||
let mut right_fork = Arc::clone(&forks[(i + 1) % forks.len()]);
|
let mut right_chopstick =
|
||||||
|
Arc::clone(&chopsticks[(i + 1) % chopsticks.len()]);
|
||||||
|
|
||||||
// To avoid a deadlock, we have to break the symmetry
|
// To avoid a deadlock, we have to break the symmetry
|
||||||
// somewhere. This will swap the forks without deinitializing
|
// somewhere. This will swap the chopsticks without deinitializing
|
||||||
// either of them.
|
// either of them.
|
||||||
if i == forks.len() - 1 {
|
if i == chopsticks.len() - 1 {
|
||||||
std::mem::swap(&mut left_fork, &mut right_fork);
|
std::mem::swap(&mut left_chopstick, &mut right_chopstick);
|
||||||
}
|
}
|
||||||
|
|
||||||
let philosopher = Philosopher {
|
let philosopher = Philosopher {
|
||||||
name: PHILOSOPHERS[i].to_string(),
|
name: PHILOSOPHERS[i].to_string(),
|
||||||
thoughts: tx,
|
thoughts: tx,
|
||||||
left_fork,
|
left_chopstick,
|
||||||
right_fork,
|
right_chopstick,
|
||||||
};
|
};
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user