1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-05-16 23:55:42 +02:00
scubed2 32a8b4bf13
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 <scubed2+git@gmail.com>
2025-02-24 16:13:16 +00:00

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}");
}
}