Files
Notes/notes-service/vendor/futures-intrusive/examples/philosophers.rs
T
2026-08-01 16:11:49 +03:00

166 lines
5.9 KiB
Rust

//! The example in this file demonstrates a solution for the
//! [Dining Philosophers Problem](https://en.wikipedia.org/wiki/Dining_philosophers_problem),
//! which uses async tasks and futures_intrusive primitives in order to
//! simulate philosophers.
#![recursion_limit = "256"]
use futures::{executor::block_on, join, select};
use futures_intrusive::{
sync::LocalMutex,
timer::{StdClock, Timer, TimerService},
};
use lazy_static::lazy_static;
use pin_utils::pin_mut;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::{sleep, spawn};
use std::time::Duration;
/// We simulate the ownership of a fork through an asynchronously awaitable mutex.
/// In order to acquire a fork, the philosopher acquires the Mutex.
/// In order to release a fork, the philosopher releases the LockGuard. This
/// happens automatically, when the LockGuard goes out of scope.
/// Since all philosophers are subtasks of the same top-level `async` task,
/// a lightweight non thread-safe `LocalMutex` can be utilized.
type Fork = LocalMutex<()>;
// Some setup for the asynchronously awaitable timer
lazy_static! {
static ref STD_CLOCK: StdClock = StdClock::new();
static ref TIMER_SERVICE: TimerService = TimerService::new(&*STD_CLOCK);
static ref STOP_TIMER: AtomicBool = AtomicBool::new(false);
}
/// Returns a reference to the global timer
fn get_timer() -> &'static dyn Timer {
&*TIMER_SERVICE
}
/// Returns a random delay duration between `min` and `(min + max_extra)`
fn rand_delay(min: Duration, max_extra: Duration) -> Duration {
let extra_ms = rand::random::<u64>() % (max_extra.as_millis() as u64);
min + Duration::from_millis(extra_ms)
}
/// How often a philosopher should eat
const TO_EAT: usize = 5;
/// Simulates a single philosopher
async fn philosopher_task<'a>(
name: &'a str,
left_fork: &'a Fork,
right_fork: &'a Fork,
) {
println!("{} is ready to go", name);
let mut eaten: usize = 0;
while eaten != TO_EAT {
println!("{} is thinking", name);
get_timer()
.delay(rand_delay(
Duration::from_millis(1000),
Duration::from_millis(1000),
))
.await;
{
println!("{} is starting to pick up forks", name);
// Create futures for acquiring both forks
let get_left_fork_future = left_fork.lock();
pin_mut!(get_left_fork_future);
let get_right_fork_future = right_fork.lock();
pin_mut!(get_right_fork_future);
// This sets up a timer. If the philosopher can't obtain both forks
// during that, they put back all acquired forks and start thinking
// again.
let abort_get_forks_future =
get_timer().delay(Duration::from_millis(300));
pin_mut!(abort_get_forks_future);
select! {
_ = get_left_fork_future => {
println!("{} got the left fork and tries to get the right fork", name);
select! {
_ = get_right_fork_future => {
println!("{} got the right fork and starts eating", name);
get_timer().delay(
rand_delay(Duration::from_millis(1000),
Duration::from_millis(200))).await;
eaten += 1;
println!("{} has finished eating [ate {} times]", name, eaten);
},
_ = abort_get_forks_future => {
println!("{} could not acquire the right fork", name);
},
}
},
_ = get_right_fork_future => {
println!("{} got the right fork and tries to get the left fork", name);
select! {
_ = get_left_fork_future => {
println!("{} got the left fork and starts eating", name);
get_timer().delay(
rand_delay(Duration::from_millis(1000),
Duration::from_millis(200))).await;
eaten += 1;
println!("{} has finished eating [ate {} times]", name, eaten);
},
_ = abort_get_forks_future => {
println!("{} could not acquire the left fork", name);
},
}
},
_ = abort_get_forks_future => {
println!("{} could not acquire any fork", name);
},
}
}
}
println!("{} has finished", name);
}
async fn simulate_philosophers() {
// Create the forks for the philosophers
let forks: [Fork; 5] = [
Fork::new((), true),
Fork::new((), true),
Fork::new((), true),
Fork::new((), true),
Fork::new((), true),
];
// Create a task for each philosopher
let p1 = philosopher_task("A", &forks[4], &forks[0]);
let p2 = philosopher_task("B", &forks[0], &forks[1]);
let p3 = philosopher_task("C", &forks[1], &forks[2]);
let p4 = philosopher_task("D", &forks[2], &forks[3]);
let p5 = philosopher_task("E", &forks[3], &forks[4]);
// Wait until all philosophers have finished eating
join!(p1, p2, p3, p4, p5);
}
fn main() {
// Spawn a background thread which advances the timer
let join_handle = spawn(move || {
timer_thread();
});
// And simulate the philosophers
block_on(simulate_philosophers());
// Stop the timer thread
STOP_TIMER.store(true, Ordering::Relaxed);
join_handle.join().unwrap();
}
fn timer_thread() {
while !STOP_TIMER.load(Ordering::Relaxed) {
sleep(Duration::from_millis(25));
TIMER_SERVICE.check_expirations();
}
}