* reenabled SubengineController tests * fixed WinitWindow drop function - disabled all tests involving winit
141 lines
4.4 KiB
Rust
141 lines
4.4 KiB
Rust
#[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<SubengineCommand>,
|
|
rx: Receiver<SubengineResponse>,
|
|
handle: ManuallyDrop<JoinHandle<()>>,
|
|
}
|
|
|
|
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<E: 'static>(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();
|
|
}
|
|
|
|
}
|