use crossbeam_channel::Sender;
use mio::Waker;
use std::io;
use std::io::ErrorKind;
use std::sync::Arc;
use crate::wire;
/// A command which may be sent to the [`super::ReactionHandler`] from outside of the [`super::Reactor`],
/// including other threads.
///
/// The handler object is owned by the reactor runtime and executes always in the context of the
/// reactor runtime thread. Thus, if other (micro)services within the app needs to communicate
/// to the handler they have to use this data type, which usually is an enumeration for a set of
/// commands supported by the handler.
pub enum ControlMessage {
Command(Box<wire::Control>),
Shutdown,
}
/// Used by the [`crate::reactor::Reactor`] to inform the
/// [`crate::reactor::ReactionHandler`] about
/// incoming commands, sent via this [`Controller`].
#[derive(Clone)]
pub struct Controller {
sender: Sender<ControlMessage>,
waker: Arc<Waker>,
}
impl Controller {
pub fn new(sender: Sender<ControlMessage>, waker: Arc<Waker>) -> Self {
Self { sender, waker }
}
pub fn wake(&self) -> io::Result<()> {
log::trace!(target: "reactor::controller", "Wakening the reactor");
self.waker.wake()
}
pub fn cmd(&self, command: wire::Control) -> io::Result<()> {
log::trace!(target: "reactor::controller", "Sending command {command:?} to the reactor");
self.sender
.send(ControlMessage::Command(Box::new(command)))
.map_err(|_| ErrorKind::BrokenPipe)?;
self.wake()
}
pub fn shutdown(self) -> Result<(), Self> {
log::info!(target: "reactor::controller", "Initiating reactor shutdown...");
let res1 = self.sender.send(ControlMessage::Shutdown);
let res2 = self.wake();
res1.or(res2).map_err(|_| self)
}
}