Skip to content

Commit 7f6deda

Browse files
committed
Rephrase dining philosophers problem
The dining philosophers problem setup is quoted from C. A. R. Hoare's "Communicating Sequential Processes." This is not the paper in which the problem originated, nor does the example use the solution from Hoare's paper. Furthermore, the quoted example uses forks and pasta, and it doesn't make much sense to need two forks to eat; the formulation of this problem that I am most familiar with uses chopsticks, which makes much more sense. In addition, the quoted example uses male pronouns, the rest of the article uses singular "they", and refers to the philosophers with just numbers at first, but then goes on to use five Western European male philosophers. Since the particular quotation is not essential, the use of forks is not particularly sensible, and to increase the diversity of philosophers, simply rewrite the statement of the problem with original text. Use the appropriate pronouns for the philosophers referred to, but continue to use singular "they" when talking about an unspecified philosopher.
1 parent f6b446f commit 7f6deda

File tree

1 file changed

+112
-117
lines changed

1 file changed

+112
-117
lines changed

src/doc/trpl/dining-philosophers.md

+112-117
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,40 @@
22

33
For our second project, let’s look at a classic concurrency problem. It’s
44
called ‘the dining philosophers’. It was originally conceived by Dijkstra in
5-
1965, but we’ll use the version from [this paper][paper] by Tony Hoare in 1985.
6-
7-
[paper]: http://www.usingcsp.com/cspbook.pdf
8-
9-
> In ancient times, a wealthy philanthropist endowed a College to accommodate
10-
> five eminent philosophers. Each philosopher had a room in which she could
11-
> engage in her professional activity of thinking; there was also a common
12-
> dining room, furnished with a circular table, surrounded by five chairs, each
13-
> labelled by the name of the philosopher who was to sit in it. They sat
14-
> anticlockwise around the table. To the left of each philosopher there was
15-
> laid a golden fork, and in the centre stood a large bowl of spaghetti, which
16-
> was constantly replenished. A philosopher was expected to spend most of her
17-
> time thinking; but when she felt hungry, she went to the dining room, sat down
18-
> in her own chair, picked up her own fork on her left, and plunged it into the
19-
> spaghetti. But such is the tangled nature of spaghetti that a second fork is
20-
> required to carry it to the mouth. The philosopher therefore had also to pick
21-
> up the fork on her right. When she was finished she would put down both her
22-
> forks, get up from her chair, and continue thinking. Of course, a fork can be
23-
> used by only one philosopher at a time. If the other philosopher wants it, she
24-
> just has to wait until the fork is available again.
5+
1965, and has been used as an example of the use of concurrency primitives in
6+
many influential works since then.
7+
8+
Plato, Hypatia, Lao, Simone, and Audre are professors of philosophy at a
9+
university. As philosophers, they spend most of their time in their offices
10+
thinking and writing; but occasionally, they need to come out for food. There
11+
is a round table in the common room between their offices, and they each have
12+
a designated seat at it; the university ensures that there is always a fresh
13+
bowl of rice at each seat, so that they may come out and dine whenever they
14+
please. However, due to budgetary constraints, the university has only been
15+
able to afford five chopsticks for the five professors; in order to ensure
16+
that everyone can eat, the chopsticks are arranged between the places at the
17+
table, so each professor can pick up the two adjacent chopstics and begin
18+
eating. Whean each is done eating, they put down their chopsticks in the
19+
original locations, and return to their studies.
2520

2621
This classic problem shows off a few different elements of concurrency. The
2722
reason is that it's actually slightly tricky to implement: a simple
2823
implementation can deadlock. For example, let's consider a simple algorithm
2924
that would solve this problem:
3025

31-
1. A philosopher picks up the fork on their left.
32-
2. They then pick up the fork on their right.
26+
1. A philosopher picks up the chopstick on their left.
27+
2. They then pick up the chopstick on their right.
3328
3. They eat.
34-
4. They return the forks.
29+
4. They return the chopsticks.
3530

3631
Now, let’s imagine this sequence of events:
3732

38-
1. Philosopher 1 begins the algorithm, picking up the fork on their left.
39-
2. Philosopher 2 begins the algorithm, picking up the fork on their left.
40-
3. Philosopher 3 begins the algorithm, picking up the fork on their left.
41-
4. Philosopher 4 begins the algorithm, picking up the fork on their left.
42-
5. Philosopher 5 begins the algorithm, picking up the fork on their left.
43-
6. ... ? All the forks are taken, but nobody can eat!
33+
1. Plato begins the algorithm, picking up the chopstick on his left.
34+
2. Hypatia begins the algorithm, picking up the chopstick on her left.
35+
3. Lao begins the algorithm, picking up the chopstick on his left.
36+
4. Simone begins the algorithm, picking up the chopstick on her left.
37+
5. Audre begins the algorithm, picking up the chopstick on her left.
38+
6. ... ? All the chopsticks are taken, but nobody can eat!
4439

4540
There are different ways to solve this problem. We’ll get to our solution in
4641
the tutorial itself. For now, let’s get started modelling the problem itself.
@@ -60,11 +55,11 @@ impl Philosopher {
6055
}
6156

6257
fn main() {
63-
let p1 = Philosopher::new("Baruch Spinoza");
64-
let p2 = Philosopher::new("Gilles Deleuze");
65-
let p3 = Philosopher::new("Karl Marx");
66-
let p4 = Philosopher::new("Friedrich Nietzsche");
67-
let p5 = Philosopher::new("Michel Foucault");
58+
let p1 = Philosopher::new("Plato");
59+
let p2 = Philosopher::new("Hypatia");
60+
let p3 = Philosopher::new("Lao");
61+
let p4 = Philosopher::new("Simone");
62+
let p5 = Philosopher::new("Audre");
6863
}
6964
```
7065

@@ -159,11 +154,11 @@ look at `main()` again:
159154
# }
160155
#
161156
fn main() {
162-
let p1 = Philosopher::new("Baruch Spinoza");
163-
let p2 = Philosopher::new("Gilles Deleuze");
164-
let p3 = Philosopher::new("Karl Marx");
165-
let p4 = Philosopher::new("Friedrich Nietzsche");
166-
let p5 = Philosopher::new("Michel Foucault");
157+
let p1 = Philosopher::new("Plato");
158+
let p2 = Philosopher::new("Hypatia");
159+
let p3 = Philosopher::new("Lao");
160+
let p4 = Philosopher::new("Simone");
161+
let p5 = Philosopher::new("Audre");
167162
}
168163
```
169164

@@ -176,11 +171,11 @@ that `new()` function, it would look like this:
176171
# name: String,
177172
# }
178173
fn main() {
179-
let p1 = Philosopher { name: "Baruch Spinoza".to_string() };
180-
let p2 = Philosopher { name: "Gilles Deleuze".to_string() };
181-
let p3 = Philosopher { name: "Karl Marx".to_string() };
174+
let p1 = Philosopher { name: "Plato".to_string() };
175+
let p2 = Philosopher { name: "Hypatia".to_string() };
176+
let p3 = Philosopher { name: "Lao".to_string() };
182177
let p4 = Philosopher { name: "Friedrich Nietzche".to_string() };
183-
let p5 = Philosopher { name: "Michel Foucault".to_string() };
178+
let p5 = Philosopher { name: "Audre".to_string() };
184179
}
185180
```
186181

@@ -211,11 +206,11 @@ impl Philosopher {
211206

212207
fn main() {
213208
let philosophers = vec![
214-
Philosopher::new("Baruch Spinoza"),
215-
Philosopher::new("Gilles Deleuze"),
216-
Philosopher::new("Karl Marx"),
217-
Philosopher::new("Friedrich Nietzsche"),
218-
Philosopher::new("Michel Foucault"),
209+
Philosopher::new("Plato"),
210+
Philosopher::new("Hypatia"),
211+
Philosopher::new("Lao"),
212+
Philosopher::new("Simone"),
213+
Philosopher::new("Audre"),
219214
];
220215

221216
for p in &philosophers {
@@ -247,11 +242,11 @@ mention they’re done eating. Running this program should give you the followin
247242
output:
248243

249244
```text
250-
Baruch Spinoza is done eating.
251-
Gilles Deleuze is done eating.
252-
Karl Marx is done eating.
253-
Friedrich Nietzsche is done eating.
254-
Michel Foucault is done eating.
245+
Plato is done eating.
246+
Hypatia is done eating.
247+
Lao is done eating.
248+
Simone is done eating.
249+
Audre is done eating.
255250
```
256251

257252
Easy enough, they’re all done! We haven’t actually implemented the real problem
@@ -285,11 +280,11 @@ impl Philosopher {
285280

286281
fn main() {
287282
let philosophers = vec![
288-
Philosopher::new("Baruch Spinoza"),
289-
Philosopher::new("Gilles Deleuze"),
290-
Philosopher::new("Karl Marx"),
291-
Philosopher::new("Friedrich Nietzsche"),
292-
Philosopher::new("Michel Foucault"),
283+
Philosopher::new("Plato"),
284+
Philosopher::new("Hypatia"),
285+
Philosopher::new("Lao"),
286+
Philosopher::new("Simone"),
287+
Philosopher::new("Audre"),
293288
];
294289

295290
for p in &philosophers {
@@ -323,16 +318,16 @@ simulate the time it takes a philosopher to eat.
323318
If you run this program, you should see each philosopher eat in turn:
324319

325320
```text
326-
Baruch Spinoza is eating.
327-
Baruch Spinoza is done eating.
328-
Gilles Deleuze is eating.
329-
Gilles Deleuze is done eating.
330-
Karl Marx is eating.
331-
Karl Marx is done eating.
332-
Friedrich Nietzsche is eating.
333-
Friedrich Nietzsche is done eating.
334-
Michel Foucault is eating.
335-
Michel Foucault is done eating.
321+
Plato is eating.
322+
Plato is done eating.
323+
Hypatia is eating.
324+
Hypatia is done eating.
325+
Lao is eating.
326+
Lao is done eating.
327+
Simone is eating.
328+
Simone is done eating.
329+
Audre is eating.
330+
Audre is done eating.
336331
```
337332

338333
Excellent! We’re getting there. There’s just one problem: we aren’t actually
@@ -366,11 +361,11 @@ impl Philosopher {
366361

367362
fn main() {
368363
let philosophers = vec![
369-
Philosopher::new("Baruch Spinoza"),
370-
Philosopher::new("Gilles Deleuze"),
371-
Philosopher::new("Karl Marx"),
372-
Philosopher::new("Friedrich Nietzsche"),
373-
Philosopher::new("Michel Foucault"),
364+
Philosopher::new("Plato"),
365+
Philosopher::new("Hypatia"),
366+
Philosopher::new("Lao"),
367+
Philosopher::new("Simone"),
368+
Philosopher::new("Audre"),
374369
];
375370

376371
let handles: Vec<_> = philosophers.into_iter().map(|p| {
@@ -456,33 +451,33 @@ If you run this program, you’ll see that the philosophers eat out of order!
456451
We have multi-threading!
457452

458453
```text
459-
Gilles Deleuze is eating.
460-
Gilles Deleuze is done eating.
461-
Friedrich Nietzsche is eating.
462-
Friedrich Nietzsche is done eating.
463-
Michel Foucault is eating.
464-
Baruch Spinoza is eating.
465-
Baruch Spinoza is done eating.
466-
Karl Marx is eating.
467-
Karl Marx is done eating.
468-
Michel Foucault is done eating.
454+
Hypatia is eating.
455+
Hypatia is done eating.
456+
Simone is eating.
457+
Simone is done eating.
458+
Audre is eating.
459+
Plato is eating.
460+
Plato is done eating.
461+
Lao is eating.
462+
Lao is done eating.
463+
Audre is done eating.
469464
```
470465

471-
But what about the forks? We haven’t modeled them at all yet.
466+
But what about the chopsticks? We haven’t modeled them at all yet.
472467

473468
To do that, let’s make a new `struct`:
474469

475470
```rust
476471
use std::sync::Mutex;
477472

478473
struct Table {
479-
forks: Vec<Mutex<()>>,
474+
chopsticks: Vec<Mutex<()>>,
480475
}
481476
```
482477

483478
This `Table` has a vector of `Mutex`es. A mutex is a way to control
484479
concurrency: only one thread can access the contents at once. This is exactly
485-
the property we need with our forks. We use an empty tuple, `()`, inside the
480+
the property we need with our chopsticks. We use an empty tuple, `()`, inside the
486481
mutex, since we’re not actually going to use the value, just hold onto it.
487482

488483
Let’s modify the program to use the `Table`:
@@ -507,8 +502,8 @@ impl Philosopher {
507502
}
508503

509504
fn eat(&self, table: &Table) {
510-
let _left = table.forks[self.left].lock().unwrap();
511-
let _right = table.forks[self.right].lock().unwrap();
505+
let _left = table.chopsticks[self.left].lock().unwrap();
506+
let _right = table.chopsticks[self.right].lock().unwrap();
512507

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

@@ -519,11 +514,11 @@ impl Philosopher {
519514
}
520515

521516
struct Table {
522-
forks: Vec<Mutex<()>>,
517+
chopsticks: Vec<Mutex<()>>,
523518
}
524519

525520
fn main() {
526-
let table = Arc::new(Table { forks: vec![
521+
let table = Arc::new(Table { chopsticks: vec![
527522
Mutex::new(()),
528523
Mutex::new(()),
529524
Mutex::new(()),
@@ -532,11 +527,11 @@ fn main() {
532527
]});
533528

534529
let philosophers = vec![
535-
Philosopher::new("Baruch Spinoza", 0, 1),
536-
Philosopher::new("Gilles Deleuze", 1, 2),
537-
Philosopher::new("Karl Marx", 2, 3),
538-
Philosopher::new("Friedrich Nietzsche", 3, 4),
539-
Philosopher::new("Michel Foucault", 0, 4),
530+
Philosopher::new("Plato", 0, 1),
531+
Philosopher::new("Hypatia", 1, 2),
532+
Philosopher::new("Lao", 2, 3),
533+
Philosopher::new("Simone", 3, 4),
534+
Philosopher::new("Audre", 0, 4),
540535
];
541536

542537
let handles: Vec<_> = philosophers.into_iter().map(|p| {
@@ -572,9 +567,9 @@ struct Philosopher {
572567
```
573568

574569
We need to add two more fields to our `Philosopher`. Each philosopher is going
575-
to have two forks: the one on their left, and the one on their right.
570+
to have two chopsticks: the one on their left, and the one on their right.
576571
We’ll use the `usize` type to indicate them, as it’s the type that you index
577-
vectors with. These two values will be the indexes into the `forks` our `Table`
572+
vectors with. These two values will be the indexes into the `chopsticks` our `Table`
578573
has.
579574

580575
```rust,ignore
@@ -592,8 +587,8 @@ We now need to construct those `left` and `right` values, so we add them to
592587

593588
```rust,ignore
594589
fn eat(&self, table: &Table) {
595-
let _left = table.forks[self.left].lock().unwrap();
596-
let _right = table.forks[self.right].lock().unwrap();
590+
let _left = table.chopsticks[self.left].lock().unwrap();
591+
let _right = table.chopsticks[self.right].lock().unwrap();
597592
598593
println!("{} is eating.", self.name);
599594
@@ -604,8 +599,8 @@ fn eat(&self, table: &Table) {
604599
```
605600

606601
We have two new lines. We’ve also added an argument, `table`. We access the
607-
`Table`’s list of forks, and then use `self.left` and `self.right` to access
608-
the fork at that particular index. That gives us access to the `Mutex` at that
602+
`Table`’s list of chopsticks, and then use `self.left` and `self.right` to access
603+
the chopstick at that particular index. That gives us access to the `Mutex` at that
609604
index, and we call `lock()` on it. If the mutex is currently being accessed by
610605
someone else, we’ll block until it becomes available.
611606

@@ -626,7 +621,7 @@ What about releasing the lock? Well, that will happen when `_left` and
626621
`_right` go out of scope, automatically.
627622

628623
```rust,ignore
629-
let table = Arc::new(Table { forks: vec![
624+
let table = Arc::new(Table { chopsticks: vec![
630625
Mutex::new(()),
631626
Mutex::new(()),
632627
Mutex::new(()),
@@ -643,11 +638,11 @@ count will go up, and when each thread ends, it will go back down.
643638

644639
```rust,ignore
645640
let philosophers = vec![
646-
Philosopher::new("Baruch Spinoza", 0, 1),
647-
Philosopher::new("Gilles Deleuze", 1, 2),
648-
Philosopher::new("Karl Marx", 2, 3),
649-
Philosopher::new("Friedrich Nietzsche", 3, 4),
650-
Philosopher::new("Michel Foucault", 0, 4),
641+
Philosopher::new("Plato", 0, 1),
642+
Philosopher::new("Hypatia", 1, 2),
643+
Philosopher::new("Lao", 2, 3),
644+
Philosopher::new("Simone", 3, 4),
645+
Philosopher::new("Audre", 0, 4),
651646
];
652647
```
653648

@@ -678,16 +673,16 @@ With this, our program works! Only two philosophers can eat at any one time,
678673
and so you’ll get some output like this:
679674

680675
```text
681-
Gilles Deleuze is eating.
682-
Friedrich Nietzsche is eating.
683-
Friedrich Nietzsche is done eating.
684-
Gilles Deleuze is done eating.
685-
Baruch Spinoza is eating.
686-
Karl Marx is eating.
687-
Baruch Spinoza is done eating.
688-
Michel Foucault is eating.
689-
Karl Marx is done eating.
690-
Michel Foucault is done eating.
676+
Hypatia is eating.
677+
Simone is eating.
678+
Simone is done eating.
679+
Hypatia is done eating.
680+
Plato is eating.
681+
Lao is eating.
682+
Plato is done eating.
683+
Audre is eating.
684+
Lao is done eating.
685+
Audre is done eating.
691686
```
692687

693688
Congrats! You’ve implemented a classic concurrency problem in Rust.

0 commit comments

Comments
 (0)