1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-07-04 05:40:29 +02:00

Actually protect against deadlock in Dining Philosophers async exercise ()

Swapping forks for one philosopher feels like cheating. It's like one of
the philosophers is facing away from the table. Or perhaps it's the only
right-handed philosopher at the table.

More importantly, there is no effective mechanism to prevent deadlocks.
Add that mechanism, it's useful for learning Rust.

The new code demonstrates let-else, drop and returning values from a
loop. `std::mem::swap` remains in the thread version of the Dining
Philosophers exercise for now.

This also fixes compilation. `left_fork` and `right_fork` had to be
`mut` in `main()` for the workaround to compile.
This commit is contained in:
Pavel Roskin
2024-01-30 17:00:19 -08:00
committed by GitHub
parent 88dd268b01
commit fbc6be4e1c

@ -41,13 +41,28 @@ impl Philosopher {
// ANCHOR: Philosopher-eat
async fn eat(&self) {
// Pick up forks...
// Keep trying until we have both forks
// ANCHOR_END: Philosopher-eat
let _first_lock = self.left_fork.lock().await;
// Add a delay before picking the second fork to allow the execution
// to transfer to another task
time::sleep(time::Duration::from_millis(1)).await;
let _second_lock = self.right_fork.lock().await;
let (_left_fork, _right_fork) = loop {
// Pick up forks...
let left_fork = self.left_fork.try_lock();
let right_fork = self.right_fork.try_lock();
let Ok(left_fork) = left_fork else {
// If we didn't get the left fork, drop the right fork if we
// have it and let other tasks make progress.
drop(right_fork);
time::sleep(time::Duration::from_millis(1)).await;
continue;
};
let Ok(right_fork) = right_fork else {
// If we didn't get the right fork, drop the left fork and let
// other tasks make progress.
drop(left_fork);
time::sleep(time::Duration::from_millis(1)).await;
continue;
};
break (left_fork, right_fork);
};
// ANCHOR: Philosopher-eat-body
println!("{} is eating...", &self.name);
@ -76,12 +91,6 @@ async fn main() {
for (i, name) in PHILOSOPHERS.iter().enumerate() {
let left_fork = Arc::clone(&forks[i]);
let right_fork = Arc::clone(&forks[(i + 1) % PHILOSOPHERS.len()]);
// To avoid a deadlock, we have to break the symmetry
// somewhere. This will swap the forks without deinitializing
// either of them.
if i == 0 {
std::mem::swap(&mut left_fork, &mut right_fork);
}
philosophers.push(Philosopher {
name: name.to_string(),
left_fork,