Last active
February 23, 2024 20:43
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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."); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.