mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-06-06 17:46:16 +02:00
Actually protect against deadlock in Dining Philosophers async exercise (#1772)
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:
parent
88dd268b01
commit
fbc6be4e1c
@ -41,13 +41,28 @@ impl Philosopher {
|
|||||||
|
|
||||||
// ANCHOR: Philosopher-eat
|
// ANCHOR: Philosopher-eat
|
||||||
async fn eat(&self) {
|
async fn eat(&self) {
|
||||||
// Pick up forks...
|
// Keep trying until we have both forks
|
||||||
// ANCHOR_END: Philosopher-eat
|
// ANCHOR_END: Philosopher-eat
|
||||||
let _first_lock = self.left_fork.lock().await;
|
let (_left_fork, _right_fork) = loop {
|
||||||
// Add a delay before picking the second fork to allow the execution
|
// Pick up forks...
|
||||||
// to transfer to another task
|
let left_fork = self.left_fork.try_lock();
|
||||||
time::sleep(time::Duration::from_millis(1)).await;
|
let right_fork = self.right_fork.try_lock();
|
||||||
let _second_lock = self.right_fork.lock().await;
|
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
|
// ANCHOR: Philosopher-eat-body
|
||||||
println!("{} is eating...", &self.name);
|
println!("{} is eating...", &self.name);
|
||||||
@ -76,12 +91,6 @@ async fn main() {
|
|||||||
for (i, name) in PHILOSOPHERS.iter().enumerate() {
|
for (i, name) in PHILOSOPHERS.iter().enumerate() {
|
||||||
let left_fork = Arc::clone(&forks[i]);
|
let left_fork = Arc::clone(&forks[i]);
|
||||||
let right_fork = Arc::clone(&forks[(i + 1) % PHILOSOPHERS.len()]);
|
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 {
|
philosophers.push(Philosopher {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
left_fork,
|
left_fork,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user