use std::any::Any;
use std::sync::{mpsc, Mutex};
use std::thread;
use super::super::state::StateFlag;
use super::message::{Message, Response};
use crate::error::RuntimeError;
pub struct Controller<M>
where
M: 'static + PartialEq + Eq + Send + Sync,
{
tx: Mutex<mpsc::Sender<Message<M>>>,
rx: Mutex<mpsc::Receiver<Response>>,
pub(super) t: Mutex<Option<thread::JoinHandle<Result<(), RuntimeError>>>>,
pub(super) state: StateFlag<ControllerState>,
}
impl<M> Controller<M>
where
M: 'static + PartialEq + Eq + Send + Sync,
{
#[inline]
pub(super) fn new(
tx: mpsc::Sender<Message<M>>,
rx: mpsc::Receiver<Response>,
t: thread::JoinHandle<Result<(), RuntimeError>>,
) -> Self {
Self {
tx: Mutex::new(tx),
rx: Mutex::new(rx),
t: Mutex::new(Some(t)),
state: StateFlag::default(),
}
}
#[inline]
pub fn wait(&self) -> Result<(), RuntimeError> {
match self.t.lock().unwrap().take() {
Some(t) => match t.join() {
Ok(r) => r,
Err(_) => RuntimeError::unexpected(),
},
None => RuntimeError::expect("Already stopped"),
}
}
#[inline]
pub fn stop(&self) -> Result<(), RuntimeError> {
self.state.stop()?;
self.send(Message::Stop)?;
match self.t.lock().unwrap().take().unwrap().join() {
Ok(r) => r,
Err(_) => RuntimeError::unexpected(),
}
}
#[inline]
pub fn pause(&self) -> Result<(), RuntimeError> {
self.set_state(ControllerState::Running, ControllerState::Paused)?;
self.send(Message::Pause)
}
#[inline]
pub fn resume(&self) -> Result<(), RuntimeError> {
self.set_state(ControllerState::Paused, ControllerState::Running)?;
self.send(Message::Resume)
}
#[inline]
pub fn hibernate(&self) -> Result<(), RuntimeError> {
self.set_state(ControllerState::Running, ControllerState::Hibernated)?;
self.send(Message::Hibernate)
}
#[inline]
pub fn wake_up(&self) -> Result<(), RuntimeError> {
self.set_state(ControllerState::Hibernated, ControllerState::Running)?;
self.send(Message::WakeUp)
}
#[inline]
pub fn request<R>(&self, m: M) -> Result<R, RuntimeError>
where
R: 'static,
{
self.assert_state(ControllerState::Running)?;
self.send(Message::Custom(m))
}
#[inline]
fn send<R>(&self, m: Message<M>) -> Result<R, RuntimeError>
where
R: 'static,
{
let (tx, rx) = {
let rx = self.rx.lock().unwrap();
let tx = self.tx.lock().unwrap();
(tx, rx)
};
tx.send(m)?;
let m = match (rx.recv()? as Box<dyn Any>).downcast() {
Ok(m) => Ok(*m),
Err(_) => RuntimeError::unexpected(),
};
{
drop(tx);
drop(rx);
}
m
}
#[inline]
fn assert_state(&self, state: ControllerState) -> Result<(), RuntimeError> {
match self.state.status()? == state {
true => Ok(()),
false => RuntimeError::expect("Unexpected flag"),
}
}
#[inline]
fn set_state(&self, last: ControllerState, now: ControllerState) -> Result<(), RuntimeError> {
self.assert_state(last)?;
self.state.start(now)
}
}
impl<M> Drop for Controller<M>
where
M: 'static + PartialEq + Eq + Send + Sync,
{
fn drop(&mut self) {
self.stop().unwrap();
}
}
#[derive(PartialEq, Eq)]
pub enum ControllerState {
Running,
Paused,
Hibernated,
}
impl Default for ControllerState {
#[inline]
fn default() -> Self {
Self::Running
}
}
impl From<usize> for ControllerState {
#[inline]
fn from(state: usize) -> Self {
match state {
1 => Self::Paused,
2 => Self::Hibernated,
0 | _ => Self::Running,
}
}
}
impl Into<usize> for ControllerState {
#[inline]
fn into(self) -> usize {
match self {
Self::Running => 0,
Self::Paused => 1,
Self::Hibernated => 2,
}
}
}