use std::mem;
use std::sync::{Arc, Mutex};
use rotor::{Machine, EventSet, Scope, Response};
use rotor::{Void};
pub struct Mutexed<M>(pub Arc<Mutex<M>>);
pub trait Replaceable: Machine {
fn empty(&self) -> Self;
fn restart(self, _scope: &mut Scope<Self::Context>)
-> Response<Self, Self::Seed>
{
panic!("State machine has been poisoned");
}
}
#[inline]
fn locked_call<M, F>(scope: &mut Scope<M::Context>, me: Mutexed<M>,
fun: F)
-> Response<Mutexed<M>, M::Seed>
where M: Replaceable,
F: FnOnce(M, &mut Scope<M::Context>) -> Response<M, M::Seed>
{
let fake_result = match me.0.lock() {
Ok(mut guard) => {
let empty = guard.empty();
let fsm = mem::replace(&mut *guard, empty);
let res = fun(fsm, scope);
res.wrap(|new_machine| {
mem::replace(&mut *guard, new_machine);
()
})
}
Err(poisoned) => {
let mut guard = poisoned.into_inner();
let empty = guard.empty();
let fsm = mem::replace(&mut *guard, empty);
let res = fsm.restart(scope);
res.wrap(|new_machine| {
mem::replace(&mut *guard, new_machine);
()
})
}
};
fake_result.wrap(|()| me)
}
impl<M: Replaceable> Machine for Mutexed<M> {
type Context = M::Context;
type Seed = M::Seed;
fn create(seed: Self::Seed, scope: &mut Scope<M::Context>)
-> Response<Self, Void>
{
M::create(seed, scope).wrap(Mutex::new).wrap(Arc::new).wrap(Mutexed)
}
fn ready(self, events: EventSet, scope: &mut Scope<M::Context>)
-> Response<Self, Self::Seed>
{
locked_call(scope, self, |fsm, scope| fsm.ready(events, scope))
}
fn spawned(self, scope: &mut Scope<M::Context>)
-> Response<Self, Self::Seed>
{
locked_call(scope, self, |fsm, scope| fsm.spawned(scope))
}
fn timeout(self, scope: &mut Scope<M::Context>)
-> Response<Self, Self::Seed>
{
locked_call(scope, self, |fsm, scope| fsm.timeout(scope))
}
fn wakeup(self, scope: &mut Scope<M::Context>)
-> Response<Self, Self::Seed>
{
locked_call(scope, self, |fsm, scope| fsm.wakeup(scope))
}
}