Threading/Executable module #1

Merged
muellerr merged 12 commits from threading into main 2022-06-09 12:50:06 +02:00
2 changed files with 175 additions and 18 deletions
Showing only changes of commit 0192a705c5 - Show all commits

View File

@ -0,0 +1,18 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Test All" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="command" value="test -- --include-ignored" />
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
<option name="channel" value="DEFAULT" />
<option name="requiredFeatures" value="true" />
<option name="allFeatures" value="false" />
<option name="emulateTerminal" value="false" />
<option name="withSudo" value="false" />
<option name="backtrace" value="SHORT" />
<envs />
<option name="isRedirectInput" value="false" />
<option name="redirectInputPath" value="" />
<method v="2">
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
</method>
</configuration>
</component>

View File

@ -26,6 +26,7 @@ pub trait Executable: Send {
} }
/// This function allows executing one task which implements the [Executable][Executable] trait /// This function allows executing one task which implements the [Executable][Executable] trait
///
/// # Arguments /// # Arguments
/// ///
/// * `executable`: Executable task /// * `executable`: Executable task
@ -74,6 +75,7 @@ pub fn exec_sched_single<
/// This function allows executing multiple tasks as long as the tasks implement the /// This function allows executing multiple tasks as long as the tasks implement the
/// [Executable][Executable] trait /// [Executable][Executable] trait
///
/// # Arguments /// # Arguments
/// ///
/// * `executable_vec`: Vector of executable objects /// * `executable_vec`: Vector of executable objects
@ -134,10 +136,11 @@ pub fn exec_sched_multi<
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{exec_sched_multi, exec_sched_single, Executable, ExecutionType, OpResult}; use super::{exec_sched_multi, exec_sched_single, Executable, ExecutionType, OpResult};
use bus::Bus;
use std::error::Error; use std::error::Error;
use std::fmt;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::Duration; use std::time::Duration;
use std::{fmt, thread};
struct TestInfo { struct TestInfo {
exec_num: u32, exec_num: u32,
@ -154,32 +157,41 @@ mod tests {
exec_num: Arc<Mutex<TestInfo>>, exec_num: Arc<Mutex<TestInfo>>,
} }
#[derive(Debug, PartialEq)] #[derive(Clone, Debug)]
struct ExampleError { struct ExampleError {
details: String, kind: ErrorKind,
code: i32, }
/// The kind of an error that can occur.
#[derive(Clone, Debug)]
pub enum ErrorKind {
Generic(String, i32)
} }
impl ExampleError { impl ExampleError {
fn new(msg: &str, code: i32) -> ExampleError { fn new(msg: &str, code: i32) -> ExampleError {
ExampleError { ExampleError {
details: msg.to_string(), kind: ErrorKind::Generic(msg.to_string(), code)
code,
} }
} }
/// Return the kind of this error.
pub fn kind(&self) -> &ErrorKind {
&self.kind
}
} }
impl fmt::Display for ExampleError { impl fmt::Display for ExampleError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.details) match self.kind() {
ErrorKind::Generic(str, code) => {
write!(f, "{str} with code {code}")
}
}
} }
} }
impl Error for ExampleError { impl Error for ExampleError {}
fn description(&self) -> &str {
&self.details
}
}
const ONE_SHOT_TASK_NAME: &str = "One Shot Task"; const ONE_SHOT_TASK_NAME: &str = "One Shot Task";
@ -233,6 +245,8 @@ mod tests {
} }
} }
const PERIODIC_TASK_NAME: &str = "Periodic Task";
impl Executable for PeriodicTask { impl Executable for PeriodicTask {
type Error = ExampleError; type Error = ExampleError;
@ -241,7 +255,7 @@ mod tests {
} }
fn task_name(&self) -> &'static str { fn task_name(&self) -> &'static str {
"Periodic Task" PERIODIC_TASK_NAME
} }
fn periodic_op(&mut self, op_code: i32) -> Result<OpResult, ExampleError> { fn periodic_op(&mut self, op_code: i32) -> Result<OpResult, ExampleError> {
@ -282,6 +296,41 @@ mod tests {
assert_eq!(data.op_code, expected_op_code); assert_eq!(data.op_code, expected_op_code);
} }
#[test]
fn test_failed_one_shot() {
let op_code_inducing_failure = -1;
let shared = Arc::new(Mutex::new(TestInfo {
exec_num: 0,
op_code: 0,
}));
let exec_task = OneShotTask {
exec_num: shared.clone(),
};
let task = Box::new(exec_task);
let jhandle = exec_sched_single(
task,
Some(Duration::from_millis(100)),
op_code_inducing_failure,
None,
);
let thread_res = jhandle.join().expect("One Shot Task failed");
assert!(thread_res.is_err());
let error = thread_res.unwrap_err();
let err = error.kind();
assert!(matches!(err, &ErrorKind::Generic { .. }));
match err {
ErrorKind::Generic(str, op_code) => {
assert_eq!(str, &String::from("One Shot Task Failure"));
assert_eq!(op_code, &op_code_inducing_failure);
}
}
let error_display = error.to_string();
assert_eq!(error_display, "One Shot Task Failure with code -1");
let data = shared.lock().expect("Locking Mutex failed");
assert_eq!(data.exec_num, 1);
assert_eq!(data.op_code, op_code_inducing_failure);
}
#[test] #[test]
fn test_simple_multi_one_shot() { fn test_simple_multi_one_shot() {
let expected_op_code = 43; let expected_op_code = 43;
@ -314,7 +363,7 @@ mod tests {
} }
#[test] #[test]
fn test_cycles() { fn test_cycles_single() {
let expected_op_code = 44; let expected_op_code = 44;
let shared = Arc::new(Mutex::new(TestInfo { let shared = Arc::new(Mutex::new(TestInfo {
exec_num: 0, exec_num: 0,
@ -325,7 +374,12 @@ mod tests {
cycles: 1, cycles: 1,
}); });
assert_eq!(cycled_task.task_name(), CYCLE_TASK_NAME); assert_eq!(cycled_task.task_name(), CYCLE_TASK_NAME);
let jh = exec_sched_single(cycled_task, Some(Duration::from_millis(100)), expected_op_code, None); let jh = exec_sched_single(
cycled_task,
Some(Duration::from_millis(100)),
expected_op_code,
None,
);
let thread_res = jh.join().expect("Cycles Task failed"); let thread_res = jh.join().expect("Cycles Task failed");
assert!(thread_res.is_ok()); assert!(thread_res.is_ok());
let data = shared.lock().expect("Locking Mutex failed"); let data = shared.lock().expect("Locking Mutex failed");
@ -334,22 +388,107 @@ mod tests {
assert_eq!(data.op_code, expected_op_code); assert_eq!(data.op_code, expected_op_code);
} }
#[test]
fn test_single_and_cycles() {
let expected_op_code = 50;
let shared = Arc::new(Mutex::new(TestInfo {
exec_num: 0,
op_code: 0,
}));
let one_shot_task = Box::new(OneShotTask {
exec_num: shared.clone(),
});
let cycled_task_0 = Box::new(FixedCyclesTask {
exec_num: shared.clone(),
cycles: 1,
});
let cycled_task_1 = Box::new(FixedCyclesTask {
exec_num: shared.clone(),
cycles: 1,
});
assert_eq!(cycled_task_0.task_name(), CYCLE_TASK_NAME);
assert_eq!(one_shot_task.task_name(), ONE_SHOT_TASK_NAME);
let task_vec: Vec<Box<dyn Executable<Error=ExampleError>>> = vec![one_shot_task, cycled_task_0, cycled_task_1];
let jh = exec_sched_multi(
task_vec,
Some(Duration::from_millis(100)),
expected_op_code,
None,
);
let thread_res = jh.join().expect("Cycles Task failed");
assert!(thread_res.is_ok());
let data = shared.lock().expect("Locking Mutex failed");
assert_eq!(thread_res.unwrap(), OpResult::Ok);
assert_eq!(data.exec_num, 3);
assert_eq!(data.op_code, expected_op_code);
}
#[test] #[test]
#[ignore] #[ignore]
fn test_periodic() { fn test_periodic_single() {
let mut terminator = Bus::new(5);
let expected_op_code = 45; let expected_op_code = 45;
let shared = Arc::new(Mutex::new(TestInfo { let shared = Arc::new(Mutex::new(TestInfo {
exec_num: 0, exec_num: 0,
op_code: 0, op_code: 0,
})); }));
let periodic_task = Box::new(PeriodicTask { let periodic_task = Box::new(PeriodicTask {
exec_num: shared.clone() exec_num: shared.clone(),
}); });
let jh = exec_sched_single(periodic_task, Some(Duration::from_millis(50)), expected_op_code, None); assert_eq!(periodic_task.task_name(), PERIODIC_TASK_NAME);
let jh = exec_sched_single(
periodic_task,
Some(Duration::from_millis(20)),
expected_op_code,
Some(terminator.add_rx()),
);
thread::sleep(Duration::from_millis(40));
terminator.broadcast(());
let thread_res = jh.join().expect("Periodic Task failed"); let thread_res = jh.join().expect("Periodic Task failed");
assert!(thread_res.is_ok()); assert!(thread_res.is_ok());
let data = shared.lock().expect("Locking Mutex failed"); let data = shared.lock().expect("Locking Mutex failed");
assert_eq!(thread_res.unwrap(), OpResult::Ok); assert_eq!(thread_res.unwrap(), OpResult::Ok);
let range = 2..4;
assert!(range.contains(&data.exec_num));
assert_eq!(data.op_code, expected_op_code);
}
#[test]
#[ignore]
fn test_periodic_multi() {
let mut terminator = Bus::new(5);
let expected_op_code = 46;
let shared = Arc::new(Mutex::new(TestInfo {
exec_num: 0,
op_code: 0,
}));
let cycled_task = Box::new(FixedCyclesTask {
exec_num: shared.clone(),
cycles: 1,
});
let periodic_task_0 = Box::new(PeriodicTask {
exec_num: shared.clone(),
});
let periodic_task_1 = Box::new(PeriodicTask {
exec_num: shared.clone(),
});
assert_eq!(periodic_task_0.task_name(), PERIODIC_TASK_NAME);
assert_eq!(periodic_task_1.task_name(), PERIODIC_TASK_NAME);
let task_vec: Vec<Box<dyn Executable<Error=ExampleError>>> = vec![cycled_task, periodic_task_0, periodic_task_1];
let jh = exec_sched_multi(
task_vec,
Some(Duration::from_millis(20)),
expected_op_code,
Some(terminator.add_rx()),
);
thread::sleep(Duration::from_millis(60));
terminator.broadcast(());
let thread_res = jh.join().expect("Periodic Task failed");
assert!(thread_res.is_ok());
let data = shared.lock().expect("Locking Mutex failed");
assert_eq!(thread_res.unwrap(), OpResult::Ok);
let range = 7..11;
assert!(range.contains(&data.exec_num));
assert_eq!(data.op_code, expected_op_code); assert_eq!(data.op_code, expected_op_code);
} }
} }