mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-05-16 23:55:42 +02:00
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>
108 lines
3.2 KiB
Rust
108 lines
3.2 KiB
Rust
// Copyright 2023 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// ANCHOR: solution
|
|
// ANCHOR: Philosopher
|
|
use std::sync::Arc;
|
|
use tokio::sync::{mpsc, Mutex};
|
|
use tokio::time;
|
|
|
|
struct Chopstick;
|
|
|
|
struct Philosopher {
|
|
name: String,
|
|
// ANCHOR_END: Philosopher
|
|
left_chopstick: Arc<Mutex<Chopstick>>,
|
|
right_chopstick: Arc<Mutex<Chopstick>>,
|
|
thoughts: mpsc::Sender<String>,
|
|
}
|
|
|
|
// ANCHOR: Philosopher-think
|
|
impl Philosopher {
|
|
async fn think(&self) {
|
|
self.thoughts
|
|
.send(format!("Eureka! {} has a new idea!", &self.name))
|
|
.await
|
|
.unwrap();
|
|
}
|
|
// ANCHOR_END: Philosopher-think
|
|
|
|
// ANCHOR: Philosopher-eat
|
|
async fn eat(&self) {
|
|
// Keep trying until we have both chopsticks
|
|
// ANCHOR_END: Philosopher-eat
|
|
// Pick up chopsticks...
|
|
let _left_chopstick = self.left_chopstick.lock().await;
|
|
let _right_chopstick = self.right_chopstick.lock().await;
|
|
|
|
// ANCHOR: Philosopher-eat-body
|
|
println!("{} is eating...", &self.name);
|
|
time::sleep(time::Duration::from_millis(5)).await;
|
|
// ANCHOR_END: Philosopher-eat-body
|
|
|
|
// The locks are dropped here
|
|
// ANCHOR: Philosopher-eat-end
|
|
}
|
|
}
|
|
|
|
// tokio scheduler doesn't deadlock with 5 philosophers, so have 2.
|
|
static PHILOSOPHERS: &[&str] = &["Socrates", "Hypatia"];
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
// ANCHOR_END: Philosopher-eat-end
|
|
// Create chopsticks
|
|
let mut chopsticks = vec![];
|
|
PHILOSOPHERS
|
|
.iter()
|
|
.for_each(|_| chopsticks.push(Arc::new(Mutex::new(Chopstick))));
|
|
|
|
// Create philosophers
|
|
let (philosophers, mut rx) = {
|
|
let mut philosophers = vec![];
|
|
let (tx, rx) = mpsc::channel(10);
|
|
for (i, name) in PHILOSOPHERS.iter().enumerate() {
|
|
let mut left_chopstick = Arc::clone(&chopsticks[i]);
|
|
let mut right_chopstick =
|
|
Arc::clone(&chopsticks[(i + 1) % PHILOSOPHERS.len()]);
|
|
if i == PHILOSOPHERS.len() - 1 {
|
|
std::mem::swap(&mut left_chopstick, &mut right_chopstick);
|
|
}
|
|
philosophers.push(Philosopher {
|
|
name: name.to_string(),
|
|
left_chopstick,
|
|
right_chopstick,
|
|
thoughts: tx.clone(),
|
|
});
|
|
}
|
|
(philosophers, rx)
|
|
// tx is dropped here, so we don't need to explicitly drop it later
|
|
};
|
|
|
|
// Make them think and eat
|
|
for phil in philosophers {
|
|
tokio::spawn(async move {
|
|
for _ in 0..100 {
|
|
phil.think().await;
|
|
phil.eat().await;
|
|
}
|
|
});
|
|
}
|
|
|
|
// Output their thoughts
|
|
while let Some(thought) = rx.recv().await {
|
|
println!("Here is a thought: {thought}");
|
|
}
|
|
}
|