Skip to content

Commit 32a8b4b

Browse files
scubed2Sterling Stein
and
Sterling Stein
authored
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 <[email protected]>
1 parent f531d4d commit 32a8b4b

File tree

4 files changed

+50
-44
lines changed

4 files changed

+50
-44
lines changed

src/concurrency/async-exercises/dining-philosophers.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ code below to a file called `src/main.rs`, fill out the blanks, and test that
1616

1717
```rust,compile_fail
1818
{{#include dining-philosophers.rs:Philosopher}}
19-
// left_fork: ...
20-
// right_fork: ...
19+
// left_chopstick: ...
20+
// right_chopstick: ...
2121
// thoughts: ...
2222
}
2323
@@ -26,7 +26,7 @@ code below to a file called `src/main.rs`, fill out the blanks, and test that
2626
{{#include dining-philosophers.rs:Philosopher-eat}}
2727
{{#include dining-philosophers.rs:Philosopher-eat-body}}
2828
{{#include dining-philosophers.rs:Philosopher-eat-end}}
29-
// Create forks
29+
// Create chopsticks
3030
3131
// Create philosophers
3232

src/concurrency/async-exercises/dining-philosophers.rs

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ use std::sync::Arc;
1818
use tokio::sync::{mpsc, Mutex};
1919
use tokio::time;
2020

21-
struct Fork;
21+
struct Chopstick;
2222

2323
struct Philosopher {
2424
name: String,
2525
// ANCHOR_END: Philosopher
26-
left_fork: Arc<Mutex<Fork>>,
27-
right_fork: Arc<Mutex<Fork>>,
26+
left_chopstick: Arc<Mutex<Chopstick>>,
27+
right_chopstick: Arc<Mutex<Chopstick>>,
2828
thoughts: mpsc::Sender<String>,
2929
}
3030

@@ -40,11 +40,11 @@ impl Philosopher {
4040

4141
// ANCHOR: Philosopher-eat
4242
async fn eat(&self) {
43-
// Keep trying until we have both forks
43+
// Keep trying until we have both chopsticks
4444
// ANCHOR_END: Philosopher-eat
45-
// Pick up forks...
46-
let _left_fork = self.left_fork.lock().await;
47-
let _right_fork = self.right_fork.lock().await;
45+
// Pick up chopsticks...
46+
let _left_chopstick = self.left_chopstick.lock().await;
47+
let _right_chopstick = self.right_chopstick.lock().await;
4848

4949
// ANCHOR: Philosopher-eat-body
5050
println!("{} is eating...", &self.name);
@@ -56,30 +56,33 @@ impl Philosopher {
5656
}
5757
}
5858

59-
static PHILOSOPHERS: &[&str] =
60-
&["Socrates", "Hypatia", "Plato", "Aristotle", "Pythagoras"];
59+
// tokio scheduler doesn't deadlock with 5 philosophers, so have 2.
60+
static PHILOSOPHERS: &[&str] = &["Socrates", "Hypatia"];
6161

6262
#[tokio::main]
6363
async fn main() {
6464
// ANCHOR_END: Philosopher-eat-end
65-
// Create forks
66-
let mut forks = vec![];
67-
(0..PHILOSOPHERS.len()).for_each(|_| forks.push(Arc::new(Mutex::new(Fork))));
65+
// Create chopsticks
66+
let mut chopsticks = vec![];
67+
PHILOSOPHERS
68+
.iter()
69+
.for_each(|_| chopsticks.push(Arc::new(Mutex::new(Chopstick))));
6870

6971
// Create philosophers
7072
let (philosophers, mut rx) = {
7173
let mut philosophers = vec![];
7274
let (tx, rx) = mpsc::channel(10);
7375
for (i, name) in PHILOSOPHERS.iter().enumerate() {
74-
let mut left_fork = Arc::clone(&forks[i]);
75-
let mut right_fork = Arc::clone(&forks[(i + 1) % PHILOSOPHERS.len()]);
76+
let mut left_chopstick = Arc::clone(&chopsticks[i]);
77+
let mut right_chopstick =
78+
Arc::clone(&chopsticks[(i + 1) % PHILOSOPHERS.len()]);
7679
if i == PHILOSOPHERS.len() - 1 {
77-
std::mem::swap(&mut left_fork, &mut right_fork);
80+
std::mem::swap(&mut left_chopstick, &mut right_chopstick);
7881
}
7982
philosophers.push(Philosopher {
8083
name: name.to_string(),
81-
left_fork,
82-
right_fork,
84+
left_chopstick,
85+
right_chopstick,
8386
thoughts: tx.clone(),
8487
});
8588
}

src/concurrency/sync-exercises/dining-philosophers.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ minutes: 20
77
The dining philosophers problem is a classic problem in concurrency:
88

99
> Five philosophers dine together at the same table. Each philosopher has their
10-
> own place at the table. There is a fork between each plate. The dish served is
11-
> a kind of spaghetti which has to be eaten with two forks. Each philosopher can
10+
> own place at the table. There is a chopstick between each plate. The dish
11+
> served is spaghetti which requires two chopsticks to eat. Each philosopher can
1212
> only alternately think and eat. Moreover, a philosopher can only eat their
13-
> spaghetti when they have both a left and right fork. Thus two forks will only
14-
> be available when their two nearest neighbors are thinking, not eating. After
15-
> an individual philosopher finishes eating, they will put down both forks.
13+
> spaghetti when they have both a left and right chopstick. Thus two chopsticks
14+
> will only be available when their two nearest neighbors are thinking, not
15+
> eating. After an individual philosopher finishes eating, they will put down
16+
> both chopsticks.
1617
1718
You will need a local [Cargo installation](../../cargo/running-locally.md) for
1819
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:
2223

2324
```rust,compile_fail
2425
{{#include dining-philosophers.rs:Philosopher}}
25-
// left_fork: ...
26-
// right_fork: ...
26+
// left_chopstick: ...
27+
// right_chopstick: ...
2728
// thoughts: ...
2829
}
2930
3031
{{#include dining-philosophers.rs:Philosopher-think}}
3132
3233
{{#include dining-philosophers.rs:Philosopher-eat}}
33-
// Pick up forks...
34+
// Pick up chopsticks...
3435
{{#include dining-philosophers.rs:Philosopher-eat-end}}
35-
// Create forks
36+
// Create chopsticks
3637
3738
// Create philosophers
3839

src/concurrency/sync-exercises/dining-philosophers.rs

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ use std::sync::{mpsc, Arc, Mutex};
1818
use std::thread;
1919
use std::time::Duration;
2020

21-
struct Fork;
21+
struct Chopstick;
2222

2323
struct Philosopher {
2424
name: String,
2525
// ANCHOR_END: Philosopher
26-
left_fork: Arc<Mutex<Fork>>,
27-
right_fork: Arc<Mutex<Fork>>,
26+
left_chopstick: Arc<Mutex<Chopstick>>,
27+
right_chopstick: Arc<Mutex<Chopstick>>,
2828
thoughts: mpsc::SyncSender<String>,
2929
}
3030

@@ -41,8 +41,8 @@ impl Philosopher {
4141
fn eat(&self) {
4242
// ANCHOR_END: Philosopher-eat
4343
println!("{} is trying to eat", &self.name);
44-
let _left = self.left_fork.lock().unwrap();
45-
let _right = self.right_fork.lock().unwrap();
44+
let _left = self.left_chopstick.lock().unwrap();
45+
let _right = self.right_chopstick.lock().unwrap();
4646

4747
// ANCHOR: Philosopher-eat-end
4848
println!("{} is eating...", &self.name);
@@ -57,27 +57,29 @@ fn main() {
5757
// ANCHOR_END: Philosopher-eat-end
5858
let (tx, rx) = mpsc::sync_channel(10);
5959

60-
let forks = (0..PHILOSOPHERS.len())
61-
.map(|_| Arc::new(Mutex::new(Fork)))
60+
let chopsticks = PHILOSOPHERS
61+
.iter()
62+
.map(|_| Arc::new(Mutex::new(Chopstick)))
6263
.collect::<Vec<_>>();
6364

64-
for i in 0..forks.len() {
65+
for i in 0..chopsticks.len() {
6566
let tx = tx.clone();
66-
let mut left_fork = Arc::clone(&forks[i]);
67-
let mut right_fork = Arc::clone(&forks[(i + 1) % forks.len()]);
67+
let mut left_chopstick = Arc::clone(&chopsticks[i]);
68+
let mut right_chopstick =
69+
Arc::clone(&chopsticks[(i + 1) % chopsticks.len()]);
6870

6971
// To avoid a deadlock, we have to break the symmetry
70-
// somewhere. This will swap the forks without deinitializing
72+
// somewhere. This will swap the chopsticks without deinitializing
7173
// either of them.
72-
if i == forks.len() - 1 {
73-
std::mem::swap(&mut left_fork, &mut right_fork);
74+
if i == chopsticks.len() - 1 {
75+
std::mem::swap(&mut left_chopstick, &mut right_chopstick);
7476
}
7577

7678
let philosopher = Philosopher {
7779
name: PHILOSOPHERS[i].to_string(),
7880
thoughts: tx,
79-
left_fork,
80-
right_fork,
81+
left_chopstick,
82+
right_chopstick,
8183
};
8284

8385
thread::spawn(move || {

0 commit comments

Comments
 (0)