#[allow(unused_imports)] use log::{debug, error, info, trace, warn}; use std::{ sync::mpsc::{Sender, Receiver}, thread::JoinHandle, mem::ManuallyDrop, ptr::read, time::Duration, }; use crate::subengine::Subengine; //--SubengineController Implementation-------------------------------------------------------------- /// Handle to manage an [`Subengine`]. /// /// The `SubengineController` runs the [`Engine`] in a separated thread with some control code to /// allow control from the main thread. SubengineControllers are used by the [`Controller`] to /// interact with Subengines. #[derive(Debug)] pub struct SubengineController { tx: Sender, rx: Receiver, handle: ManuallyDrop>, } impl Drop for SubengineController { fn drop(&mut self) { self.exec(SubengineCommand::Stop); unsafe { let _ = ManuallyDrop::into_inner(read(&mut self.handle)).join(); } debug!("SubengineController dropped !"); } } impl SubengineController { /// Creates a new `SubengineController` from a given [`Engine`]. Since the latter will be moved /// to a separated thread, it must implement the [`Send`] trait. pub fn new(engine: E) -> SubengineController where E: Subengine + std::marker::Send, { use std::{ thread, sync::mpsc::channel, }; debug!("Creating SubengineController..."); let (tx, e_rx) = channel(); let (e_tx, rx) = channel(); let handle = thread::spawn(move || { trace!("Subengine thread started !"); loop { //TODO manage errors match e_rx.recv().unwrap() { SubengineCommand::Run => engine.run(), SubengineCommand::Stop => return, }; e_tx.send(SubengineResponse::Done).unwrap(); } }); SubengineController { tx, rx, handle: ManuallyDrop::new(handle), } } /// Sends a command to the [`Subengine`]. This is function is not blocking and WILL NOT check if /// the command was received. pub fn exec(&self, cmd: SubengineCommand) { self.tx.send(cmd).unwrap(); } /// Blocking function, waits for the [`Subengine`] to finish executing the last command given. pub fn wait_for_exec(&self, timeout: Duration) -> Result<(),SubengineWaitError> { match self.rx.recv_timeout(timeout) { Err(_) => Err(SubengineWaitError::NoResponse), Ok(val) => match val { SubengineResponse::Done => Ok(()), //_ => Err(SubengineWaitError::BadResponse), }} } } //--SubengineCommand enum--------------------------------------------------------------------------- /// Commands that can be sent to an [`Subengine`] via an `EngineController`. #[derive(Debug)] pub enum SubengineCommand { Run, Stop, } //--SubengineWaitError enum------------------------------------------------------------------------- /// Errors that can be returned by the wait_for_exec function. #[derive(Debug)] pub enum SubengineWaitError { NoResponse, BadResponse, } //--SubengineResponse enum-------------------------------------------------------------------------- #[derive(Debug)] enum SubengineResponse { Done, } //--Tests------------------------------------------------------------------------------------------- #[cfg(test)] mod tests { use super::*; use crate::subengine::TestSubengine; #[test] fn test_new_drop() { let (test_subengine, _test_rx) = TestSubengine::new("run"); let _subengine_controller = SubengineController::new(test_subengine); } #[test] fn test_exec() { let (test_subengine, test_rx) = TestSubengine::new("run"); let subengine_controller = SubengineController::new(test_subengine); subengine_controller.exec(SubengineCommand::Run); let response = test_rx.recv_timeout(Duration::from_millis(10)).unwrap(); assert_eq!(response, "run"); } #[test] fn test_wait_for_exec() { let (test_subengine, _test_rx) = TestSubengine::new("run"); let subengine_controller = SubengineController::new(test_subengine); subengine_controller.exec(SubengineCommand::Run); subengine_controller.wait_for_exec(Duration::from_millis(10)).unwrap(); } }