From 061fcf2d97dc8755d686d7ed446ad9942ddc9c85 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 16 May 2022 11:01:57 +0200 Subject: [PATCH 01/12] continued threading tests --- src/main.rs | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 63f5441..0f30f53 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,138 @@ -// use launchpad::ObjectManager; +use std::error::Error; +use std::fmt; +use std::thread; +use std::thread::JoinHandle; +use std::time::Duration; + +enum OpResult { + Ok, +} + +enum ExecutionType { + Infinite, + Cycles(u32), + OneShot, +} + +trait ExecutableWithAssocConst { + type Error; + const EXEC_TYPE: ExecutionType; + const TASK_FREQ: Option; + const TASK_NAME: &'static str; + fn periodic_op(&mut self) -> Result; +} + +trait Executable { + type Error; + fn exec_type(&self) -> ExecutionType; + fn task_freq(&self) -> Option; + fn task_name(&self) -> &'static str; + fn periodic_op(&mut self) -> Result; +} + +struct ExampleTask {} + +#[derive(Debug)] +struct ExampleError { + details: String, +} + +impl ExampleError { + fn new(msg: &str) -> ExampleError { + ExampleError { + details: msg.to_string(), + } + } +} + +impl fmt::Display for ExampleError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.details) + } +} + +impl Error for ExampleError { + fn description(&self) -> &str { + &self.details + } +} + +impl ExecutableWithAssocConst for ExampleTask { + type Error = ExampleError; + const EXEC_TYPE: ExecutionType = ExecutionType::OneShot; + const TASK_FREQ: Option = Some(Duration::from_millis(500)); + const TASK_NAME: &'static str = "Test Task"; + + fn periodic_op(&mut self) -> Result { + println!("Periodic Operation!"); + Ok(OpResult::Ok) + } +} fn main() { - println!("Hello World"); + let mut exec_task = ExampleTask {}; + let jhandle = thread::spawn(move || match ExampleTask::EXEC_TYPE { + ExecutionType::OneShot => exec_task.periodic_op(), + ExecutionType::Infinite => loop { + exec_task.periodic_op().expect("Periodic Op failed"); + let freq = ExampleTask::TASK_FREQ.unwrap_or_else(|| { + panic!( + "No task frequency specified for task {}", + ExampleTask::TASK_NAME + ) + }); + thread::sleep(freq) + }, + ExecutionType::Cycles(cycles) => { + for _i in 0..cycles - 1 { + exec_task.periodic_op().unwrap(); + let freq = ExampleTask::TASK_FREQ.unwrap_or_else(|| { + panic!( + "No task frequency specified for task {}", + ExampleTask::TASK_NAME + ) + }); + thread::sleep(freq) + } + Ok(OpResult::Ok) + } + }); + let mut exec_task2 = ExampleTask {}; + let jhandle2 = test_thread(exec_task2); + jhandle + .join() + .expect("Joining thread failed") + .expect("Task failed"); + jhandle2 + .join() + .expect("Joining thread 2 failed") + .expect("Task 2 failed"); +} + +fn test_thread< + T: ExecutableWithAssocConst + Send + 'static, + E: Error + Send + 'static, +>( + mut executable: T, +) -> JoinHandle> { + // let executable = Arc::new(executable_unprotected); + thread::spawn(move || match T::EXEC_TYPE { + ExecutionType::OneShot => executable.periodic_op(), + ExecutionType::Infinite => loop { + executable.periodic_op().expect("Periodic Op failed"); + let freq = T::TASK_FREQ + .unwrap_or_else(|| panic!("No task frequency specified for task {}", T::TASK_NAME)); + thread::sleep(freq) + }, + ExecutionType::Cycles(cycles) => { + for _i in 0..cycles - 1 { + executable.periodic_op().unwrap(); + let freq = T::TASK_FREQ.unwrap_or_else(|| { + panic!("No task frequency specified for task {}", T::TASK_NAME) + }); + thread::sleep(freq) + } + Ok(OpResult::Ok) + } + }) } From 529d2ff67dcdbedc02a868d190f887b5e07fce9f Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 16 May 2022 11:24:59 +0200 Subject: [PATCH 02/12] error handling fixes --- src/core.rs | 1 + src/core/executable.rs | 27 +++++++++++++ src/main.rs | 86 ++++++++++-------------------------------- 3 files changed, 47 insertions(+), 67 deletions(-) create mode 100644 src/core/executable.rs diff --git a/src/core.rs b/src/core.rs index 61031db..6ee7941 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1,2 +1,3 @@ pub mod events; +pub mod executable; pub mod objects; diff --git a/src/core/executable.rs b/src/core/executable.rs new file mode 100644 index 0000000..527e5ee --- /dev/null +++ b/src/core/executable.rs @@ -0,0 +1,27 @@ +use std::time::Duration; + +pub enum OpResult { + Ok, +} + +pub enum ExecutionType { + Infinite, + Cycles(u32), + OneShot, +} + +pub trait Executable { + type Error; + const EXEC_TYPE: ExecutionType; + const TASK_FREQ: Option; + const TASK_NAME: &'static str; + fn periodic_op(&mut self, op_code: u32) -> Result; +} + +trait ExecutableWithAssociatedFuncs { + type Error; + fn exec_type(&self) -> ExecutionType; + fn task_freq(&self) -> Option; + fn task_name(&self) -> &'static str; + fn periodic_op(&mut self) -> Result; +} diff --git a/src/main.rs b/src/main.rs index 0f30f53..d2bb3d6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,35 +1,10 @@ +use launchpad::core::executable::{Executable, ExecutionType, OpResult}; use std::error::Error; use std::fmt; use std::thread; use std::thread::JoinHandle; use std::time::Duration; -enum OpResult { - Ok, -} - -enum ExecutionType { - Infinite, - Cycles(u32), - OneShot, -} - -trait ExecutableWithAssocConst { - type Error; - const EXEC_TYPE: ExecutionType; - const TASK_FREQ: Option; - const TASK_NAME: &'static str; - fn periodic_op(&mut self) -> Result; -} - -trait Executable { - type Error; - fn exec_type(&self) -> ExecutionType; - fn task_freq(&self) -> Option; - fn task_name(&self) -> &'static str; - fn periodic_op(&mut self) -> Result; -} - struct ExampleTask {} #[derive(Debug)] @@ -57,48 +32,28 @@ impl Error for ExampleError { } } -impl ExecutableWithAssocConst for ExampleTask { +impl Executable for ExampleTask { type Error = ExampleError; const EXEC_TYPE: ExecutionType = ExecutionType::OneShot; const TASK_FREQ: Option = Some(Duration::from_millis(500)); const TASK_NAME: &'static str = "Test Task"; - fn periodic_op(&mut self) -> Result { - println!("Periodic Operation!"); - Ok(OpResult::Ok) + fn periodic_op(&mut self, op_code: u32) -> Result { + if op_code == 0 { + println!("Periodic Operation OK!"); + Ok(OpResult::Ok) + } else { + println!("Periodic Operation Fail!"); + Err(ExampleError::new("Example Task Failure")) + } } } fn main() { - let mut exec_task = ExampleTask {}; - let jhandle = thread::spawn(move || match ExampleTask::EXEC_TYPE { - ExecutionType::OneShot => exec_task.periodic_op(), - ExecutionType::Infinite => loop { - exec_task.periodic_op().expect("Periodic Op failed"); - let freq = ExampleTask::TASK_FREQ.unwrap_or_else(|| { - panic!( - "No task frequency specified for task {}", - ExampleTask::TASK_NAME - ) - }); - thread::sleep(freq) - }, - ExecutionType::Cycles(cycles) => { - for _i in 0..cycles - 1 { - exec_task.periodic_op().unwrap(); - let freq = ExampleTask::TASK_FREQ.unwrap_or_else(|| { - panic!( - "No task frequency specified for task {}", - ExampleTask::TASK_NAME - ) - }); - thread::sleep(freq) - } - Ok(OpResult::Ok) - } - }); - let mut exec_task2 = ExampleTask {}; - let jhandle2 = test_thread(exec_task2); + let exec_task = ExampleTask {}; + let jhandle = test_thread(exec_task, 0); + let exec_task2 = ExampleTask {}; + let jhandle2 = test_thread(exec_task2, 1); jhandle .join() .expect("Joining thread failed") @@ -109,24 +64,21 @@ fn main() { .expect("Task 2 failed"); } -fn test_thread< - T: ExecutableWithAssocConst + Send + 'static, - E: Error + Send + 'static, ->( +fn test_thread + Send + 'static, E: Error + Send + 'static>( mut executable: T, + op_code: u32, ) -> JoinHandle> { - // let executable = Arc::new(executable_unprotected); thread::spawn(move || match T::EXEC_TYPE { - ExecutionType::OneShot => executable.periodic_op(), + ExecutionType::OneShot => executable.periodic_op(op_code), ExecutionType::Infinite => loop { - executable.periodic_op().expect("Periodic Op failed"); + executable.periodic_op(op_code)?; let freq = T::TASK_FREQ .unwrap_or_else(|| panic!("No task frequency specified for task {}", T::TASK_NAME)); thread::sleep(freq) }, ExecutionType::Cycles(cycles) => { for _i in 0..cycles - 1 { - executable.periodic_op().unwrap(); + executable.periodic_op(op_code)?; let freq = T::TASK_FREQ.unwrap_or_else(|| { panic!("No task frequency specified for task {}", T::TASK_NAME) }); From e4b5df8287be5e16ac052d38bdd87a84e697da6e Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 26 May 2022 17:51:10 +0200 Subject: [PATCH 03/12] allow passing in task vectors now --- src/core/executable.rs | 6 +-- src/main.rs | 109 +++++++++++++++++++++++++++++------------ 2 files changed, 80 insertions(+), 35 deletions(-) diff --git a/src/core/executable.rs b/src/core/executable.rs index 527e5ee..eaa6dc0 100644 --- a/src/core/executable.rs +++ b/src/core/executable.rs @@ -1,5 +1,3 @@ -use std::time::Duration; - pub enum OpResult { Ok, } @@ -13,15 +11,13 @@ pub enum ExecutionType { pub trait Executable { type Error; const EXEC_TYPE: ExecutionType; - const TASK_FREQ: Option; const TASK_NAME: &'static str; - fn periodic_op(&mut self, op_code: u32) -> Result; + fn periodic_op(&mut self, op_code: i32) -> Result; } trait ExecutableWithAssociatedFuncs { type Error; fn exec_type(&self) -> ExecutionType; - fn task_freq(&self) -> Option; fn task_name(&self) -> &'static str; fn periodic_op(&mut self) -> Result; } diff --git a/src/main.rs b/src/main.rs index d2bb3d6..234c095 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,9 @@ use std::thread; use std::thread::JoinHandle; use std::time::Duration; -struct ExampleTask {} +struct OneShotTask {} +struct FixedCyclesTask {} +struct PeriodicTask {} #[derive(Debug)] struct ExampleError { @@ -32,28 +34,63 @@ impl Error for ExampleError { } } -impl Executable for ExampleTask { +impl Executable for OneShotTask { type Error = ExampleError; const EXEC_TYPE: ExecutionType = ExecutionType::OneShot; - const TASK_FREQ: Option = Some(Duration::from_millis(500)); - const TASK_NAME: &'static str = "Test Task"; + const TASK_NAME: &'static str = "One Shot Test Task"; - fn periodic_op(&mut self, op_code: u32) -> Result { - if op_code == 0 { - println!("Periodic Operation OK!"); + fn periodic_op(&mut self, op_code: i32) -> Result { + if op_code >= 0 { + println!("One-shot operation with operation code {op_code} OK!"); Ok(OpResult::Ok) } else { - println!("Periodic Operation Fail!"); + println!("One-shot operation failure by passing op code {op_code}!"); + Err(ExampleError::new("Example Task Failure")) + } + } +} + +impl Executable for FixedCyclesTask { + type Error = ExampleError; + const EXEC_TYPE: ExecutionType = ExecutionType::Cycles(5); + const TASK_NAME: &'static str = "Fixed Cycles Task"; + + fn periodic_op(&mut self, op_code: i32) -> Result { + if op_code >= 0 { + println!("Fixed-cycle operation with operation code {op_code} OK!"); + Ok(OpResult::Ok) + } else { + println!("Fixed-cycle operation failure by passing op code {op_code}!"); + Err(ExampleError::new("Example Task Failure")) + } + } +} + +impl Executable for PeriodicTask { + type Error = ExampleError; + const EXEC_TYPE: ExecutionType = ExecutionType::Cycles(5); + const TASK_NAME: &'static str = "Fixed Cycles Task"; + + fn periodic_op(&mut self, op_code: i32) -> Result { + if op_code >= 0 { + println!("Periodic operation with operation code {op_code} OK!"); + Ok(OpResult::Ok) + } else { + println!("Periodic operation failure by passing op code {op_code}!"); Err(ExampleError::new("Example Task Failure")) } } } fn main() { - let exec_task = ExampleTask {}; - let jhandle = test_thread(exec_task, 0); - let exec_task2 = ExampleTask {}; - let jhandle2 = test_thread(exec_task2, 1); + let exec_task = OneShotTask {}; + let mut task_vec = Vec::new(); + task_vec.push(exec_task); + let jhandle = test_thread(task_vec, Some(Duration::from_millis(500)), 0); + let exec_task2 = FixedCyclesTask {}; + let mut task_vec2 = Vec::new(); + task_vec2.push(exec_task2); + let jhandle2 = test_thread(task_vec2, Some(Duration::from_millis(1000)), 1); jhandle .join() .expect("Joining thread failed") @@ -65,26 +102,38 @@ fn main() { } fn test_thread + Send + 'static, E: Error + Send + 'static>( - mut executable: T, - op_code: u32, + mut executable_vec: Vec, + task_freq: Option, + op_code: i32, ) -> JoinHandle> { - thread::spawn(move || match T::EXEC_TYPE { - ExecutionType::OneShot => executable.periodic_op(op_code), - ExecutionType::Infinite => loop { - executable.periodic_op(op_code)?; - let freq = T::TASK_FREQ - .unwrap_or_else(|| panic!("No task frequency specified for task {}", T::TASK_NAME)); - thread::sleep(freq) - }, - ExecutionType::Cycles(cycles) => { - for _i in 0..cycles - 1 { - executable.periodic_op(op_code)?; - let freq = T::TASK_FREQ.unwrap_or_else(|| { - panic!("No task frequency specified for task {}", T::TASK_NAME) - }); - thread::sleep(freq) + let mut cycle_counts = vec![0; executable_vec.len()]; + let mut removal_flags = vec![false; executable_vec.len()]; + thread::spawn(move || loop { + for (idx, executable) in executable_vec.iter_mut().enumerate() { + match T::EXEC_TYPE { + ExecutionType::OneShot => { + executable.periodic_op(op_code)?; + removal_flags[idx] = true; + } + ExecutionType::Infinite => { + executable.periodic_op(op_code)?; + } + ExecutionType::Cycles(cycles) => { + executable.periodic_op(op_code)?; + cycle_counts[idx] += 1; + if cycle_counts[idx] == cycles { + removal_flags[idx] = true; + } + } } - Ok(OpResult::Ok) } + executable_vec.retain(|_| !*removal_flags.iter().next().unwrap()); + removal_flags.retain(|&i| i == false); + if executable_vec.is_empty() { + return Ok(OpResult::Ok); + } + let freq = task_freq + .unwrap_or_else(|| panic!("No task frequency specified for task {}", T::TASK_NAME)); + thread::sleep(freq); }) } From be2a4ac3a2d6d64d96ee047c9a43596eaa31e12e Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 26 May 2022 18:53:07 +0200 Subject: [PATCH 04/12] testing --- src/core/executable.rs | 20 +++++++------- src/main.rs | 62 +++++++++++++++++++++++++++++++----------- 2 files changed, 56 insertions(+), 26 deletions(-) diff --git a/src/core/executable.rs b/src/core/executable.rs index eaa6dc0..4750fe4 100644 --- a/src/core/executable.rs +++ b/src/core/executable.rs @@ -8,16 +8,16 @@ pub enum ExecutionType { OneShot, } -pub trait Executable { +pub trait Executable: Send { type Error; - const EXEC_TYPE: ExecutionType; - const TASK_NAME: &'static str; + //const EXEC_TYPE: ExecutionType; + //const TASK_NAME: &'static str; + + fn exec_type(&self) -> ExecutionType;// { + // return Self::EXEC_TYPE; + //} + fn task_name(&self) -> &'static str;//{ + // return Self::TASK_NAME; + //} fn periodic_op(&mut self, op_code: i32) -> Result; } - -trait ExecutableWithAssociatedFuncs { - type Error; - fn exec_type(&self) -> ExecutionType; - fn task_name(&self) -> &'static str; - fn periodic_op(&mut self) -> Result; -} diff --git a/src/main.rs b/src/main.rs index 234c095..8be3696 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,8 +36,14 @@ impl Error for ExampleError { impl Executable for OneShotTask { type Error = ExampleError; - const EXEC_TYPE: ExecutionType = ExecutionType::OneShot; - const TASK_NAME: &'static str = "One Shot Test Task"; + + fn exec_type(&self) -> ExecutionType { + ExecutionType::OneShot + } + + fn task_name(&self) -> &'static str { + "One Shot Task" + } fn periodic_op(&mut self, op_code: i32) -> Result { if op_code >= 0 { @@ -52,8 +58,14 @@ impl Executable for OneShotTask { impl Executable for FixedCyclesTask { type Error = ExampleError; - const EXEC_TYPE: ExecutionType = ExecutionType::Cycles(5); - const TASK_NAME: &'static str = "Fixed Cycles Task"; + + fn exec_type(&self) -> ExecutionType { + ExecutionType::Cycles(5) + } + + fn task_name(&self) -> &'static str { + "Fixed Cycles Task" + } fn periodic_op(&mut self, op_code: i32) -> Result { if op_code >= 0 { @@ -68,8 +80,14 @@ impl Executable for FixedCyclesTask { impl Executable for PeriodicTask { type Error = ExampleError; - const EXEC_TYPE: ExecutionType = ExecutionType::Cycles(5); - const TASK_NAME: &'static str = "Fixed Cycles Task"; + + fn exec_type(&self) -> ExecutionType { + ExecutionType::Infinite + } + + fn task_name(&self) -> &'static str { + "Periodic Task" + } fn periodic_op(&mut self, op_code: i32) -> Result { if op_code >= 0 { @@ -82,15 +100,14 @@ impl Executable for PeriodicTask { } } -fn main() { +fn test0() { let exec_task = OneShotTask {}; - let mut task_vec = Vec::new(); - task_vec.push(exec_task); + let task_vec = vec![Box::new(exec_task)]; let jhandle = test_thread(task_vec, Some(Duration::from_millis(500)), 0); let exec_task2 = FixedCyclesTask {}; - let mut task_vec2 = Vec::new(); - task_vec2.push(exec_task2); + let task_vec2 = vec![Box::new(exec_task2)]; let jhandle2 = test_thread(task_vec2, Some(Duration::from_millis(1000)), 1); + jhandle .join() .expect("Joining thread failed") @@ -100,9 +117,22 @@ fn main() { .expect("Joining thread 2 failed") .expect("Task 2 failed"); } +fn main() { -fn test_thread + Send + 'static, E: Error + Send + 'static>( - mut executable_vec: Vec, + + thread::sleep(Duration::from_millis(1000)); + let one_shot_in_vec = OneShotTask {}; + let cycles_in_vec = FixedCyclesTask {}; + let test_vec: Vec>> = vec![Box::new(one_shot_in_vec), Box::new(cycles_in_vec)]; + let jhandle3 = test_thread(test_vec, Some(Duration::from_millis(500)), 3); + jhandle3 + .join() + .expect("Joining thread 3 failed") + .expect("Task 3 failed"); +} + +fn test_thread + Send + 'static + ?Sized, E: Error + Send + 'static>( + mut executable_vec: Vec>, task_freq: Option, op_code: i32, ) -> JoinHandle> { @@ -110,7 +140,7 @@ fn test_thread + Send + 'static, E: Error + Send + 'sta let mut removal_flags = vec![false; executable_vec.len()]; thread::spawn(move || loop { for (idx, executable) in executable_vec.iter_mut().enumerate() { - match T::EXEC_TYPE { + match executable.exec_type() { ExecutionType::OneShot => { executable.periodic_op(op_code)?; removal_flags[idx] = true; @@ -128,12 +158,12 @@ fn test_thread + Send + 'static, E: Error + Send + 'sta } } executable_vec.retain(|_| !*removal_flags.iter().next().unwrap()); - removal_flags.retain(|&i| i == false); + removal_flags.retain(|&i| !i); if executable_vec.is_empty() { return Ok(OpResult::Ok); } let freq = task_freq - .unwrap_or_else(|| panic!("No task frequency specified for task {}", T::TASK_NAME)); + .unwrap_or_else(|| panic!("No task frequency specified")); thread::sleep(freq); }) } From 35aa7c450ce274fef0974e30986b7289b69faa8c Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 26 May 2022 19:19:16 +0200 Subject: [PATCH 05/12] some bugfixes --- src/core/executable.rs | 10 ++-------- src/main.rs | 36 +++++++++++++++++++++++------------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/core/executable.rs b/src/core/executable.rs index 4750fe4..a94ca16 100644 --- a/src/core/executable.rs +++ b/src/core/executable.rs @@ -10,14 +10,8 @@ pub enum ExecutionType { pub trait Executable: Send { type Error; - //const EXEC_TYPE: ExecutionType; - //const TASK_NAME: &'static str; - fn exec_type(&self) -> ExecutionType;// { - // return Self::EXEC_TYPE; - //} - fn task_name(&self) -> &'static str;//{ - // return Self::TASK_NAME; - //} + fn exec_type(&self) -> ExecutionType; + fn task_name(&self) -> &'static str; fn periodic_op(&mut self, op_code: i32) -> Result; } diff --git a/src/main.rs b/src/main.rs index 8be3696..06b9f61 100644 --- a/src/main.rs +++ b/src/main.rs @@ -60,7 +60,7 @@ impl Executable for FixedCyclesTask { type Error = ExampleError; fn exec_type(&self) -> ExecutionType { - ExecutionType::Cycles(5) + ExecutionType::Cycles(3) } fn task_name(&self) -> &'static str { @@ -103,10 +103,11 @@ impl Executable for PeriodicTask { fn test0() { let exec_task = OneShotTask {}; let task_vec = vec![Box::new(exec_task)]; - let jhandle = test_thread(task_vec, Some(Duration::from_millis(500)), 0); + let jhandle = executable_scheduler(task_vec, Some(Duration::from_millis(100)), 0); let exec_task2 = FixedCyclesTask {}; - let task_vec2 = vec![Box::new(exec_task2)]; - let jhandle2 = test_thread(task_vec2, Some(Duration::from_millis(1000)), 1); + let task_vec2: Vec + Send>> = + vec![Box::new(exec_task2)]; + let jhandle2 = executable_scheduler(task_vec2, Some(Duration::from_millis(100)), 1); jhandle .join() @@ -117,21 +118,28 @@ fn test0() { .expect("Joining thread 2 failed") .expect("Task 2 failed"); } -fn main() { - - thread::sleep(Duration::from_millis(1000)); +fn test1() { let one_shot_in_vec = OneShotTask {}; let cycles_in_vec = FixedCyclesTask {}; - let test_vec: Vec>> = vec![Box::new(one_shot_in_vec), Box::new(cycles_in_vec)]; - let jhandle3 = test_thread(test_vec, Some(Duration::from_millis(500)), 3); + let test_vec: Vec>> = + vec![Box::new(one_shot_in_vec), Box::new(cycles_in_vec)]; + let jhandle3 = executable_scheduler(test_vec, Some(Duration::from_millis(100)), 3); jhandle3 .join() .expect("Joining thread 3 failed") .expect("Task 3 failed"); } +fn main() { + test0(); + thread::sleep(Duration::from_millis(1000)); + test1(); +} -fn test_thread + Send + 'static + ?Sized, E: Error + Send + 'static>( +fn executable_scheduler< + T: Executable + Send + 'static + ?Sized, + E: Error + Send + 'static, +>( mut executable_vec: Vec>, task_freq: Option, op_code: i32, @@ -157,13 +165,15 @@ fn test_thread + Send + 'static + ?Sized, E: Error + Se } } } - executable_vec.retain(|_| !*removal_flags.iter().next().unwrap()); + let mut removal_iter = removal_flags.iter(); + executable_vec.retain(|_| !*removal_iter.next().unwrap()); + removal_iter = removal_flags.iter(); + cycle_counts.retain(|_| !*removal_iter.next().unwrap()); removal_flags.retain(|&i| !i); if executable_vec.is_empty() { return Ok(OpResult::Ok); } - let freq = task_freq - .unwrap_or_else(|| panic!("No task frequency specified")); + let freq = task_freq.unwrap_or_else(|| panic!("No task frequency specified")); thread::sleep(freq); }) } From e9567e38727d3b85e62b8f7c7894b5023c7abe5f Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 26 May 2022 19:23:31 +0200 Subject: [PATCH 06/12] moved exec scheduler into lib --- src/core/executable.rs | 47 ++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 45 +--------------------------------------- 2 files changed, 48 insertions(+), 44 deletions(-) diff --git a/src/core/executable.rs b/src/core/executable.rs index a94ca16..9e2a4e8 100644 --- a/src/core/executable.rs +++ b/src/core/executable.rs @@ -1,3 +1,8 @@ +use std::error::Error; +use std::thread; +use std::thread::JoinHandle; +use std::time::Duration; + pub enum OpResult { Ok, } @@ -15,3 +20,45 @@ pub trait Executable: Send { fn task_name(&self) -> &'static str; fn periodic_op(&mut self, op_code: i32) -> Result; } + +pub fn executable_scheduler< + T: Executable + Send + 'static + ?Sized, + E: Error + Send + 'static, +>( + mut executable_vec: Vec>, + task_freq: Option, + op_code: i32, +) -> JoinHandle> { + let mut cycle_counts = vec![0; executable_vec.len()]; + let mut removal_flags = vec![false; executable_vec.len()]; + thread::spawn(move || loop { + for (idx, executable) in executable_vec.iter_mut().enumerate() { + match executable.exec_type() { + ExecutionType::OneShot => { + executable.periodic_op(op_code)?; + removal_flags[idx] = true; + } + ExecutionType::Infinite => { + executable.periodic_op(op_code)?; + } + ExecutionType::Cycles(cycles) => { + executable.periodic_op(op_code)?; + cycle_counts[idx] += 1; + if cycle_counts[idx] == cycles { + removal_flags[idx] = true; + } + } + } + } + let mut removal_iter = removal_flags.iter(); + executable_vec.retain(|_| !*removal_iter.next().unwrap()); + removal_iter = removal_flags.iter(); + cycle_counts.retain(|_| !*removal_iter.next().unwrap()); + removal_flags.retain(|&i| !i); + if executable_vec.is_empty() { + return Ok(OpResult::Ok); + } + let freq = task_freq.unwrap_or_else(|| panic!("No task frequency specified")); + thread::sleep(freq); + }) +} diff --git a/src/main.rs b/src/main.rs index 06b9f61..e2b983e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,7 @@ -use launchpad::core::executable::{Executable, ExecutionType, OpResult}; +use launchpad::core::executable::{executable_scheduler, Executable, ExecutionType, OpResult}; use std::error::Error; use std::fmt; use std::thread; -use std::thread::JoinHandle; use std::time::Duration; struct OneShotTask {} @@ -135,45 +134,3 @@ fn main() { thread::sleep(Duration::from_millis(1000)); test1(); } - -fn executable_scheduler< - T: Executable + Send + 'static + ?Sized, - E: Error + Send + 'static, ->( - mut executable_vec: Vec>, - task_freq: Option, - op_code: i32, -) -> JoinHandle> { - let mut cycle_counts = vec![0; executable_vec.len()]; - let mut removal_flags = vec![false; executable_vec.len()]; - thread::spawn(move || loop { - for (idx, executable) in executable_vec.iter_mut().enumerate() { - match executable.exec_type() { - ExecutionType::OneShot => { - executable.periodic_op(op_code)?; - removal_flags[idx] = true; - } - ExecutionType::Infinite => { - executable.periodic_op(op_code)?; - } - ExecutionType::Cycles(cycles) => { - executable.periodic_op(op_code)?; - cycle_counts[idx] += 1; - if cycle_counts[idx] == cycles { - removal_flags[idx] = true; - } - } - } - } - let mut removal_iter = removal_flags.iter(); - executable_vec.retain(|_| !*removal_iter.next().unwrap()); - removal_iter = removal_flags.iter(); - cycle_counts.retain(|_| !*removal_iter.next().unwrap()); - removal_flags.retain(|&i| !i); - if executable_vec.is_empty() { - return Ok(OpResult::Ok); - } - let freq = task_freq.unwrap_or_else(|| panic!("No task frequency specified")); - thread::sleep(freq); - }) -} From 40ba758a4eb8744a122401a0638e6a7fd1937adb Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 26 May 2022 21:10:08 +0200 Subject: [PATCH 07/12] cancel condition works --- Cargo.lock | 179 +++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 4 +- src/core/executable.rs | 10 +++ src/main.rs | 46 ++++++++--- 4 files changed, 228 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 12c6fba..b0b631b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,13 +2,158 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "atomic-option" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0db678acb667b525ac40a324fc5f7d3390e29239b31c7327bb8157f5b4fff593" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bus" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1e66e1779f5b1440f1a58220ba3b3ded4427175f0a9fb8d7066521f8b4e8f2b" +dependencies = [ + "atomic-option", + "crossbeam-channel 0.4.4", + "num_cpus", + "parking_lot_core", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "crossbeam-channel" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" +dependencies = [ + "crossbeam-utils 0.7.2", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils 0.8.8", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if 0.1.10", + "lazy_static", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "launchpad" version = "0.1.0" dependencies = [ + "bus", + "crossbeam-channel 0.5.4", "thiserror", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "parking_lot_core" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" +dependencies = [ + "cfg-if 0.1.10", + "cloudabi", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + [[package]] name = "proc-macro2" version = "1.0.38" @@ -27,6 +172,18 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + [[package]] name = "syn" version = "1.0.94" @@ -63,3 +220,25 @@ name = "unicode-xid" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 717fb73..116c94f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -thiserror = "1.0" \ No newline at end of file +thiserror = "1.0" +bus = "2.2.3" +crossbeam-channel = "0.5.4" \ No newline at end of file diff --git a/src/core/executable.rs b/src/core/executable.rs index 9e2a4e8..fad7d83 100644 --- a/src/core/executable.rs +++ b/src/core/executable.rs @@ -1,10 +1,13 @@ +use bus::BusReader; use std::error::Error; +use std::sync::mpsc::TryRecvError; use std::thread; use std::thread::JoinHandle; use std::time::Duration; pub enum OpResult { Ok, + TerminationRequested, } pub enum ExecutionType { @@ -28,10 +31,17 @@ pub fn executable_scheduler< mut executable_vec: Vec>, task_freq: Option, op_code: i32, + mut termination: BusReader<()>, ) -> JoinHandle> { let mut cycle_counts = vec![0; executable_vec.len()]; let mut removal_flags = vec![false; executable_vec.len()]; thread::spawn(move || loop { + match termination.try_recv() { + Ok(_) | Err(TryRecvError::Disconnected) => { + removal_flags.iter_mut().for_each(|x| *x = true); + } + Err(TryRecvError::Empty) => (), + } for (idx, executable) in executable_vec.iter_mut().enumerate() { match executable.exec_type() { ExecutionType::OneShot => { diff --git a/src/main.rs b/src/main.rs index e2b983e..b838125 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,9 @@ +use bus::{Bus, BusReader}; +use crossbeam_channel::{unbounded, Receiver, Sender}; use launchpad::core::executable::{executable_scheduler, Executable, ExecutionType, OpResult}; use std::error::Error; use std::fmt; +use std::mem::transmute; use std::thread; use std::time::Duration; @@ -99,14 +102,24 @@ impl Executable for PeriodicTask { } } -fn test0() { +fn test0(term_bus: &mut Bus<()>) { let exec_task = OneShotTask {}; let task_vec = vec![Box::new(exec_task)]; - let jhandle = executable_scheduler(task_vec, Some(Duration::from_millis(100)), 0); + let jhandle = executable_scheduler( + task_vec, + Some(Duration::from_millis(100)), + 0, + term_bus.add_rx(), + ); let exec_task2 = FixedCyclesTask {}; let task_vec2: Vec + Send>> = vec![Box::new(exec_task2)]; - let jhandle2 = executable_scheduler(task_vec2, Some(Duration::from_millis(100)), 1); + let jhandle2 = executable_scheduler( + task_vec2, + Some(Duration::from_millis(100)), + 1, + term_bus.add_rx(), + ); jhandle .join() @@ -118,19 +131,32 @@ fn test0() { .expect("Task 2 failed"); } -fn test1() { +fn test1(term_bus: &mut Bus<()>) { let one_shot_in_vec = OneShotTask {}; let cycles_in_vec = FixedCyclesTask {}; - let test_vec: Vec>> = - vec![Box::new(one_shot_in_vec), Box::new(cycles_in_vec)]; - let jhandle3 = executable_scheduler(test_vec, Some(Duration::from_millis(100)), 3); + let periodic_in_vec = PeriodicTask {}; + let test_vec: Vec>> = vec![ + Box::new(one_shot_in_vec), + Box::new(cycles_in_vec), + Box::new(periodic_in_vec), + ]; + let jhandle3 = executable_scheduler( + test_vec, + Some(Duration::from_millis(100)), + 3, + term_bus.add_rx(), + ); + thread::sleep(Duration::from_millis(5000)); + println!("Broadcasting cancel"); + term_bus.broadcast(()); jhandle3 .join() .expect("Joining thread 3 failed") .expect("Task 3 failed"); } + fn main() { - test0(); - thread::sleep(Duration::from_millis(1000)); - test1(); + let mut tx = Bus::new(5); + test0(&mut tx); + test1(&mut tx); } From 7989d197fda1a4a3255a37f5d842d50aa94fbf14 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 26 May 2022 21:10:25 +0200 Subject: [PATCH 08/12] clippy fix --- src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index b838125..f77a51d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,9 @@ -use bus::{Bus, BusReader}; -use crossbeam_channel::{unbounded, Receiver, Sender}; +use bus::{Bus}; + use launchpad::core::executable::{executable_scheduler, Executable, ExecutionType, OpResult}; use std::error::Error; use std::fmt; -use std::mem::transmute; + use std::thread; use std::time::Duration; From 59103135d9db25faf3b03c896009a54ce98cf25c Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 8 Jun 2022 19:42:32 +0200 Subject: [PATCH 09/12] continued executable module --- .idea/runConfigurations/Docs.xml | 18 ++ src/core/executable.rs | 293 ++++++++++++++++++++++++++++++- src/core/objects.rs | 14 +- src/main.rs | 161 +---------------- 4 files changed, 319 insertions(+), 167 deletions(-) create mode 100644 .idea/runConfigurations/Docs.xml diff --git a/.idea/runConfigurations/Docs.xml b/.idea/runConfigurations/Docs.xml new file mode 100644 index 0000000..c4efeaa --- /dev/null +++ b/.idea/runConfigurations/Docs.xml @@ -0,0 +1,18 @@ + + + + \ No newline at end of file diff --git a/src/core/executable.rs b/src/core/executable.rs index fad7d83..5b8193a 100644 --- a/src/core/executable.rs +++ b/src/core/executable.rs @@ -5,6 +5,7 @@ use std::thread; use std::thread::JoinHandle; use std::time::Duration; +#[derive(Debug, PartialEq)] pub enum OpResult { Ok, TerminationRequested, @@ -24,23 +25,80 @@ pub trait Executable: Send { fn periodic_op(&mut self, op_code: i32) -> Result; } -pub fn executable_scheduler< +/// This function allows executing one task which implements the [Executable][Executable] trait +/// # Arguments +/// +/// * `executable`: Executable task +/// * `task_freq`: Optional frequency of task. Required for periodic and fixed cycle tasks +/// * `op_code`: Operation code which is passed to the executable task [operation call][Executable::periodic_op] +/// * `termination`: Optional termination handler which can cancel threads with a broadcast +pub fn exec_sched_single< + T: Executable + Send + 'static + ?Sized, + E: Error + Send + 'static, +>( + mut executable: Box, + task_freq: Option, + op_code: i32, + mut termination: Option>, +) -> JoinHandle> { + let mut cycle_count = 0; + thread::spawn(move || loop { + if let Some(ref mut terminator) = termination { + match terminator.try_recv() { + Ok(_) | Err(TryRecvError::Disconnected) => { + return Ok(OpResult::Ok); + } + Err(TryRecvError::Empty) => (), + } + } + match executable.exec_type() { + ExecutionType::OneShot => { + executable.periodic_op(op_code)?; + return Ok(OpResult::Ok); + } + ExecutionType::Infinite => { + executable.periodic_op(op_code)?; + } + ExecutionType::Cycles(cycles) => { + executable.periodic_op(op_code)?; + cycle_count += 1; + if cycle_count == cycles { + return Ok(OpResult::Ok); + } + } + } + let freq = task_freq.unwrap_or_else(|| panic!("No task frequency specified")); + thread::sleep(freq); + }) +} + +/// This function allows executing multiple tasks as long as the tasks implement the +/// [Executable][Executable] trait +/// # Arguments +/// +/// * `executable_vec`: Vector of executable objects +/// * `task_freq`: Optional frequency of task. Required for periodic and fixed cycle tasks +/// * `op_code`: Operation code which is passed to the executable task periodic_op call +/// * `termination`: Optional termination handler which can cancel threads with a broadcast +pub fn exec_sched_multi< T: Executable + Send + 'static + ?Sized, E: Error + Send + 'static, >( mut executable_vec: Vec>, task_freq: Option, op_code: i32, - mut termination: BusReader<()>, + mut termination: Option>, ) -> JoinHandle> { let mut cycle_counts = vec![0; executable_vec.len()]; let mut removal_flags = vec![false; executable_vec.len()]; thread::spawn(move || loop { - match termination.try_recv() { - Ok(_) | Err(TryRecvError::Disconnected) => { - removal_flags.iter_mut().for_each(|x| *x = true); + if let Some(ref mut terminator) = termination { + match terminator.try_recv() { + Ok(_) | Err(TryRecvError::Disconnected) => { + removal_flags.iter_mut().for_each(|x| *x = true); + } + Err(TryRecvError::Empty) => (), } - Err(TryRecvError::Empty) => (), } for (idx, executable) in executable_vec.iter_mut().enumerate() { match executable.exec_type() { @@ -72,3 +130,226 @@ pub fn executable_scheduler< thread::sleep(freq); }) } + +#[cfg(test)] +mod tests { + use super::{exec_sched_multi, exec_sched_single, Executable, ExecutionType, OpResult}; + use std::error::Error; + use std::fmt; + use std::sync::{Arc, Mutex}; + use std::time::Duration; + + struct TestInfo { + exec_num: u32, + op_code: i32, + } + struct OneShotTask { + exec_num: Arc>, + } + struct FixedCyclesTask { + cycles: u32, + exec_num: Arc>, + } + struct PeriodicTask { + exec_num: Arc>, + } + + #[derive(Debug, PartialEq)] + struct ExampleError { + details: String, + code: i32, + } + + impl ExampleError { + fn new(msg: &str, code: i32) -> ExampleError { + ExampleError { + details: msg.to_string(), + code, + } + } + } + + impl fmt::Display for ExampleError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.details) + } + } + + impl Error for ExampleError { + fn description(&self) -> &str { + &self.details + } + } + + const ONE_SHOT_TASK_NAME: &'static str = "One Shot Task"; + + impl Executable for OneShotTask { + type Error = ExampleError; + + fn exec_type(&self) -> ExecutionType { + ExecutionType::OneShot + } + + fn task_name(&self) -> &'static str { + return ONE_SHOT_TASK_NAME; + } + + fn periodic_op(&mut self, op_code: i32) -> Result { + let mut data = self.exec_num.lock().expect("Locking Mutex failed"); + data.exec_num += 1; + data.op_code = op_code; + std::mem::drop(data); + if op_code >= 0 { + Ok(OpResult::Ok) + } else { + Err(ExampleError::new("One Shot Task Failure", op_code)) + } + } + } + + const CYCLE_TASK_NAME: &'static str = "Fixed Cycles Task"; + + impl Executable for FixedCyclesTask { + type Error = ExampleError; + + fn exec_type(&self) -> ExecutionType { + ExecutionType::Cycles(self.cycles) + } + + fn task_name(&self) -> &'static str { + return CYCLE_TASK_NAME + } + + fn periodic_op(&mut self, op_code: i32) -> Result { + let mut data = self.exec_num.lock().expect("Locking Mutex failed"); + data.exec_num += 1; + data.op_code = op_code; + std::mem::drop(data); + if op_code >= 0 { + Ok(OpResult::Ok) + } else { + Err(ExampleError::new("Fixed Cycle Task Failure", op_code)) + } + } + } + + impl Executable for PeriodicTask { + type Error = ExampleError; + + fn exec_type(&self) -> ExecutionType { + ExecutionType::Infinite + } + + fn task_name(&self) -> &'static str { + "Periodic Task" + } + + fn periodic_op(&mut self, op_code: i32) -> Result { + let mut data = self.exec_num.lock().expect("Locking Mutex failed"); + data.exec_num += 1; + data.op_code = op_code; + std::mem::drop(data); + if op_code >= 0 { + Ok(OpResult::Ok) + } else { + Err(ExampleError::new("Example Task Failure", op_code)) + } + } + } + + #[test] + fn test_simple_one_shot() { + let expected_op_code = 42; + 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)), + expected_op_code, + None, + ); + let thread_res = jhandle.join().expect("One Shot Task failed"); + assert!(thread_res.is_ok()); + assert_eq!(thread_res.unwrap(), OpResult::Ok); + let data = shared.lock().expect("Locking Mutex failed"); + assert_eq!(data.exec_num, 1); + assert_eq!(data.op_code, expected_op_code); + } + + #[test] + fn test_simple_multi_one_shot() { + let expected_op_code = 43; + let shared = Arc::new(Mutex::new(TestInfo { + exec_num: 0, + op_code: 0, + })); + let exec_task_0 = OneShotTask { + exec_num: shared.clone(), + }; + let exec_task_1 = OneShotTask { + exec_num: shared.clone(), + }; + let task_vec = vec![Box::new(exec_task_0), Box::new(exec_task_1)]; + for task in task_vec.iter() { + assert_eq!(task.task_name(), ONE_SHOT_TASK_NAME); + } + let jhandle = exec_sched_multi( + task_vec, + Some(Duration::from_millis(100)), + expected_op_code, + None, + ); + let thread_res = jhandle.join().expect("One Shot Task failed"); + assert!(thread_res.is_ok()); + assert_eq!(thread_res.unwrap(), OpResult::Ok); + let data = shared.lock().expect("Locking Mutex failed"); + assert_eq!(data.exec_num, 2); + assert_eq!(data.op_code, expected_op_code); + } + + #[test] + fn test_cycles() { + let expected_op_code = 44; + 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, + }); + 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 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, 1); + assert_eq!(data.op_code, expected_op_code); + } + + #[test] + #[ignore] + fn test_periodic() { + let expected_op_code = 45; + let shared = Arc::new(Mutex::new(TestInfo { + exec_num: 0, + op_code: 0, + })); + let periodic_task = Box::new(PeriodicTask { + exec_num: shared.clone() + }); + let jh = exec_sched_single(periodic_task, Some(Duration::from_millis(50)), expected_op_code, None); + 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); + assert_eq!(data.op_code, expected_op_code); + } +} diff --git a/src/core/objects.rs b/src/core/objects.rs index dd51ef2..e339358 100644 --- a/src/core/objects.rs +++ b/src/core/objects.rs @@ -124,7 +124,7 @@ mod tests { #[test] fn test_obj_manager_simple() { - let mut obj_manager = ObjectManager::new(); + let mut obj_manager = ObjectManager::default(); let expl_obj_id = ObjectId { id: 0, name: "Example 0", @@ -159,5 +159,17 @@ mod tests { let expl_obj_back_casted = obj_back_casted.unwrap(); assert_eq!(expl_obj_back_casted.string, String::from("Hello Test")); assert!(expl_obj_back_casted.was_initialized); + + let existing_obj_id = ObjectId { + id: 12, + name: "Example 1", + }; + let invalid_obj = OtherExampleObject { + id: existing_obj_id, + string: String::from("Hello Test"), + was_initialized: false, + }; + + assert_eq!(obj_manager.insert(Box::new(invalid_obj)), false); } } diff --git a/src/main.rs b/src/main.rs index f77a51d..7527576 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,162 +1,3 @@ -use bus::{Bus}; - -use launchpad::core::executable::{executable_scheduler, Executable, ExecutionType, OpResult}; -use std::error::Error; -use std::fmt; - -use std::thread; -use std::time::Duration; - -struct OneShotTask {} -struct FixedCyclesTask {} -struct PeriodicTask {} - -#[derive(Debug)] -struct ExampleError { - details: String, -} - -impl ExampleError { - fn new(msg: &str) -> ExampleError { - ExampleError { - details: msg.to_string(), - } - } -} - -impl fmt::Display for ExampleError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.details) - } -} - -impl Error for ExampleError { - fn description(&self) -> &str { - &self.details - } -} - -impl Executable for OneShotTask { - type Error = ExampleError; - - fn exec_type(&self) -> ExecutionType { - ExecutionType::OneShot - } - - fn task_name(&self) -> &'static str { - "One Shot Task" - } - - fn periodic_op(&mut self, op_code: i32) -> Result { - if op_code >= 0 { - println!("One-shot operation with operation code {op_code} OK!"); - Ok(OpResult::Ok) - } else { - println!("One-shot operation failure by passing op code {op_code}!"); - Err(ExampleError::new("Example Task Failure")) - } - } -} - -impl Executable for FixedCyclesTask { - type Error = ExampleError; - - fn exec_type(&self) -> ExecutionType { - ExecutionType::Cycles(3) - } - - fn task_name(&self) -> &'static str { - "Fixed Cycles Task" - } - - fn periodic_op(&mut self, op_code: i32) -> Result { - if op_code >= 0 { - println!("Fixed-cycle operation with operation code {op_code} OK!"); - Ok(OpResult::Ok) - } else { - println!("Fixed-cycle operation failure by passing op code {op_code}!"); - Err(ExampleError::new("Example Task Failure")) - } - } -} - -impl Executable for PeriodicTask { - type Error = ExampleError; - - fn exec_type(&self) -> ExecutionType { - ExecutionType::Infinite - } - - fn task_name(&self) -> &'static str { - "Periodic Task" - } - - fn periodic_op(&mut self, op_code: i32) -> Result { - if op_code >= 0 { - println!("Periodic operation with operation code {op_code} OK!"); - Ok(OpResult::Ok) - } else { - println!("Periodic operation failure by passing op code {op_code}!"); - Err(ExampleError::new("Example Task Failure")) - } - } -} - -fn test0(term_bus: &mut Bus<()>) { - let exec_task = OneShotTask {}; - let task_vec = vec![Box::new(exec_task)]; - let jhandle = executable_scheduler( - task_vec, - Some(Duration::from_millis(100)), - 0, - term_bus.add_rx(), - ); - let exec_task2 = FixedCyclesTask {}; - let task_vec2: Vec + Send>> = - vec![Box::new(exec_task2)]; - let jhandle2 = executable_scheduler( - task_vec2, - Some(Duration::from_millis(100)), - 1, - term_bus.add_rx(), - ); - - jhandle - .join() - .expect("Joining thread failed") - .expect("Task failed"); - jhandle2 - .join() - .expect("Joining thread 2 failed") - .expect("Task 2 failed"); -} - -fn test1(term_bus: &mut Bus<()>) { - let one_shot_in_vec = OneShotTask {}; - let cycles_in_vec = FixedCyclesTask {}; - let periodic_in_vec = PeriodicTask {}; - let test_vec: Vec>> = vec![ - Box::new(one_shot_in_vec), - Box::new(cycles_in_vec), - Box::new(periodic_in_vec), - ]; - let jhandle3 = executable_scheduler( - test_vec, - Some(Duration::from_millis(100)), - 3, - term_bus.add_rx(), - ); - thread::sleep(Duration::from_millis(5000)); - println!("Broadcasting cancel"); - term_bus.broadcast(()); - jhandle3 - .join() - .expect("Joining thread 3 failed") - .expect("Task 3 failed"); -} - fn main() { - let mut tx = Bus::new(5); - test0(&mut tx); - test1(&mut tx); + println!("hello"); } From d2830cb5b62205579de767af34a388002048235c Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 8 Jun 2022 19:43:32 +0200 Subject: [PATCH 10/12] clippy fixes --- src/core/executable.rs | 8 ++++---- src/core/objects.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/executable.rs b/src/core/executable.rs index 5b8193a..b0fdf5a 100644 --- a/src/core/executable.rs +++ b/src/core/executable.rs @@ -181,7 +181,7 @@ mod tests { } } - const ONE_SHOT_TASK_NAME: &'static str = "One Shot Task"; + const ONE_SHOT_TASK_NAME: &str = "One Shot Task"; impl Executable for OneShotTask { type Error = ExampleError; @@ -191,7 +191,7 @@ mod tests { } fn task_name(&self) -> &'static str { - return ONE_SHOT_TASK_NAME; + ONE_SHOT_TASK_NAME } fn periodic_op(&mut self, op_code: i32) -> Result { @@ -207,7 +207,7 @@ mod tests { } } - const CYCLE_TASK_NAME: &'static str = "Fixed Cycles Task"; + const CYCLE_TASK_NAME: &str = "Fixed Cycles Task"; impl Executable for FixedCyclesTask { type Error = ExampleError; @@ -217,7 +217,7 @@ mod tests { } fn task_name(&self) -> &'static str { - return CYCLE_TASK_NAME + CYCLE_TASK_NAME } fn periodic_op(&mut self, op_code: i32) -> Result { diff --git a/src/core/objects.rs b/src/core/objects.rs index e339358..0da10e4 100644 --- a/src/core/objects.rs +++ b/src/core/objects.rs @@ -170,6 +170,6 @@ mod tests { was_initialized: false, }; - assert_eq!(obj_manager.insert(Box::new(invalid_obj)), false); + assert!(!obj_manager.insert(Box::new(invalid_obj))); } } From 0192a705c520f52101ebee39a457d211b1e6d0c1 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 9 Jun 2022 12:49:06 +0200 Subject: [PATCH 11/12] this should be sufficient coverage/testing --- .idea/runConfigurations/Test_All.xml | 18 +++ src/core/executable.rs | 175 ++++++++++++++++++++++++--- 2 files changed, 175 insertions(+), 18 deletions(-) create mode 100644 .idea/runConfigurations/Test_All.xml diff --git a/.idea/runConfigurations/Test_All.xml b/.idea/runConfigurations/Test_All.xml new file mode 100644 index 0000000..6f884df --- /dev/null +++ b/.idea/runConfigurations/Test_All.xml @@ -0,0 +1,18 @@ + + + + \ No newline at end of file diff --git a/src/core/executable.rs b/src/core/executable.rs index b0fdf5a..aca2692 100644 --- a/src/core/executable.rs +++ b/src/core/executable.rs @@ -26,6 +26,7 @@ pub trait Executable: Send { } /// This function allows executing one task which implements the [Executable][Executable] trait +/// /// # Arguments /// /// * `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 /// [Executable][Executable] trait +/// /// # Arguments /// /// * `executable_vec`: Vector of executable objects @@ -134,10 +136,11 @@ pub fn exec_sched_multi< #[cfg(test)] mod tests { use super::{exec_sched_multi, exec_sched_single, Executable, ExecutionType, OpResult}; + use bus::Bus; use std::error::Error; - use std::fmt; use std::sync::{Arc, Mutex}; use std::time::Duration; + use std::{fmt, thread}; struct TestInfo { exec_num: u32, @@ -154,32 +157,41 @@ mod tests { exec_num: Arc>, } - #[derive(Debug, PartialEq)] + #[derive(Clone, Debug)] struct ExampleError { - details: String, - code: i32, + kind: ErrorKind, + } + + /// The kind of an error that can occur. + #[derive(Clone, Debug)] + pub enum ErrorKind { + Generic(String, i32) } impl ExampleError { fn new(msg: &str, code: i32) -> ExampleError { ExampleError { - details: msg.to_string(), - code, + kind: ErrorKind::Generic(msg.to_string(), code) } } + + /// Return the kind of this error. + pub fn kind(&self) -> &ErrorKind { + &self.kind + } } impl fmt::Display for ExampleError { 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 { - fn description(&self) -> &str { - &self.details - } - } + impl Error for ExampleError {} 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 { type Error = ExampleError; @@ -241,7 +255,7 @@ mod tests { } fn task_name(&self) -> &'static str { - "Periodic Task" + PERIODIC_TASK_NAME } fn periodic_op(&mut self, op_code: i32) -> Result { @@ -282,6 +296,41 @@ mod tests { 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] fn test_simple_multi_one_shot() { let expected_op_code = 43; @@ -314,7 +363,7 @@ mod tests { } #[test] - fn test_cycles() { + fn test_cycles_single() { let expected_op_code = 44; let shared = Arc::new(Mutex::new(TestInfo { exec_num: 0, @@ -325,7 +374,12 @@ mod tests { cycles: 1, }); 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"); assert!(thread_res.is_ok()); let data = shared.lock().expect("Locking Mutex failed"); @@ -334,22 +388,107 @@ mod tests { 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>> = 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] #[ignore] - fn test_periodic() { + fn test_periodic_single() { + let mut terminator = Bus::new(5); let expected_op_code = 45; let shared = Arc::new(Mutex::new(TestInfo { exec_num: 0, op_code: 0, })); 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"); assert!(thread_res.is_ok()); let data = shared.lock().expect("Locking Mutex failed"); 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>> = 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); } } From a17b3123a5bb10eb09faa28cd74b82a39f3a7d20 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 9 Jun 2022 12:49:48 +0200 Subject: [PATCH 12/12] cargo fmt --- src/core/executable.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/core/executable.rs b/src/core/executable.rs index aca2692..68935c1 100644 --- a/src/core/executable.rs +++ b/src/core/executable.rs @@ -165,13 +165,13 @@ mod tests { /// The kind of an error that can occur. #[derive(Clone, Debug)] pub enum ErrorKind { - Generic(String, i32) + Generic(String, i32), } impl ExampleError { fn new(msg: &str, code: i32) -> ExampleError { ExampleError { - kind: ErrorKind::Generic(msg.to_string(), code) + kind: ErrorKind::Generic(msg.to_string(), code), } } @@ -408,7 +408,8 @@ mod tests { }); 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>> = vec![one_shot_task, cycled_task_0, cycled_task_1]; + let task_vec: Vec>> = + vec![one_shot_task, cycled_task_0, cycled_task_1]; let jh = exec_sched_multi( task_vec, Some(Duration::from_millis(100)), @@ -474,7 +475,8 @@ mod tests { }); assert_eq!(periodic_task_0.task_name(), PERIODIC_TASK_NAME); assert_eq!(periodic_task_1.task_name(), PERIODIC_TASK_NAME); - let task_vec: Vec>> = vec![cycled_task, periodic_task_0, periodic_task_1]; + let task_vec: Vec>> = + vec![cycled_task, periodic_task_0, periodic_task_1]; let jh = exec_sched_multi( task_vec, Some(Duration::from_millis(20)),