Solución de Chandy/Misra
Para cada par de filósofos que compiten por un palito, se crea un palito, asignado al filósofo con el ID más bajo (con esto se evita el deadlock).
Cada palito puede estar sucio o limpio. Inicialmente, todos los palitos están sucios.
Cuando un filósofo quiere usar un conjunto de recursos (es decir, comer), dicho filósofo debe obtener los palitos de sus vecinos contendientes.
Para todos los palitos que el filósofo no tiene, debe enviar un mensaje solicitándolo a su vecino.
Cuando un filósofo con un palito recibe un mensaje de solicitud, conserva el palito si está limpio, dejando al vecino en espera; pero lo entrega si está sucio, "limpiandolo" antes de hacerlo.
Después de que un filósofo haya terminado de comer, todos sus palitos quedan sucios. Si otro filósofo había solicitado previamente uno de los palitos, el filósofo que acaba de terminar de comer limpia el palito y lo envía.
extern crate actix;
use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::time::Duration;
use actix::{Actor, ActorFutureExt, Addr, AsyncContext, Context, Handler, Message, Recipient, ResponseActFuture, System, WrapFuture};
use actix::clock::sleep;
use actix::dev::fut::future::Map;
use actix::fut::result;
use rand::{Rng, thread_rng};
const N: usize = 5;
type Neighbours = HashMap<ChopstickId, Addr<Philosopher>>;
#[derive(Message)]
#[rtype(result = "()")]
struct SetNeighbours(Neighbours);
#[derive(Message)]
#[rtype(result = "()")]
struct Hungry;
#[derive(Message)]
#[rtype(result = "()")]
struct ChopstickRequest(ChopstickId);
#[derive(Message)]
#[rtype(result = "()")]
struct ChopstickResponse(ChopstickId);
#[derive(Message)]
#[rtype(result = "()")]
struct EatingDone;
#[derive(PartialEq)]
enum ChopstickState {
DontHave,
Dirty,
Clean,
Requested
}
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
struct ChopstickId(usize);
struct Philosopher {
id: usize,
chopsticks: HashMap<ChopstickId, ChopstickState>,
neighbours: Neighbours
}
impl Philosopher {
fn sleep<WakeupMessage>(&self, msg: WakeupMessage) -> ResponseActFuture<Self, ()>
where
Self: Handler<WakeupMessage>,
WakeupMessage: Message + Send + 'static,
WakeupMessage::Result: Send,
{
println!("[{}] pensando", self.id);
Box::pin(sleep(Duration::from_millis(thread_rng().gen_range(2000, 5000)))
.into_actor(self)
.map(move |_result, _me, ctx| {
ctx.address().try_send(msg).unwrap();
}))
}
fn eat_if_ready(&self) -> ResponseActFuture<Self, ()> {
if self.chopsticks.iter().all(|(_id, state)| *state != ChopstickState::DontHave) { // si los tengo todos
println!("[{}] comiendo", self.id);
self.sleep(EatingDone)
} else {
println!("[{}] aun no puedo comer", self.id);
Box::pin(std::future::ready(()).into_actor(self))
}
}
}
impl Actor for Philosopher {
type Context = Context<Self>;
}
impl Handler<SetNeighbours> for Philosopher {
type Result = ResponseActFuture<Self, ()>;
fn handle(&mut self, msg: SetNeighbours, _ctx: &mut Context<Self>) -> Self::Result {
println!("[{}] recibi a mis vecinos", self.id);
self.neighbours = msg.0;
self.sleep(Hungry)
}
}
impl Handler<Hungry> for Philosopher {
type Result = ResponseActFuture<Self, ()>;
fn handle(&mut self, _msg: Hungry, _ctx: &mut Context<Self>) -> Self::Result {
println!("[{}] por comer", self.id);
for (chopstick_id, state) in self.chopsticks.iter() {
if *state == ChopstickState::DontHave {
println!("[{}] pido palito {}", self.id, chopstick_id.0);
self.neighbours.get(chopstick_id).unwrap().try_send(ChopstickRequest(*chopstick_id)).unwrap();
}
}
self.eat_if_ready()
}
}
impl Handler<ChopstickRequest> for Philosopher {
type Result = ();
fn handle(&mut self, msg: ChopstickRequest, _ctx: &mut Context<Self>) -> Self::Result {
println!("[{}] me piden palito {}", self.id, msg.0.0);
let chopstick = msg.0;
let chopstick_state = &self.chopsticks.get(&chopstick);
match chopstick_state {
Some(ChopstickState::Dirty) => {
println!("[{}] se lo doy ahora", self.id);
self.neighbours.get(&chopstick).unwrap().try_send(ChopstickResponse(msg.0));
self.chopsticks.insert(chopstick, ChopstickState::DontHave);
},
Some(ChopstickState::Clean) => {
println!("[{}] se lo doy cuando termine", self.id);
self.chopsticks.insert(chopstick, ChopstickState::Requested);
},
_ => {
println!("[{}] no deberia pasar", self.id);
}
}
}
}
impl Handler<ChopstickResponse> for Philosopher {
type Result = ResponseActFuture<Self, ()>;
fn handle(&mut self, msg: ChopstickResponse, _ctx: &mut Context<Self>) -> Self::Result {
println!("[{}] recibi palito {}", self.id, msg.0.0);
self.chopsticks.insert(msg.0,ChopstickState::Clean);
self.eat_if_ready()
}
}
impl Handler<EatingDone> for Philosopher {
type Result = ResponseActFuture<Self, ()>;
fn handle(&mut self, _msg: EatingDone, _ctx: &mut Context<Self>) -> Self::Result {
println!("[{}] terminé de comer", self.id);
for (chopstick, mut state) in self.chopsticks.iter_mut() {
if *state == ChopstickState::Requested {
println!("[{}] entrego palito {}", self.id, chopstick.0);
self.neighbours.get(chopstick).unwrap().try_send(ChopstickResponse(*chopstick)).unwrap();
*state = ChopstickState::DontHave
} else {
println!("[{}] marco como sucio palito {}", self.id, chopstick.0);
*state = ChopstickState::Dirty
}
}
self.sleep(Hungry)
}
}
fn main() {
let system = System::new();
system.block_on(async {
let mut philosophers = vec!();
for id in 0..N {
// Deadlock avoidance forcing the initial state
philosophers.push(Philosopher {
id,
chopsticks: HashMap::from([
(ChopstickId(id), if id == 0 { ChopstickState::Dirty } else { ChopstickState::DontHave }),
(ChopstickId((id + 1) % N), if id == N-1 { ChopstickState::DontHave } else { ChopstickState::Dirty })
]),
neighbours: HashMap::with_capacity(2)
}.start())
}
for id in 0..N {
let prev = if id == 0 { N - 1 } else { id - 1 };
let next = (id + 1) % N;
philosophers[id].try_send(SetNeighbours(HashMap::from([
(ChopstickId(id), philosophers[prev].clone()),
(ChopstickId(next), philosophers[next].clone())
]))).unwrap();
}
});
system.run().unwrap();
}