Skip to content

Instantly share code, notes, and snippets.

@fancellu
Last active February 23, 2024 20:43
Show Gist options
  • Save fancellu/6300c7bcb0ae8d35039450c44f0069df to your computer and use it in GitHub Desktop.
Save fancellu/6300c7bcb0ae8d35039450c44f0069df to your computer and use it in GitHub Desktop.
Rust dining philosophers problem, to eat, each must grab 2 forks, but we have same number of forks as philosophers
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
struct Fork {
id: usize,
mutex: Mutex<()>,
}
impl Fork {
fn new(id: usize) -> Fork {
Fork {
id,
mutex: Mutex::new(()),
}
}
}
struct Philosopher {
name: String,
left: Arc<Fork>,
right: Arc<Fork>,
}
impl Philosopher {
fn new(name: String, left: Arc<Fork>, right: Arc<Fork>) -> Philosopher {
println!("{} Created", name);
Philosopher { name, left, right }
}
fn eat(&self) {
// grab lowest id fork first to avoid deadlock
let left_id = self.left.id;
let right_id = self.right.id;
let (first_fork, second_fork) = if left_id < right_id {
(self.left.clone(), self.right.clone())
} else {
(self.right.clone(), self.left.clone())
};
println!(
"{} is picking up fork {} and {}",
self.name, left_id, right_id
);
// wait for forks to be available, grab
let _left = first_fork.mutex.lock().unwrap();
let _right = second_fork.mutex.lock().unwrap();
let random_delay = rand::random::<u64>() % 400;
let sleep_for = 400 + random_delay;
println!("{} is eating for {} millis", self.name, sleep_for);
thread::sleep(Duration::from_millis(sleep_for));
println!("{} is done eating", self.name);
// forks are dropped when not needed
}
}
fn main() {
let fork_count = 5;
let forks = (0..fork_count)
.map(|id| Arc::new(Fork::new(id)))
.collect::<Vec<_>>();
// We have same number of Philosophers, but to eat, you need to have 2 forks
let philosophers = (0..fork_count)
.map(|id| {
let left = forks[id].clone();
let right = forks[(id + 1) % forks.len()].clone();
Philosopher::new(format!("Philosopher {}", id), left, right)
})
.collect::<Vec<_>>();
// time to eat
let handles = philosophers
.into_iter()
.map(|philosopher| {
thread::spawn(move || {
philosopher.eat();
})
})
.collect::<Vec<_>>();
// Wait for them to finish
for handle in handles {
handle.join().unwrap()
}
println!("All philosophers have eaten.");
}
@fancellu
Copy link
Author

Output

Philosopher 0 Created
Philosopher 1 Created
Philosopher 2 Created
Philosopher 3 Created
Philosopher 4 Created
Philosopher 0 is picking up fork 0 and 1
Philosopher 2 is picking up fork 2 and 3
Philosopher 1 is picking up fork 1 and 2
Philosopher 3 is picking up fork 3 and 4
Philosopher 4 is picking up fork 4 and 0
Philosopher 2 is eating for 603 millis
Philosopher 0 is eating for 648 millis
Philosopher 2 is done eating
Philosopher 3 is eating for 756 millis
Philosopher 0 is done eating
Philosopher 1 is eating for 524 millis
Philosopher 1 is done eating
Philosopher 3 is done eating
Philosopher 4 is eating for 762 millis
Philosopher 4 is done eating
All philosophers have eaten.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment