From 7aec5819c6c5e9ef2f604e2f675e62d127917875 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Wed, 6 Dec 2023 16:39:55 +0100 Subject: [PATCH 01/11] rust modules --- mission_rust/CMakeLists.txt | 2 + mission_rust/src/fsrc/mod.rs | 4 + mission_rust/src/fsrc/osal/mod.rs | 25 +++ mission_rust/src/fsrc/queues/mod.rs | 88 +++++++++ mission_rust/src/fsrc/sif.rs | 35 ++++ mission_rust/src/fsrc/tasks/mod.rs | 102 ++++++++++ mission_rust/src/lib.rs | 295 +++------------------------- 7 files changed, 282 insertions(+), 269 deletions(-) create mode 100644 mission_rust/src/fsrc/mod.rs create mode 100644 mission_rust/src/fsrc/osal/mod.rs create mode 100644 mission_rust/src/fsrc/queues/mod.rs create mode 100644 mission_rust/src/fsrc/sif.rs create mode 100644 mission_rust/src/fsrc/tasks/mod.rs diff --git a/mission_rust/CMakeLists.txt b/mission_rust/CMakeLists.txt index 7093c55..217aba8 100644 --- a/mission_rust/CMakeLists.txt +++ b/mission_rust/CMakeLists.txt @@ -1,3 +1,5 @@ +#TODO can we get CMake to configure cmake --build --clean to run cargo clean? + add_custom_target( mission_rust_internal COMMAND cargo build $<$:--release> diff --git a/mission_rust/src/fsrc/mod.rs b/mission_rust/src/fsrc/mod.rs new file mode 100644 index 0000000..1ba25a8 --- /dev/null +++ b/mission_rust/src/fsrc/mod.rs @@ -0,0 +1,4 @@ +pub mod sif; +pub mod queues; +pub mod osal; +pub mod tasks; \ No newline at end of file diff --git a/mission_rust/src/fsrc/osal/mod.rs b/mission_rust/src/fsrc/osal/mod.rs new file mode 100644 index 0000000..e80b241 --- /dev/null +++ b/mission_rust/src/fsrc/osal/mod.rs @@ -0,0 +1,25 @@ +type TaskFunction = unsafe extern "C" fn(*mut cty::c_void); + +extern "C" { + pub fn outbyte(c: cty::c_char); + //void *create_task(TaskFunction_t taskFunction, void *parameter, size_t stack_size) + pub fn create_task( + taskFunction: TaskFunction, + parameter: *const cty::c_void, + stack_size: cty::size_t, + ) -> *const cty::c_void; + + pub fn get_task_name() -> *const core::ffi::c_uchar; + + pub fn stop_it(); + + pub fn delete_task(handle: *const cty::c_void); + + pub fn task_delay(milliseconds: cty::uint32_t); + + //void *create_queue(size_t length, size_t element_size) + pub fn create_queue(length: cty::size_t, element_size: cty::size_t) -> *const cty::c_void; + + pub fn queue_receive(queue: *const cty::c_void, message: *const cty::c_void) -> cty::uint8_t; + pub fn queue_send(queue: *const cty::c_void, message: *const cty::c_void) -> cty::uint8_t; +} \ No newline at end of file diff --git a/mission_rust/src/fsrc/queues/mod.rs b/mission_rust/src/fsrc/queues/mod.rs new file mode 100644 index 0000000..cd78616 --- /dev/null +++ b/mission_rust/src/fsrc/queues/mod.rs @@ -0,0 +1,88 @@ +pub struct MessageQueue { + queue_id: *const cty::c_void, + _unused: Option, //need to constrain the queue to one message type for safety, but compiler needs that to be used +} + +pub struct MessageQueueSender { + queue_id: Option<*const cty::c_void>, + _unused: Option, //need to constrain the sender to one message type for safety, but compiler needs that to be used +} + +impl MessageQueue { + pub fn new(depth: usize) -> Self { + let mut instance: Self; + unsafe { + instance = Self { + queue_id: 0 as *const cty::c_void, + _unused: None, + }; + //TODO check cast of depth + instance.queue_id = crate::fsrc::osal::create_queue(depth, core::mem::size_of::()); + + if instance.queue_id == 0 as *mut cty::c_void { + panic!("could not create Queue"); + } + instance + } + } + + pub fn get_sender(&self) -> MessageQueueSender { + let instance: MessageQueueSender = MessageQueueSender:: { + queue_id: Some(self.queue_id), + _unused: None, + }; + instance + } + + pub fn receive(&self) -> Result { + let mut message: T = T::default(); + let res: cty::uint8_t; + unsafe { + //message = core::mem::MaybeUninit::zeroed().assume_init(); // We only return it if the queue received something + let message_pointer: *mut cty::c_void = &mut message as *mut _ as *mut cty::c_void; + res = crate::fsrc::osal::queue_receive(self.queue_id, message_pointer); + } + if res == 1 { + Ok(message) + } else { + Err(()) + } + } +} + +impl MessageQueueSender { + pub fn new() -> Self { + Self { + queue_id: None, + _unused: None, + } + } + + pub fn send(&self, message: T) -> Result<(), ()> { + let queue_id = self.queue_id.expect("unitialized Message Queue"); + let res: cty::uint8_t; + unsafe { + let message_pointer: *const cty::c_void = &message as *const _ as *const cty::c_void; + res = crate::fsrc::osal::queue_send(queue_id, message_pointer); + } + if res == 1 { + Ok(()) + } else { + Err(()) + } + } +} + +#[derive(Clone, Copy)] +pub struct GenericMessageData { + pub p1: u32, + pub p2: u32, +} + +#[derive(Copy, Clone, Default)] +pub enum Message { + OK, + #[default] + FAILED, + DATA(GenericMessageData), +} \ No newline at end of file diff --git a/mission_rust/src/fsrc/sif.rs b/mission_rust/src/fsrc/sif.rs new file mode 100644 index 0000000..a1801a3 --- /dev/null +++ b/mission_rust/src/fsrc/sif.rs @@ -0,0 +1,35 @@ +pub struct Outbytes {} + +use core::fmt::{Error, Write}; + +impl Write for Outbytes { + fn write_str(&mut self, s: &str) -> Result<(), Error> { + for c in s.as_bytes() { + unsafe { + crate::fsrc::osal::outbyte(*c); + } + } + Ok(()) + } +} + +#[macro_export] +macro_rules! sifln { + ($(,)?) => ( + let mut stdout = Outbytes {}; + writeln!(stdout); + ); + ($($arg:tt)*) => ( + let mut stdout = crate::fsrc::sif::Outbytes {}; + let _alwaysok = writeln!(stdout, $($arg)*); + ); +} + +#[macro_export] +macro_rules! sif { + ($($arg:tt)*) => ( + let mut stdout = crate::fsrc::sif::Outbytes {}; + let _alwaysok = write!(stdout, $($arg)*); + ); +} + diff --git a/mission_rust/src/fsrc/tasks/mod.rs b/mission_rust/src/fsrc/tasks/mod.rs new file mode 100644 index 0000000..878540c --- /dev/null +++ b/mission_rust/src/fsrc/tasks/mod.rs @@ -0,0 +1,102 @@ +#[no_mangle] +extern "C" fn task_entry(task_object: *mut cty::c_void) { + let task: &mut dyn TaskIF; + unsafe { + let pointer = task_object as *mut PeriodicTask; + task = &mut *pointer; + } + task.run(); +} + +pub trait ExecutableObjectIF { + fn perform(&mut self); +} + +pub trait TaskIF { + fn run(&mut self); + fn get_stack_size(&self) -> cty::size_t; + fn set_handle(&mut self, task_handle: *const cty::c_void); + fn get_handle(&self) -> *const cty::c_void; +} + +pub struct PeriodicTask<'a> { + pub stack_size: cty::size_t, //TODO generic type and safety + pub task_handle: *const cty::c_void, + pub period: usize, + pub task_objects: &'a mut [&'a mut dyn ExecutableObjectIF], +} + +impl<'a> PeriodicTask<'a> { + pub fn new( + objects: &'a mut [&'a mut dyn ExecutableObjectIF], + stack_size: usize, + period: usize, + ) -> PeriodicTask<'a> { + let instance: PeriodicTask<'a> = Self { + stack_size: stack_size, + task_handle: 0 as *const cty::c_void, + period: period, + task_objects: objects, + }; + instance + } +} + +impl<'a> TaskIF for PeriodicTask<'a> { + fn run(&mut self) { + loop { + for object in self.task_objects.iter_mut() { + object.perform(); + } + //TODO make this exact + unsafe { + crate::fsrc::osal::task_delay(self.period as cty::uint32_t); //TODO type of delay should be generic but safe (cap to max in C) + } + } + } + fn get_stack_size(&self) -> cty::size_t { + self.stack_size + } + fn set_handle(&mut self, task_handle: *const cty::c_void) { + self.task_handle = task_handle; + } + fn get_handle(&self) -> *const cty::c_void { + self.task_handle + } +} + +pub struct TaskExecutor<'a> { + pub tasks: &'a mut [&'a mut dyn TaskIF], +} + +impl<'a> TaskExecutor<'a> { + pub fn run_tasks(&mut self) { + for task in self.tasks.iter_mut() { + // we give away a raw pointer, to be called by an OS task + // while this is generally very broken, we use a reference tied + // to our own lifetime and destroy the task when we get dropped + // this way, the reference is guaranteed to be valid over our + // lifetime while the task is deleted at the end of our lifetime + let task_pointer: *const cty::c_void = *task as *mut _ as *const cty::c_void; //TODO this does work without the "*" in front of the task -> Why?? + let handle; + unsafe { + handle = crate::fsrc::osal::create_task(task_entry, task_pointer, task.get_stack_size()); + } + if handle == 0 as *mut cty::c_void { + panic!("could not create Task"); + } else { + task.set_handle(handle); + } + } + } +} + +impl<'a> Drop for TaskExecutor<'a> { + fn drop(&mut self) { + for task in self.tasks.iter_mut() { + unsafe { + crate::fsrc::osal::delete_task(task.get_handle()); + } + } + } +} \ No newline at end of file diff --git a/mission_rust/src/lib.rs b/mission_rust/src/lib.rs index 2103732..68bd7d8 100644 --- a/mission_rust/src/lib.rs +++ b/mission_rust/src/lib.rs @@ -4,38 +4,23 @@ //TODO os errors in API calls //TODO look into a pattern for late initialized stuff, currently using Option (can we make it compile time safe?) -#[macro_export] -macro_rules! sifln { - ($(,)?) => ( - let mut stdout = Outbytes {}; - writeln!(stdout); - ); - ($($arg:tt)*) => ( - let mut stdout = Outbytes {}; - let _alwaysok = writeln!(stdout, $($arg)*); - ); -} - -#[macro_export] -macro_rules! sif { - ($($arg:tt)*) => ( - let mut stdout = Outbytes {}; - let _alwaysok = write!(stdout, $($arg)*); - ); -} +mod fsrc; use core::mem::size_of; use core::panic::PanicInfo; +use core::fmt::Write; + +use fsrc::*; #[panic_handler] fn panic(panic: &PanicInfo<'_>) -> ! { unsafe { - stop_it(); + osal::stop_it(); } // TODO: Make this unicode-safe sif!("In Task \""); unsafe { - let task_name = get_task_name(); + let task_name = osal::get_task_name(); let mut offset = 0; while *task_name.offset(offset) != 0 { sif!("{}", *task_name.offset(offset) as char); @@ -48,31 +33,7 @@ fn panic(panic: &PanicInfo<'_>) -> ! { loop {} } -type TaskFunction = unsafe extern "C" fn(*mut cty::c_void); -extern "C" { - fn outbyte(c: cty::c_char); - //void *create_task(TaskFunction_t taskFunction, void *parameter, size_t stack_size) - fn create_task( - taskFunction: TaskFunction, - parameter: *const cty::c_void, - stack_size: cty::size_t, - ) -> *const cty::c_void; - - fn get_task_name() -> *const core::ffi::c_uchar; - - fn stop_it(); - - fn delete_task(handle: *const cty::c_void); - - fn task_delay(milliseconds: cty::uint32_t); - - //void *create_queue(size_t length, size_t element_size) - fn create_queue(length: cty::size_t, element_size: cty::size_t) -> *const cty::c_void; - - fn queue_receive(queue: *const cty::c_void, message: *const cty::c_void) -> cty::uint8_t; - fn queue_send(queue: *const cty::c_void, message: *const cty::c_void) -> cty::uint8_t; -} #[no_mangle] extern "C" fn rust_main() { @@ -81,137 +42,35 @@ extern "C" fn rust_main() { sifln!("Mission done"); } -#[no_mangle] -extern "C" fn task_entry(task_object: *mut cty::c_void) { - let task: &mut dyn TaskIF; - unsafe { - let pointer = task_object as *mut PeriodicTask; - task = &mut *pointer; - } - task.run(); -} - -trait ExecutableObjectIF { - fn perform(&mut self); -} - -trait TaskIF { - fn run(&mut self); - fn get_stack_size(&self) -> cty::size_t; - fn set_handle(&mut self, task_handle: *const cty::c_void); - fn get_handle(&self) -> *const cty::c_void; -} - -struct PeriodicTask<'a> { - stack_size: cty::size_t, //TODO generic type and safety - task_handle: *const cty::c_void, - period: usize, - task_objects: &'a mut [&'a mut dyn ExecutableObjectIF], -} - -impl<'a> PeriodicTask<'a> { - fn new( - objects: &'a mut [&'a mut dyn ExecutableObjectIF], - stack_size: usize, - period: usize, - ) -> PeriodicTask<'a> { - let instance: PeriodicTask<'a> = Self { - stack_size: stack_size, - task_handle: 0 as *const cty::c_void, - period: period, - task_objects: objects, - }; - instance - } -} - -impl<'a> TaskIF for PeriodicTask<'a> { - fn run(&mut self) { - loop { - for object in self.task_objects.iter_mut() { - object.perform(); - } - //TODO make this exact - unsafe { - task_delay(self.period as cty::uint32_t); //TODO type of delay should be generic but safe (cap to max in C) - } - } - } - fn get_stack_size(&self) -> cty::size_t { - self.stack_size - } - fn set_handle(&mut self, task_handle: *const cty::c_void) { - self.task_handle = task_handle; - } - fn get_handle(&self) -> *const cty::c_void { - self.task_handle - } -} - -struct TaskExecutor<'a> { - tasks: &'a mut [&'a mut dyn TaskIF], -} - -impl<'a> TaskExecutor<'a> { - fn run_tasks(&mut self) { - for task in self.tasks.iter_mut() { - // we give away a raw pointer, to be called by an OS task - // while this is generally very broken, we use a reference tied - // to our own lifetime and destroy the task when we get dropped - // this way, the reference is guaranteed to be valid over our - // lifetime while the task is deleted at the end of our lifetime - let task_pointer: *const cty::c_void = *task as *mut _ as *const cty::c_void; //TODO this does work without the "*" in front of the task -> Why?? - let handle; - unsafe { - handle = create_task(task_entry, task_pointer, task.get_stack_size()); - } - if handle == 0 as *mut cty::c_void { - panic!("could not create Task"); - } else { - task.set_handle(handle); - } - } - } -} - -impl<'a> Drop for TaskExecutor<'a> { - fn drop(&mut self) { - for task in self.tasks.iter_mut() { - unsafe { - delete_task(task.get_handle()); - } - } - } -} struct Handler { id: u32, - command_queue: MessageQueue, + command_queue: queues::MessageQueue, } struct HandlerSender { id: u32, cycle: u8, - other_handler: MessageQueueSender, + other_handler: queues::MessageQueueSender, } impl Handler { - fn handle_message(&self, message: Message) { + fn handle_message(&self, message: queues::Message) { match message { - Message::OK => { + queues::Message::OK => { sifln!("OK"); } - Message::FAILED => { + queues::Message::FAILED => { sifln!("FAILED"); } - Message::DATA(data) => { + queues::Message::DATA(data) => { sifln!("p1: {}, p2 {}", data.p1, data.p2); } } } } -impl ExecutableObjectIF for Handler { +impl tasks::ExecutableObjectIF for Handler { fn perform(&mut self) { sifln!("Handler {} performs", self.id); let result = self.command_queue.receive(); @@ -224,20 +83,20 @@ impl ExecutableObjectIF for Handler { } } -impl ExecutableObjectIF for HandlerSender { +impl tasks::ExecutableObjectIF for HandlerSender { fn perform(&mut self) { sifln!("HandlerSender {} performs step {}", self.id, self.cycle); match self.cycle { 0 => { - let _ = self.other_handler.send(Message::OK); + let _ = self.other_handler.send(queues::Message::OK); } 1 => { - let _ = self.other_handler.send(Message::FAILED); + let _ = self.other_handler.send(queues::Message::FAILED); } 2 => { let _ = self .other_handler - .send(Message::DATA(GenericMessageData { p1: 13, p2: 2 })); + .send(queues::Message::DATA(queues::GenericMessageData { p1: 13, p2: 2 })); } _ => (), } @@ -245,129 +104,27 @@ impl ExecutableObjectIF for HandlerSender { } } -struct MessageQueue { - queue_id: *const cty::c_void, - _unused: Option, //need to constrain the queue to one message type for safety, but compiler needs that to be used -} - -struct MessageQueueSender { - queue_id: Option<*const cty::c_void>, - _unused: Option, //need to constrain the sender to one message type for safety, but compiler needs that to be used -} - -impl MessageQueue { - fn new(depth: usize) -> Self { - let mut instance: Self; - unsafe { - instance = Self { - queue_id: 0 as *const cty::c_void, - _unused: None, - }; - //TODO check cast of depth - instance.queue_id = create_queue(depth, core::mem::size_of::()); - - if instance.queue_id == 0 as *mut cty::c_void { - panic!("could not create Queue"); - } - instance - } - } - - fn get_sender(&self) -> MessageQueueSender { - let instance: MessageQueueSender = MessageQueueSender:: { - queue_id: Some(self.queue_id), - _unused: None, - }; - instance - } - - fn receive(&self) -> Result { - let mut message: T = T::default(); - let res: cty::uint8_t; - unsafe { - //message = core::mem::MaybeUninit::zeroed().assume_init(); // We only return it if the queue received something - let message_pointer: *mut cty::c_void = &mut message as *mut _ as *mut cty::c_void; - res = queue_receive(self.queue_id, message_pointer); - } - if res == 1 { - Ok(message) - } else { - Err(()) - } - } -} - -impl MessageQueueSender { - fn new() -> Self { - Self { - queue_id: None, - _unused: None, - } - } - - fn send(&self, message: T) -> Result<(), ()> { - let queue_id = self.queue_id.expect("unitialized Message Queue"); - let res: cty::uint8_t; - unsafe { - let message_pointer: *const cty::c_void = &message as *const _ as *const cty::c_void; - res = queue_send(queue_id, message_pointer); - } - if res == 1 { - Ok(()) - } else { - Err(()) - } - } -} - -#[derive(Clone, Copy)] -struct GenericMessageData { - p1: u32, - p2: u32, -} - -#[derive(Copy, Clone, Default)] -enum Message { - OK, - #[default] - FAILED, - DATA(GenericMessageData), -} - -struct Outbytes {} - -use core::fmt::{Error, Write}; -impl Write for Outbytes { - fn write_str(&mut self, s: &str) -> Result<(), Error> { - for c in s.as_bytes() { - unsafe { - outbyte(*c); - } - } - Ok(()) - } -} fn mission() { sifln!("Mission enter"); let mut h1 = Handler { id: 1, - command_queue: MessageQueue::new(5), + command_queue: queues::MessageQueue::new(5), }; let mut h2 = HandlerSender { id: 2, cycle: 0, - other_handler: MessageQueueSender::::new(), + other_handler: queues::MessageQueueSender::::new(), }; h2.other_handler = h1.command_queue.get_sender(); - let array: &mut [&mut dyn ExecutableObjectIF] = &mut [&mut h1]; + let array: &mut [&mut dyn tasks::ExecutableObjectIF] = &mut [&mut h1]; - let mut t1 = PeriodicTask::new(array, 512, 200); + let mut t1 = tasks::PeriodicTask::new(array, 512, 200); - let mut t2: PeriodicTask = PeriodicTask { + let mut t2: tasks::PeriodicTask = tasks::PeriodicTask { task_objects: &mut [&mut h2], stack_size: 512, period: 400, @@ -376,9 +133,9 @@ fn mission() { let _i = 1; - sifln!("sizeof {}, pointer struct {:p}, pointer element {:p}, next element{:p}", size_of::(), &t2, t2.task_objects, &_i); + sifln!("sizeof {}, pointer struct {:p}, pointer element {:p}, next element{:p}", size_of::(), &t2, t2.task_objects, &_i); - let mut task_executor = TaskExecutor { + let mut task_executor = tasks::TaskExecutor { tasks: &mut [&mut t1, &mut t2], }; @@ -388,12 +145,12 @@ fn mission() { sifln!("Mission delay"); unsafe { - task_delay(2000); + osal::task_delay(2000); } sifln!("executor dropped"); drop(task_executor); unsafe { - task_delay(2000); + osal::task_delay(2000); } sifln!("Mission delay done"); } From a86791b722749436a380906251c940acbd947185 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Wed, 6 Dec 2023 17:34:57 +0100 Subject: [PATCH 02/11] typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f23f88f..d6e188e 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ It will exit after the Zynq is configured and ready to firmware. Then load the actual obsw, in the build (`build_cli` in the example above) folder run: ```sh -arm-none-eabi-gdb romeo-obws +arm-none-eabi-gdb romeo-obsw >target extended-remote localhost:3333 >load >cont From 29f1383697482ac2fe7269d77cd98b4ce87d13d6 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Wed, 6 Dec 2023 23:12:00 +0100 Subject: [PATCH 03/11] first version of object initialization (I hate (aka do not understand) lifetimes) --- mission_rust/src/fsrc/mod.rs | 3 +- mission_rust/src/fsrc/objectmanager.rs | 12 +++++ mission_rust/src/fsrc/queues/mod.rs | 29 +++++------ mission_rust/src/fsrc/tasks/mod.rs | 61 ++++++++++++++++++++--- mission_rust/src/lib.rs | 67 +++++++++++++++++--------- 5 files changed, 123 insertions(+), 49 deletions(-) create mode 100644 mission_rust/src/fsrc/objectmanager.rs diff --git a/mission_rust/src/fsrc/mod.rs b/mission_rust/src/fsrc/mod.rs index 1ba25a8..22b2765 100644 --- a/mission_rust/src/fsrc/mod.rs +++ b/mission_rust/src/fsrc/mod.rs @@ -1,4 +1,5 @@ pub mod sif; pub mod queues; pub mod osal; -pub mod tasks; \ No newline at end of file +pub mod tasks; +pub mod objectmanager; \ No newline at end of file diff --git a/mission_rust/src/fsrc/objectmanager.rs b/mission_rust/src/fsrc/objectmanager.rs new file mode 100644 index 0000000..50b2d8e --- /dev/null +++ b/mission_rust/src/fsrc/objectmanager.rs @@ -0,0 +1,12 @@ +use super::tasks::ExecutableObjectIF; + +pub type ObjectId = usize; +pub trait ObjectManager<'a> { + fn get_object(&self, id: ObjectId) -> Option<&'a dyn SystemObject>; +} + +pub trait SystemObject: ExecutableObjectIF { + fn get_id(&self) -> ObjectId; + fn initialize(&mut self, object_manager: &dyn ObjectManager) -> Result<(),()>; + fn get_command_queue(&self) -> crate::fsrc::queues::MessageQueueSender; +} \ No newline at end of file diff --git a/mission_rust/src/fsrc/queues/mod.rs b/mission_rust/src/fsrc/queues/mod.rs index cd78616..950a567 100644 --- a/mission_rust/src/fsrc/queues/mod.rs +++ b/mission_rust/src/fsrc/queues/mod.rs @@ -1,23 +1,20 @@ -pub struct MessageQueue { +pub struct MessageQueue { queue_id: *const cty::c_void, - _unused: Option, //need to constrain the queue to one message type for safety, but compiler needs that to be used } -pub struct MessageQueueSender { +pub struct MessageQueueSender { queue_id: Option<*const cty::c_void>, - _unused: Option, //need to constrain the sender to one message type for safety, but compiler needs that to be used } -impl MessageQueue { +impl MessageQueue { pub fn new(depth: usize) -> Self { let mut instance: Self; unsafe { instance = Self { queue_id: 0 as *const cty::c_void, - _unused: None, - }; + }; //TODO check cast of depth - instance.queue_id = crate::fsrc::osal::create_queue(depth, core::mem::size_of::()); + instance.queue_id = crate::fsrc::osal::create_queue(depth, core::mem::size_of::()); if instance.queue_id == 0 as *mut cty::c_void { panic!("could not create Queue"); @@ -26,16 +23,15 @@ impl MessageQueue { } } - pub fn get_sender(&self) -> MessageQueueSender { - let instance: MessageQueueSender = MessageQueueSender:: { + pub fn get_sender(&self) -> MessageQueueSender { + let instance: MessageQueueSender = MessageQueueSender { queue_id: Some(self.queue_id), - _unused: None, }; instance } - pub fn receive(&self) -> Result { - let mut message: T = T::default(); + pub fn receive(&self) -> Result { + let mut message: Message = Message::default(); let res: cty::uint8_t; unsafe { //message = core::mem::MaybeUninit::zeroed().assume_init(); // We only return it if the queue received something @@ -50,15 +46,14 @@ impl MessageQueue { } } -impl MessageQueueSender { +impl MessageQueueSender { pub fn new() -> Self { Self { queue_id: None, - _unused: None, - } + } } - pub fn send(&self, message: T) -> Result<(), ()> { + pub fn send(&self, message: Message) -> Result<(), ()> { let queue_id = self.queue_id.expect("unitialized Message Queue"); let res: cty::uint8_t; unsafe { diff --git a/mission_rust/src/fsrc/tasks/mod.rs b/mission_rust/src/fsrc/tasks/mod.rs index 878540c..d3460e3 100644 --- a/mission_rust/src/fsrc/tasks/mod.rs +++ b/mission_rust/src/fsrc/tasks/mod.rs @@ -1,3 +1,7 @@ +use core::slice; + +use super::objectmanager::ObjectManager; + #[no_mangle] extern "C" fn task_entry(task_object: *mut cty::c_void) { let task: &mut dyn TaskIF; @@ -12,23 +16,25 @@ pub trait ExecutableObjectIF { fn perform(&mut self); } -pub trait TaskIF { +pub trait TaskIF<'a> { fn run(&mut self); fn get_stack_size(&self) -> cty::size_t; fn set_handle(&mut self, task_handle: *const cty::c_void); fn get_handle(&self) -> *const cty::c_void; + fn get_objects(&'a self) -> &'a [&'a mut dyn crate::objectmanager::SystemObject]; + fn initialize(&mut self, object_manager: &dyn ObjectManager) -> Result<(), ()>; } pub struct PeriodicTask<'a> { pub stack_size: cty::size_t, //TODO generic type and safety pub task_handle: *const cty::c_void, pub period: usize, - pub task_objects: &'a mut [&'a mut dyn ExecutableObjectIF], + pub task_objects: &'a mut [&'a mut dyn crate::objectmanager::SystemObject], } impl<'a> PeriodicTask<'a> { pub fn new( - objects: &'a mut [&'a mut dyn ExecutableObjectIF], + objects: &'a mut [&'a mut dyn crate::objectmanager::SystemObject], stack_size: usize, period: usize, ) -> PeriodicTask<'a> { @@ -42,7 +48,7 @@ impl<'a> PeriodicTask<'a> { } } -impl<'a> TaskIF for PeriodicTask<'a> { +impl<'a> TaskIF<'a> for PeriodicTask<'a> { fn run(&mut self) { loop { for object in self.task_objects.iter_mut() { @@ -63,14 +69,34 @@ impl<'a> TaskIF for PeriodicTask<'a> { fn get_handle(&self) -> *const cty::c_void { self.task_handle } + fn get_objects(&'a self) -> &'a [&'a mut dyn crate::objectmanager::SystemObject] { + self.task_objects + } + fn initialize(&mut self, object_manager: &dyn ObjectManager) -> Result<(), ()> { + for object in self.task_objects.iter_mut() { + let result = object.initialize(object_manager); + match result { + Ok(()) => continue, + Err(()) => return Err(()), + } + } + Ok(()) + } } pub struct TaskExecutor<'a> { - pub tasks: &'a mut [&'a mut dyn TaskIF], + pub tasks: &'a mut [&'a mut dyn TaskIF<'a>], } impl<'a> TaskExecutor<'a> { pub fn run_tasks(&mut self) { + let object_manager = TaskObjectManager { + tasks: unsafe { slice::from_raw_parts(self.tasks.as_ptr(), self.tasks.len()) }, + }; + for task in self.tasks.iter_mut() { + let _ = task.initialize(&object_manager); + } + drop(object_manager); for task in self.tasks.iter_mut() { // we give away a raw pointer, to be called by an OS task // while this is generally very broken, we use a reference tied @@ -80,7 +106,8 @@ impl<'a> TaskExecutor<'a> { let task_pointer: *const cty::c_void = *task as *mut _ as *const cty::c_void; //TODO this does work without the "*" in front of the task -> Why?? let handle; unsafe { - handle = crate::fsrc::osal::create_task(task_entry, task_pointer, task.get_stack_size()); + handle = + crate::fsrc::osal::create_task(task_entry, task_pointer, task.get_stack_size()); } if handle == 0 as *mut cty::c_void { panic!("could not create Task"); @@ -91,6 +118,26 @@ impl<'a> TaskExecutor<'a> { } } +struct TaskObjectManager<'a> { + tasks: &'a [&'a mut dyn TaskIF<'a>], +} + +impl<'a> crate::objectmanager::ObjectManager<'a> for TaskObjectManager<'a> { + fn get_object( + &self, + id: crate::objectmanager::ObjectId, + ) -> Option<&'a dyn crate::objectmanager::SystemObject> { + for task in self.tasks.iter() { + for object in task.get_objects().iter() { + if object.get_id() == id { + return Some(*object); + } + } + } + None + } +} + impl<'a> Drop for TaskExecutor<'a> { fn drop(&mut self) { for task in self.tasks.iter_mut() { @@ -99,4 +146,4 @@ impl<'a> Drop for TaskExecutor<'a> { } } } -} \ No newline at end of file +} diff --git a/mission_rust/src/lib.rs b/mission_rust/src/lib.rs index 68bd7d8..ae16d89 100644 --- a/mission_rust/src/lib.rs +++ b/mission_rust/src/lib.rs @@ -6,10 +6,10 @@ mod fsrc; -use core::mem::size_of; -use core::panic::PanicInfo; use core::fmt::Write; +use core::panic::PanicInfo; +use fsrc::objectmanager::SystemObject; use fsrc::*; #[panic_handler] @@ -33,8 +33,6 @@ fn panic(panic: &PanicInfo<'_>) -> ! { loop {} } - - #[no_mangle] extern "C" fn rust_main() { sifln!("Rust startup 🚀"); @@ -42,16 +40,16 @@ extern "C" fn rust_main() { sifln!("Mission done"); } - struct Handler { - id: u32, - command_queue: queues::MessageQueue, + id: objectmanager::ObjectId, + command_queue: queues::MessageQueue, } struct HandlerSender { - id: u32, + id: objectmanager::ObjectId, + other_handler: objectmanager::ObjectId, cycle: u8, - other_handler: queues::MessageQueueSender, + other_handler_queue: queues::MessageQueueSender, } impl Handler { @@ -88,15 +86,15 @@ impl tasks::ExecutableObjectIF for HandlerSender { sifln!("HandlerSender {} performs step {}", self.id, self.cycle); match self.cycle { 0 => { - let _ = self.other_handler.send(queues::Message::OK); + let _ = self.other_handler_queue.send(queues::Message::OK); } 1 => { - let _ = self.other_handler.send(queues::Message::FAILED); + let _ = self.other_handler_queue.send(queues::Message::FAILED); } 2 => { - let _ = self - .other_handler - .send(queues::Message::DATA(queues::GenericMessageData { p1: 13, p2: 2 })); + let _ = self.other_handler_queue.send(queues::Message::DATA( + queues::GenericMessageData { p1: 13, p2: 2 }, + )); } _ => (), } @@ -104,6 +102,34 @@ impl tasks::ExecutableObjectIF for HandlerSender { } } +impl SystemObject for Handler { + fn get_command_queue(&self) -> crate::fsrc::queues::MessageQueueSender { + self.command_queue.get_sender() + } + fn get_id(&self) -> objectmanager::ObjectId { + self.id + } + fn initialize(&mut self, _object_manager: &dyn objectmanager::ObjectManager) -> Result<(), ()> { + Ok(()) + } +} + +impl SystemObject for HandlerSender { + fn get_command_queue(&self) -> crate::fsrc::queues::MessageQueueSender { + queues::MessageQueueSender::new() + } + fn get_id(&self) -> objectmanager::ObjectId { + self.id + } + fn initialize(&mut self, object_manager: &dyn objectmanager::ObjectManager) -> Result<(), ()> { + let other_handler = object_manager.get_object(self.other_handler); + match other_handler { + Some(other) => self.other_handler_queue = other.get_command_queue(), + None => return Err(()), + } + Ok(()) + } +} fn mission() { sifln!("Mission enter"); @@ -114,13 +140,12 @@ fn mission() { }; let mut h2 = HandlerSender { id: 2, + other_handler: 1, cycle: 0, - other_handler: queues::MessageQueueSender::::new(), + other_handler_queue: queues::MessageQueueSender::new(), }; - h2.other_handler = h1.command_queue.get_sender(); - - let array: &mut [&mut dyn tasks::ExecutableObjectIF] = &mut [&mut h1]; + let array: &mut [&mut dyn objectmanager::SystemObject] = &mut [&mut h1]; let mut t1 = tasks::PeriodicTask::new(array, 512, 200); @@ -131,16 +156,10 @@ fn mission() { task_handle: 0 as *const cty::c_void, }; - let _i = 1; - - sifln!("sizeof {}, pointer struct {:p}, pointer element {:p}, next element{:p}", size_of::(), &t2, t2.task_objects, &_i); - let mut task_executor = tasks::TaskExecutor { tasks: &mut [&mut t1, &mut t2], }; - sifln!("{:p}", task_executor.tasks[0]); - task_executor.run_tasks(); sifln!("Mission delay"); From efc752637e2baa0c509e81bcad5e15f156eae67c Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Fri, 8 Dec 2023 00:48:32 +0100 Subject: [PATCH 04/11] cosmetics --- mission_rust/src/fsrc/objectmanager.rs | 12 ++++----- mission_rust/src/fsrc/osal/mod.rs | 2 +- mission_rust/src/fsrc/tasks/mod.rs | 18 ++++++------- mission_rust/src/lib.rs | 35 ++++++++++++++++---------- 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/mission_rust/src/fsrc/objectmanager.rs b/mission_rust/src/fsrc/objectmanager.rs index 50b2d8e..2071a96 100644 --- a/mission_rust/src/fsrc/objectmanager.rs +++ b/mission_rust/src/fsrc/objectmanager.rs @@ -1,12 +1,12 @@ -use super::tasks::ExecutableObjectIF; +use super::*; pub type ObjectId = usize; pub trait ObjectManager<'a> { - fn get_object(&self, id: ObjectId) -> Option<&'a dyn SystemObject>; + fn get_object(&self, id: ObjectId) -> Result<&'a dyn SystemObjectIF, ()>; } -pub trait SystemObject: ExecutableObjectIF { +pub trait SystemObjectIF: tasks::ExecutableObjectIF { fn get_id(&self) -> ObjectId; - fn initialize(&mut self, object_manager: &dyn ObjectManager) -> Result<(),()>; - fn get_command_queue(&self) -> crate::fsrc::queues::MessageQueueSender; -} \ No newline at end of file + fn initialize(&mut self, object_manager: &dyn ObjectManager) -> Result<(), ()>; + fn get_command_queue(&self) -> queues::MessageQueueSender; +} diff --git a/mission_rust/src/fsrc/osal/mod.rs b/mission_rust/src/fsrc/osal/mod.rs index e80b241..241de2e 100644 --- a/mission_rust/src/fsrc/osal/mod.rs +++ b/mission_rust/src/fsrc/osal/mod.rs @@ -9,7 +9,7 @@ extern "C" { stack_size: cty::size_t, ) -> *const cty::c_void; - pub fn get_task_name() -> *const core::ffi::c_uchar; + pub fn get_task_name() -> *const core::ffi::c_char; pub fn stop_it(); diff --git a/mission_rust/src/fsrc/tasks/mod.rs b/mission_rust/src/fsrc/tasks/mod.rs index d3460e3..9251c73 100644 --- a/mission_rust/src/fsrc/tasks/mod.rs +++ b/mission_rust/src/fsrc/tasks/mod.rs @@ -21,7 +21,7 @@ pub trait TaskIF<'a> { fn get_stack_size(&self) -> cty::size_t; fn set_handle(&mut self, task_handle: *const cty::c_void); fn get_handle(&self) -> *const cty::c_void; - fn get_objects(&'a self) -> &'a [&'a mut dyn crate::objectmanager::SystemObject]; + fn get_objects(&'a self) -> &'a [&'a mut dyn crate::objectmanager::SystemObjectIF]; fn initialize(&mut self, object_manager: &dyn ObjectManager) -> Result<(), ()>; } @@ -29,12 +29,12 @@ pub struct PeriodicTask<'a> { pub stack_size: cty::size_t, //TODO generic type and safety pub task_handle: *const cty::c_void, pub period: usize, - pub task_objects: &'a mut [&'a mut dyn crate::objectmanager::SystemObject], + pub task_objects: &'a mut [&'a mut dyn crate::objectmanager::SystemObjectIF], } impl<'a> PeriodicTask<'a> { pub fn new( - objects: &'a mut [&'a mut dyn crate::objectmanager::SystemObject], + objects: &'a mut [&'a mut dyn crate::objectmanager::SystemObjectIF], stack_size: usize, period: usize, ) -> PeriodicTask<'a> { @@ -69,7 +69,7 @@ impl<'a> TaskIF<'a> for PeriodicTask<'a> { fn get_handle(&self) -> *const cty::c_void { self.task_handle } - fn get_objects(&'a self) -> &'a [&'a mut dyn crate::objectmanager::SystemObject] { + fn get_objects(&'a self) -> &'a [&'a mut dyn crate::objectmanager::SystemObjectIF] { self.task_objects } fn initialize(&mut self, object_manager: &dyn ObjectManager) -> Result<(), ()> { @@ -89,12 +89,12 @@ pub struct TaskExecutor<'a> { } impl<'a> TaskExecutor<'a> { - pub fn run_tasks(&mut self) { + pub fn init_and_run(&mut self) { let object_manager = TaskObjectManager { tasks: unsafe { slice::from_raw_parts(self.tasks.as_ptr(), self.tasks.len()) }, }; for task in self.tasks.iter_mut() { - let _ = task.initialize(&object_manager); + let _ = task.initialize(&object_manager).unwrap(); } drop(object_manager); for task in self.tasks.iter_mut() { @@ -126,15 +126,15 @@ impl<'a> crate::objectmanager::ObjectManager<'a> for TaskObjectManager<'a> { fn get_object( &self, id: crate::objectmanager::ObjectId, - ) -> Option<&'a dyn crate::objectmanager::SystemObject> { + ) -> Result<&'a dyn crate::objectmanager::SystemObjectIF, ()> { for task in self.tasks.iter() { for object in task.get_objects().iter() { if object.get_id() == id { - return Some(*object); + return Ok(*object); } } } - None + Err(()) } } diff --git a/mission_rust/src/lib.rs b/mission_rust/src/lib.rs index ae16d89..0a537a7 100644 --- a/mission_rust/src/lib.rs +++ b/mission_rust/src/lib.rs @@ -9,9 +9,13 @@ mod fsrc; use core::fmt::Write; use core::panic::PanicInfo; -use fsrc::objectmanager::SystemObject; +use fsrc::objectmanager::SystemObjectIF; use fsrc::*; +extern "C" { + fn done(); +} + #[panic_handler] fn panic(panic: &PanicInfo<'_>) -> ! { unsafe { @@ -20,16 +24,21 @@ fn panic(panic: &PanicInfo<'_>) -> ! { // TODO: Make this unicode-safe sif!("In Task \""); unsafe { - let task_name = osal::get_task_name(); - let mut offset = 0; - while *task_name.offset(offset) != 0 { - sif!("{}", *task_name.offset(offset) as char); - offset = offset + 1; + let task_name = core::ffi::CStr::from_ptr(osal::get_task_name()); + let task_name_utf8 = core::str::from_utf8(task_name.to_bytes()); + match task_name_utf8 { + Ok(string) => { + sif!("{}", string); + } + Err(_) => { + sif!("Schei� Encoding"); + } } } sifln!("\":"); sifln!("{}", panic); //TODO: stop RTOS, exit if hosted + unsafe { done() }; loop {} } @@ -102,7 +111,7 @@ impl tasks::ExecutableObjectIF for HandlerSender { } } -impl SystemObject for Handler { +impl SystemObjectIF for Handler { fn get_command_queue(&self) -> crate::fsrc::queues::MessageQueueSender { self.command_queue.get_sender() } @@ -114,7 +123,7 @@ impl SystemObject for Handler { } } -impl SystemObject for HandlerSender { +impl SystemObjectIF for HandlerSender { fn get_command_queue(&self) -> crate::fsrc::queues::MessageQueueSender { queues::MessageQueueSender::new() } @@ -124,8 +133,8 @@ impl SystemObject for HandlerSender { fn initialize(&mut self, object_manager: &dyn objectmanager::ObjectManager) -> Result<(), ()> { let other_handler = object_manager.get_object(self.other_handler); match other_handler { - Some(other) => self.other_handler_queue = other.get_command_queue(), - None => return Err(()), + Ok(other) => self.other_handler_queue = other.get_command_queue(), + Err(_) => return Err(()), } Ok(()) } @@ -140,12 +149,12 @@ fn mission() { }; let mut h2 = HandlerSender { id: 2, - other_handler: 1, + other_handler: 3, cycle: 0, other_handler_queue: queues::MessageQueueSender::new(), }; - let array: &mut [&mut dyn objectmanager::SystemObject] = &mut [&mut h1]; + let array: &mut [&mut dyn objectmanager::SystemObjectIF] = &mut [&mut h1]; let mut t1 = tasks::PeriodicTask::new(array, 512, 200); @@ -160,7 +169,7 @@ fn mission() { tasks: &mut [&mut t1, &mut t2], }; - task_executor.run_tasks(); + task_executor.init_and_run(); sifln!("Mission delay"); unsafe { From 59f4a82da9b72bcee28fd84da41ce7f85e26324c Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Fri, 8 Dec 2023 22:21:49 +0100 Subject: [PATCH 05/11] working on datasets, stopped by "trait upcasting coercion is experimental" --- mission_rust/src/fsrc/datasets/mod.rs | 95 ++++++++++++++++++++++++++ mission_rust/src/fsrc/mod.rs | 3 +- mission_rust/src/fsrc/objectmanager.rs | 2 +- mission_rust/src/lib.rs | 37 ++++++++-- 4 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 mission_rust/src/fsrc/datasets/mod.rs diff --git a/mission_rust/src/fsrc/datasets/mod.rs b/mission_rust/src/fsrc/datasets/mod.rs new file mode 100644 index 0000000..dea8b6c --- /dev/null +++ b/mission_rust/src/fsrc/datasets/mod.rs @@ -0,0 +1,95 @@ +#[derive(Copy, Clone, PartialEq)] +pub struct TypeId { + id: usize, +} + +// inspired by https://github.com/jswrenn/deflect/ +pub trait Reflection { + #[inline(never)] + fn get_type_id(&self) -> TypeId { + TypeId { + id: ::get_type_id as usize, + } + } +} + +pub trait DatapoolOwnerIF { + fn get_set(&self, type_id: TypeId) -> Option<&dyn DataSetIF> { + None + } +} + +pub trait DataSetIF { + fn get_type_id(&self) -> TypeId; + fn get_actual_data(&self) -> &dyn Reflection; + //fn get_mutex_handle(&self) -> TODO +} + +pub struct OwnedDataset { + actual_data: T, +} + +pub struct ReferencedDataset { + //we use a pointer here to avoid lifetimes + actual_data: Option<*const T>, +} + +impl DataSetIF for OwnedDataset { + fn get_type_id(&self) -> TypeId { + self.actual_data.get_type_id() + } + + fn get_actual_data(&self) -> &dyn Reflection { + &self.actual_data + } +} + +impl OwnedDataset { + pub fn new() -> OwnedDataset { + OwnedDataset:: { + actual_data: T::default(), + } + } + + pub fn read(&mut self) -> T { + //mutex + self.actual_data + } + + pub fn commit(&mut self, data: T) { + //mutex + self.actual_data = data; + } +} + +impl ReferencedDataset { + pub fn new() -> ReferencedDataset { + ReferencedDataset:: { actual_data: None } + } + + pub fn read(&mut self) -> Result { + //mutex + match self.actual_data { + None => Err(()), + Some(data) => Ok(unsafe { *data }), + } + } + + pub fn initialize(&mut self, owner: &dyn DatapoolOwnerIF) -> Result<(), ()> { + let temp: T = T::default(); //TODO find nicer solution whithout local instance and trait bound to Default + let type_id = temp.get_type_id(); + let other_set: &dyn DataSetIF; + match owner.get_set(type_id) { + None => { + return Err(()); + } + Some(set) => { + other_set = set; + } + } + //pointer cast is safe because we checked the type_id + self.actual_data = Some(other_set.get_actual_data() as *const dyn Reflection as *const T); + //self.mutex = other.mutex + Ok(()) + } +} diff --git a/mission_rust/src/fsrc/mod.rs b/mission_rust/src/fsrc/mod.rs index 22b2765..cc5c015 100644 --- a/mission_rust/src/fsrc/mod.rs +++ b/mission_rust/src/fsrc/mod.rs @@ -2,4 +2,5 @@ pub mod sif; pub mod queues; pub mod osal; pub mod tasks; -pub mod objectmanager; \ No newline at end of file +pub mod objectmanager; +pub mod datasets; \ No newline at end of file diff --git a/mission_rust/src/fsrc/objectmanager.rs b/mission_rust/src/fsrc/objectmanager.rs index 2071a96..36e6cc7 100644 --- a/mission_rust/src/fsrc/objectmanager.rs +++ b/mission_rust/src/fsrc/objectmanager.rs @@ -5,7 +5,7 @@ pub trait ObjectManager<'a> { fn get_object(&self, id: ObjectId) -> Result<&'a dyn SystemObjectIF, ()>; } -pub trait SystemObjectIF: tasks::ExecutableObjectIF { +pub trait SystemObjectIF: tasks::ExecutableObjectIF + datasets::DatapoolOwnerIF { fn get_id(&self) -> ObjectId; fn initialize(&mut self, object_manager: &dyn ObjectManager) -> Result<(), ()>; fn get_command_queue(&self) -> queues::MessageQueueSender; diff --git a/mission_rust/src/lib.rs b/mission_rust/src/lib.rs index 0a537a7..5a8054f 100644 --- a/mission_rust/src/lib.rs +++ b/mission_rust/src/lib.rs @@ -9,6 +9,7 @@ mod fsrc; use core::fmt::Write; use core::panic::PanicInfo; +use fsrc::datasets::{DataSetIF, DatapoolOwnerIF}; use fsrc::objectmanager::SystemObjectIF; use fsrc::*; @@ -49,9 +50,18 @@ extern "C" fn rust_main() { sifln!("Mission done"); } +#[derive (Copy, Clone, Default)] +struct HandlerData { + x: u32, + y: f32 +} + +impl datasets::Reflection for HandlerData {} + struct Handler { id: objectmanager::ObjectId, command_queue: queues::MessageQueue, + data: datasets::OwnedDataset } struct HandlerSender { @@ -59,8 +69,10 @@ struct HandlerSender { other_handler: objectmanager::ObjectId, cycle: u8, other_handler_queue: queues::MessageQueueSender, + other_data: datasets::ReferencedDataset } + impl Handler { fn handle_message(&self, message: queues::Message) { match message { @@ -123,6 +135,18 @@ impl SystemObjectIF for Handler { } } +impl datasets::DatapoolOwnerIF for Handler { + fn get_set(&self, type_id: datasets::TypeId) -> Option<&dyn datasets::DataSetIF>{ + if type_id == self.data.get_type_id(){ + Some(&self.data) + } else { + None + } + } +} + +impl DatapoolOwnerIF for HandlerSender {} + impl SystemObjectIF for HandlerSender { fn get_command_queue(&self) -> crate::fsrc::queues::MessageQueueSender { queues::MessageQueueSender::new() @@ -131,11 +155,14 @@ impl SystemObjectIF for HandlerSender { self.id } fn initialize(&mut self, object_manager: &dyn objectmanager::ObjectManager) -> Result<(), ()> { - let other_handler = object_manager.get_object(self.other_handler); - match other_handler { - Ok(other) => self.other_handler_queue = other.get_command_queue(), + let other_handler_maybe = object_manager.get_object(self.other_handler); + let other_handler = + match other_handler_maybe { + Ok(other) => other, Err(_) => return Err(()), - } + }; + self.other_handler_queue = other_handler.get_command_queue(); + //TODO self.other_data.initialize(other_handler); // oh come on :/ Ok(()) } } @@ -146,12 +173,14 @@ fn mission() { let mut h1 = Handler { id: 1, command_queue: queues::MessageQueue::new(5), + data: datasets::OwnedDataset::new() }; let mut h2 = HandlerSender { id: 2, other_handler: 3, cycle: 0, other_handler_queue: queues::MessageQueueSender::new(), + other_data: datasets::ReferencedDataset::new() }; let array: &mut [&mut dyn objectmanager::SystemObjectIF] = &mut [&mut h1]; From bae91a61d4bdcf0ea0798c32aeb12307428073be Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Mon, 11 Dec 2023 15:34:09 +0100 Subject: [PATCH 06/11] trait inheritance is strange --- mission_rust/src/fsrc/datasets/mod.rs | 7 +++++-- mission_rust/src/fsrc/tasks/mod.rs | 2 ++ mission_rust/src/lib.rs | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/mission_rust/src/fsrc/datasets/mod.rs b/mission_rust/src/fsrc/datasets/mod.rs index dea8b6c..8883b04 100644 --- a/mission_rust/src/fsrc/datasets/mod.rs +++ b/mission_rust/src/fsrc/datasets/mod.rs @@ -1,3 +1,5 @@ +use super::objectmanager::SystemObjectIF; + #[derive(Copy, Clone, PartialEq)] pub struct TypeId { id: usize, @@ -14,7 +16,7 @@ pub trait Reflection { } pub trait DatapoolOwnerIF { - fn get_set(&self, type_id: TypeId) -> Option<&dyn DataSetIF> { + fn get_set(&self, _type_id: TypeId) -> Option<&dyn DataSetIF> { None } } @@ -75,7 +77,7 @@ impl ReferencedDataset { } } - pub fn initialize(&mut self, owner: &dyn DatapoolOwnerIF) -> Result<(), ()> { + pub fn initialize(&mut self, owner: &dyn SystemObjectIF) -> Result<(), ()> { let temp: T = T::default(); //TODO find nicer solution whithout local instance and trait bound to Default let type_id = temp.get_type_id(); let other_set: &dyn DataSetIF; @@ -88,6 +90,7 @@ impl ReferencedDataset { } } //pointer cast is safe because we checked the type_id + //getting pointer to avoid lifetime check self.actual_data = Some(other_set.get_actual_data() as *const dyn Reflection as *const T); //self.mutex = other.mutex Ok(()) diff --git a/mission_rust/src/fsrc/tasks/mod.rs b/mission_rust/src/fsrc/tasks/mod.rs index 9251c73..253dfb0 100644 --- a/mission_rust/src/fsrc/tasks/mod.rs +++ b/mission_rust/src/fsrc/tasks/mod.rs @@ -90,6 +90,7 @@ pub struct TaskExecutor<'a> { impl<'a> TaskExecutor<'a> { pub fn init_and_run(&mut self) { + //TODO unlock global multitasking mutex let object_manager = TaskObjectManager { tasks: unsafe { slice::from_raw_parts(self.tasks.as_ptr(), self.tasks.len()) }, }; @@ -140,6 +141,7 @@ impl<'a> crate::objectmanager::ObjectManager<'a> for TaskObjectManager<'a> { impl<'a> Drop for TaskExecutor<'a> { fn drop(&mut self) { + //TODO lock global multitasking mutex for task in self.tasks.iter_mut() { unsafe { crate::fsrc::osal::delete_task(task.get_handle()); diff --git a/mission_rust/src/lib.rs b/mission_rust/src/lib.rs index 5a8054f..94794c7 100644 --- a/mission_rust/src/lib.rs +++ b/mission_rust/src/lib.rs @@ -162,7 +162,8 @@ impl SystemObjectIF for HandlerSender { Err(_) => return Err(()), }; self.other_handler_queue = other_handler.get_command_queue(); - //TODO self.other_data.initialize(other_handler); // oh come on :/ + + self.other_data.initialize(other_handler)?; Ok(()) } } From e585ecafcc253f9a47bc2c986be29cde2c173875 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Mon, 11 Dec 2023 22:44:06 +0100 Subject: [PATCH 07/11] adding mutexes, cleaning up types --- bsp_z7/ps7_cortexa9_0/include/xil_printf.h | 2 +- .../libsrc/standalone/src/outbyte.c | 4 +- mission/freeRTOS_rust_helper.c | 31 ++++++++++++- mission_rust/Cargo.lock | 9 ---- mission_rust/Cargo.toml | 1 - mission_rust/src/fsrc/datasets/mod.rs | 42 ++++++++++++++---- mission_rust/src/fsrc/mod.rs | 5 ++- mission_rust/src/fsrc/mutex.rs | 44 +++++++++++++++++++ mission_rust/src/fsrc/osal/mod.rs | 28 +++++++----- mission_rust/src/fsrc/queues/mod.rs | 31 ++++++------- mission_rust/src/fsrc/sif.rs | 2 + mission_rust/src/fsrc/tasks/mod.rs | 34 +++++++------- mission_rust/src/lib.rs | 2 +- 13 files changed, 165 insertions(+), 70 deletions(-) create mode 100644 mission_rust/src/fsrc/mutex.rs diff --git a/bsp_z7/ps7_cortexa9_0/include/xil_printf.h b/bsp_z7/ps7_cortexa9_0/include/xil_printf.h index 062ad6b..4906e2a 100644 --- a/bsp_z7/ps7_cortexa9_0/include/xil_printf.h +++ b/bsp_z7/ps7_cortexa9_0/include/xil_printf.h @@ -43,7 +43,7 @@ typedef s32 (*func_ptr)(int c); void xil_printf( const char8 *ctrl1, ...); void xil_vprintf(const char8 *ctrl1, va_list argp); void print( const char8 *ptr); -extern void outbyte (char c); +extern void outbyte (uint8_t c); extern char inbyte(void); #ifdef __cplusplus diff --git a/bsp_z7/ps7_cortexa9_0/libsrc/standalone/src/outbyte.c b/bsp_z7/ps7_cortexa9_0/libsrc/standalone/src/outbyte.c index db60e6d..1080862 100644 --- a/bsp_z7/ps7_cortexa9_0/libsrc/standalone/src/outbyte.c +++ b/bsp_z7/ps7_cortexa9_0/libsrc/standalone/src/outbyte.c @@ -8,12 +8,12 @@ #ifdef __cplusplus extern "C" { #endif -void outbyte(char c); +void outbyte(uint8_t c); #ifdef __cplusplus } #endif -void outbyte(char c) { +void outbyte(uint8_t c) { XUartPs_SendByte(STDOUT_BASEADDRESS, c); } diff --git a/mission/freeRTOS_rust_helper.c b/mission/freeRTOS_rust_helper.c index 7fc3fbb..21b5c51 100644 --- a/mission/freeRTOS_rust_helper.c +++ b/mission/freeRTOS_rust_helper.c @@ -10,7 +10,8 @@ void stop_it() { taskENTER_CRITICAL(); } // TODO return some error code? void *create_task(TaskFunction_t taskFunction, void *parameter, - size_t stack_size) { + uint32_t stack_size) { + //TODO verify uint32_t vs configSTACK_DEPTH_TYPE TaskHandle_t newTask; BaseType_t result = xTaskCreate(taskFunction, "rust", stack_size, parameter, 4, &newTask); @@ -22,6 +23,7 @@ void *create_task(TaskFunction_t taskFunction, void *parameter, } void task_delay(uint32_t milliseconds) { + //TODO verify uint32_t vs TickType_t vTaskDelay(pdMS_TO_TICKS(milliseconds)); } @@ -29,7 +31,8 @@ void delete_task(void * task){ vTaskSuspend(task); //we can not use vDeleteTask as it would free the allocated memory which is forbidden using heap1 (which we use) } -void *create_queue(size_t length, size_t element_size) { +void *create_queue(uint32_t length, uint32_t element_size) { + //TODO verify uint32_t vs UBaseType_t QueueHandle_t newQueue = xQueueCreate(length, element_size); return newQueue; } @@ -48,4 +51,28 @@ uint8_t queue_send(void *queue, void *message) { } else { return 0; } +} + +void *create_mutex() { + return xSemaphoreCreateRecursiveMutex(); +} + +uint8_t take_mutex(void * handle) { + // TODO check if global semaphore is free (ie, we are doing multitasking) + // if not, pointers are invalid, bail out + if (xSemaphoreTakeRecursive(handle, portMAX_DELAY) == pdPASS) { + return 1; + } else { + return 0; + } +} + +uint8_t give_mutex(void * handle) { + // TODO check if global semaphore is free (ie, we are doing multitasking) + // if not, pointers are invalid, bail out + if (xSemaphoreGiveRecursive(handle) == pdPASS) { + return 1; + } else { + return 0; + } } \ No newline at end of file diff --git a/mission_rust/Cargo.lock b/mission_rust/Cargo.lock index 4427a22..c0a15a2 100644 --- a/mission_rust/Cargo.lock +++ b/mission_rust/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "cty" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" - [[package]] name = "mission_rust" version = "0.1.0" -dependencies = [ - "cty", -] diff --git a/mission_rust/Cargo.toml b/mission_rust/Cargo.toml index a625263..5f705b3 100644 --- a/mission_rust/Cargo.toml +++ b/mission_rust/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["staticlib"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -cty = "0.2.2" diff --git a/mission_rust/src/fsrc/datasets/mod.rs b/mission_rust/src/fsrc/datasets/mod.rs index 8883b04..a2304be 100644 --- a/mission_rust/src/fsrc/datasets/mod.rs +++ b/mission_rust/src/fsrc/datasets/mod.rs @@ -1,3 +1,4 @@ +use super::mutex; use super::objectmanager::SystemObjectIF; #[derive(Copy, Clone, PartialEq)] @@ -24,16 +25,18 @@ pub trait DatapoolOwnerIF { pub trait DataSetIF { fn get_type_id(&self) -> TypeId; fn get_actual_data(&self) -> &dyn Reflection; - //fn get_mutex_handle(&self) -> TODO + fn get_mutex(&self) -> mutex::RawMutex; } pub struct OwnedDataset { actual_data: T, + mutex: mutex::RawMutex, } pub struct ReferencedDataset { //we use a pointer here to avoid lifetimes actual_data: Option<*const T>, + mutex: Option, } impl DataSetIF for OwnedDataset { @@ -44,33 +47,54 @@ impl DataSetIF for OwnedDataset { fn get_actual_data(&self) -> &dyn Reflection { &self.actual_data } + + fn get_mutex(&self) -> mutex::RawMutex { + return self.mutex; + } } impl OwnedDataset { pub fn new() -> OwnedDataset { OwnedDataset:: { actual_data: T::default(), + mutex: mutex::RawMutex::new(), } } - pub fn read(&mut self) -> T { - //mutex - self.actual_data + pub fn read(&mut self) -> Result { + let _mutex_guard = match self.mutex.take() { + Err(()) => return Err(()), + Ok(guard) => guard, + }; + Ok(self.actual_data) } + //TODO do we want to know if it fails? pub fn commit(&mut self, data: T) { - //mutex + let _mutex_guard = match self.mutex.take() { + Err(()) => return, + Ok(guard) => guard, + }; self.actual_data = data; } } impl ReferencedDataset { pub fn new() -> ReferencedDataset { - ReferencedDataset:: { actual_data: None } + ReferencedDataset:: { + actual_data: None, + mutex: None, + } } - + pub fn read(&mut self) -> Result { - //mutex + let _mutex_guard = match self.mutex { + None => return Err(()), + Some(mutex) => match mutex.take() { + Err(()) => return Err(()), + Ok(guard) => guard, + }, + }; match self.actual_data { None => Err(()), Some(data) => Ok(unsafe { *data }), @@ -92,7 +116,7 @@ impl ReferencedDataset { //pointer cast is safe because we checked the type_id //getting pointer to avoid lifetime check self.actual_data = Some(other_set.get_actual_data() as *const dyn Reflection as *const T); - //self.mutex = other.mutex + self.mutex = Some(other_set.get_mutex()); Ok(()) } } diff --git a/mission_rust/src/fsrc/mod.rs b/mission_rust/src/fsrc/mod.rs index cc5c015..f56e588 100644 --- a/mission_rust/src/fsrc/mod.rs +++ b/mission_rust/src/fsrc/mod.rs @@ -1,6 +1,9 @@ +//TODO control visibility of internal structs + pub mod sif; pub mod queues; pub mod osal; pub mod tasks; pub mod objectmanager; -pub mod datasets; \ No newline at end of file +pub mod datasets; +mod mutex; \ No newline at end of file diff --git a/mission_rust/src/fsrc/mutex.rs b/mission_rust/src/fsrc/mutex.rs new file mode 100644 index 0000000..c1e926b --- /dev/null +++ b/mission_rust/src/fsrc/mutex.rs @@ -0,0 +1,44 @@ +use super::osal; + +// We allow copy because we protext our pointer via a global mutex +// when using static allocation (which is TBCoded) +#[derive(Clone, Copy)] +pub struct RawMutex { + handle: *const core::ffi::c_void, +} + +pub struct RawMutexGuard { + handle: *const core::ffi::c_void, +} + +impl Drop for RawMutexGuard { + fn drop(&mut self) { + //TODO do we need some check here? + unsafe{osal::give_mutex(self.handle)}; + } +} + +//TODO for non allocating, keep handle as option, to be set either in an initialize() call +// or maybe lazy later on, TBD if lazy is needed or we can guarantee init to be called +impl RawMutex { + pub fn new() -> Self { + let handle = unsafe {osal::create_mutex()}; + Self { + handle: handle + } + } + + pub fn take(&self) -> Result { + match unsafe {osal::take_mutex(self.handle)} { + 1 => Ok(RawMutexGuard { handle: self.handle }), + _ => Err(()) //TODO error code + } + } + + // pub fn give(&self) -> Result<(),()> { + // match unsafe {osal::give_mutex(self.handle)} { + // 1 => Ok(()), + // _ => Err(()) //TODO error code + // } + // } +} \ No newline at end of file diff --git a/mission_rust/src/fsrc/osal/mod.rs b/mission_rust/src/fsrc/osal/mod.rs index 241de2e..3710125 100644 --- a/mission_rust/src/fsrc/osal/mod.rs +++ b/mission_rust/src/fsrc/osal/mod.rs @@ -1,25 +1,33 @@ -type TaskFunction = unsafe extern "C" fn(*mut cty::c_void); +type TaskFunction = unsafe extern "C" fn(*mut core::ffi::c_void); + +//TODO verify uXX == uintXX_t extern "C" { - pub fn outbyte(c: cty::c_char); + pub fn outbyte(c: u8); //void *create_task(TaskFunction_t taskFunction, void *parameter, size_t stack_size) pub fn create_task( taskFunction: TaskFunction, - parameter: *const cty::c_void, - stack_size: cty::size_t, - ) -> *const cty::c_void; + parameter: *const core::ffi::c_void, + stack_size: u32, + ) -> *const core::ffi::c_void; pub fn get_task_name() -> *const core::ffi::c_char; pub fn stop_it(); - pub fn delete_task(handle: *const cty::c_void); + pub fn delete_task(handle: *const core::ffi::c_void); - pub fn task_delay(milliseconds: cty::uint32_t); + pub fn task_delay(milliseconds: u32); //void *create_queue(size_t length, size_t element_size) - pub fn create_queue(length: cty::size_t, element_size: cty::size_t) -> *const cty::c_void; + pub fn create_queue(length: u32, element_size: u32) -> *const core::ffi::c_void; + + pub fn queue_receive(queue: *const core::ffi::c_void, message: *const core::ffi::c_void) -> u8; + pub fn queue_send(queue: *const core::ffi::c_void, message: *const core::ffi::c_void) -> u8; + + pub fn create_mutex() -> *const core::ffi::c_void; + + pub fn take_mutex(mutex: *const core::ffi::c_void) -> u8; + pub fn give_mutex(mutex: *const core::ffi::c_void) -> u8; - pub fn queue_receive(queue: *const cty::c_void, message: *const cty::c_void) -> cty::uint8_t; - pub fn queue_send(queue: *const cty::c_void, message: *const cty::c_void) -> cty::uint8_t; } \ No newline at end of file diff --git a/mission_rust/src/fsrc/queues/mod.rs b/mission_rust/src/fsrc/queues/mod.rs index 950a567..890d6b6 100644 --- a/mission_rust/src/fsrc/queues/mod.rs +++ b/mission_rust/src/fsrc/queues/mod.rs @@ -1,25 +1,24 @@ pub struct MessageQueue { - queue_id: *const cty::c_void, + queue_id: *const core::ffi::c_void, } pub struct MessageQueueSender { - queue_id: Option<*const cty::c_void>, + queue_id: Option<*const core::ffi::c_void>, } impl MessageQueue { pub fn new(depth: usize) -> Self { - let mut instance: Self; unsafe { - instance = Self { - queue_id: 0 as *const cty::c_void, - }; //TODO check cast of depth - instance.queue_id = crate::fsrc::osal::create_queue(depth, core::mem::size_of::()); + let cast_depth: u32 = u32::try_from(depth).unwrap(); //TODO check these + let element_size: u32 = u32::try_from(core::mem::size_of::()).unwrap(); + let queue_id = crate::fsrc::osal::create_queue(cast_depth, element_size); - if instance.queue_id == 0 as *mut cty::c_void { + if queue_id == 0 as *mut core::ffi::c_void { panic!("could not create Queue"); } - instance + + Self { queue_id: queue_id } } } @@ -32,10 +31,10 @@ impl MessageQueue { pub fn receive(&self) -> Result { let mut message: Message = Message::default(); - let res: cty::uint8_t; + let res: u8; unsafe { //message = core::mem::MaybeUninit::zeroed().assume_init(); // We only return it if the queue received something - let message_pointer: *mut cty::c_void = &mut message as *mut _ as *mut cty::c_void; + let message_pointer: *mut core::ffi::c_void = &mut message as *mut _ as *mut core::ffi::c_void; res = crate::fsrc::osal::queue_receive(self.queue_id, message_pointer); } if res == 1 { @@ -48,16 +47,14 @@ impl MessageQueue { impl MessageQueueSender { pub fn new() -> Self { - Self { - queue_id: None, - } + Self { queue_id: None } } pub fn send(&self, message: Message) -> Result<(), ()> { let queue_id = self.queue_id.expect("unitialized Message Queue"); - let res: cty::uint8_t; + let res: u8; unsafe { - let message_pointer: *const cty::c_void = &message as *const _ as *const cty::c_void; + let message_pointer: *const core::ffi::c_void = &message as *const _ as *const core::ffi::c_void; res = crate::fsrc::osal::queue_send(queue_id, message_pointer); } if res == 1 { @@ -80,4 +77,4 @@ pub enum Message { #[default] FAILED, DATA(GenericMessageData), -} \ No newline at end of file +} diff --git a/mission_rust/src/fsrc/sif.rs b/mission_rust/src/fsrc/sif.rs index a1801a3..f56438b 100644 --- a/mission_rust/src/fsrc/sif.rs +++ b/mission_rust/src/fsrc/sif.rs @@ -1,3 +1,5 @@ +// TODO this is platform specific + pub struct Outbytes {} use core::fmt::{Error, Write}; diff --git a/mission_rust/src/fsrc/tasks/mod.rs b/mission_rust/src/fsrc/tasks/mod.rs index 253dfb0..936a43c 100644 --- a/mission_rust/src/fsrc/tasks/mod.rs +++ b/mission_rust/src/fsrc/tasks/mod.rs @@ -3,7 +3,7 @@ use core::slice; use super::objectmanager::ObjectManager; #[no_mangle] -extern "C" fn task_entry(task_object: *mut cty::c_void) { +extern "C" fn task_entry(task_object: *mut core::ffi::c_void) { let task: &mut dyn TaskIF; unsafe { let pointer = task_object as *mut PeriodicTask; @@ -18,29 +18,29 @@ pub trait ExecutableObjectIF { pub trait TaskIF<'a> { fn run(&mut self); - fn get_stack_size(&self) -> cty::size_t; - fn set_handle(&mut self, task_handle: *const cty::c_void); - fn get_handle(&self) -> *const cty::c_void; + fn get_stack_size(&self) -> u32; + fn set_handle(&mut self, task_handle: *const core::ffi::c_void); + fn get_handle(&self) -> *const core::ffi::c_void; fn get_objects(&'a self) -> &'a [&'a mut dyn crate::objectmanager::SystemObjectIF]; fn initialize(&mut self, object_manager: &dyn ObjectManager) -> Result<(), ()>; } pub struct PeriodicTask<'a> { - pub stack_size: cty::size_t, //TODO generic type and safety - pub task_handle: *const cty::c_void, - pub period: usize, + pub stack_size: u32, //TODO generic type and safety + pub task_handle: *const core::ffi::c_void, + pub period: u32, pub task_objects: &'a mut [&'a mut dyn crate::objectmanager::SystemObjectIF], } impl<'a> PeriodicTask<'a> { pub fn new( objects: &'a mut [&'a mut dyn crate::objectmanager::SystemObjectIF], - stack_size: usize, - period: usize, + stack_size: u32, + period: u32, ) -> PeriodicTask<'a> { let instance: PeriodicTask<'a> = Self { stack_size: stack_size, - task_handle: 0 as *const cty::c_void, + task_handle: 0 as *const core::ffi::c_void, period: period, task_objects: objects, }; @@ -56,17 +56,17 @@ impl<'a> TaskIF<'a> for PeriodicTask<'a> { } //TODO make this exact unsafe { - crate::fsrc::osal::task_delay(self.period as cty::uint32_t); //TODO type of delay should be generic but safe (cap to max in C) + crate::fsrc::osal::task_delay(self.period); //TODO type of delay should be generic but safe (cap to max in C) } } } - fn get_stack_size(&self) -> cty::size_t { + fn get_stack_size(&self) -> u32 { self.stack_size } - fn set_handle(&mut self, task_handle: *const cty::c_void) { + fn set_handle(&mut self, task_handle: *const core::ffi::c_void) { self.task_handle = task_handle; } - fn get_handle(&self) -> *const cty::c_void { + fn get_handle(&self) -> *const core::ffi::c_void { self.task_handle } fn get_objects(&'a self) -> &'a [&'a mut dyn crate::objectmanager::SystemObjectIF] { @@ -104,13 +104,13 @@ impl<'a> TaskExecutor<'a> { // to our own lifetime and destroy the task when we get dropped // this way, the reference is guaranteed to be valid over our // lifetime while the task is deleted at the end of our lifetime - let task_pointer: *const cty::c_void = *task as *mut _ as *const cty::c_void; //TODO this does work without the "*" in front of the task -> Why?? + let task_pointer: *const core::ffi::c_void = *task as *mut _ as *const core::ffi::c_void; //TODO this does work without the "*" in front of the task -> Why?? let handle; unsafe { handle = - crate::fsrc::osal::create_task(task_entry, task_pointer, task.get_stack_size()); + crate::fsrc::osal::create_task(task_entry, task_pointer, u32::try_from(task.get_stack_size()).unwrap()); } - if handle == 0 as *mut cty::c_void { + if handle == 0 as *mut core::ffi::c_void { panic!("could not create Task"); } else { task.set_handle(handle); diff --git a/mission_rust/src/lib.rs b/mission_rust/src/lib.rs index 94794c7..4a56dbd 100644 --- a/mission_rust/src/lib.rs +++ b/mission_rust/src/lib.rs @@ -192,7 +192,7 @@ fn mission() { task_objects: &mut [&mut h2], stack_size: 512, period: 400, - task_handle: 0 as *const cty::c_void, + task_handle: 0 as *const core::ffi::c_void, }; let mut task_executor = tasks::TaskExecutor { From 881ae8c0966d3ff2c707186120ead2fa7d19e765 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Tue, 12 Dec 2023 11:03:24 +0100 Subject: [PATCH 08/11] renamed trait --- mission_rust/src/fsrc/datasets/mod.rs | 20 ++++++++++---------- mission_rust/src/lib.rs | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/mission_rust/src/fsrc/datasets/mod.rs b/mission_rust/src/fsrc/datasets/mod.rs index a2304be..bc04710 100644 --- a/mission_rust/src/fsrc/datasets/mod.rs +++ b/mission_rust/src/fsrc/datasets/mod.rs @@ -7,11 +7,11 @@ pub struct TypeId { } // inspired by https://github.com/jswrenn/deflect/ -pub trait Reflection { +pub trait HasTypeId { #[inline(never)] fn get_type_id(&self) -> TypeId { TypeId { - id: ::get_type_id as usize, + id: Self::get_type_id as usize, } } } @@ -24,27 +24,27 @@ pub trait DatapoolOwnerIF { pub trait DataSetIF { fn get_type_id(&self) -> TypeId; - fn get_actual_data(&self) -> &dyn Reflection; + fn get_actual_data(&self) -> &dyn HasTypeId; fn get_mutex(&self) -> mutex::RawMutex; } -pub struct OwnedDataset { +pub struct OwnedDataset { actual_data: T, mutex: mutex::RawMutex, } -pub struct ReferencedDataset { +pub struct ReferencedDataset { //we use a pointer here to avoid lifetimes actual_data: Option<*const T>, mutex: Option, } -impl DataSetIF for OwnedDataset { +impl DataSetIF for OwnedDataset { fn get_type_id(&self) -> TypeId { self.actual_data.get_type_id() } - fn get_actual_data(&self) -> &dyn Reflection { + fn get_actual_data(&self) -> &dyn HasTypeId { &self.actual_data } @@ -53,7 +53,7 @@ impl DataSetIF for OwnedDataset { } } -impl OwnedDataset { +impl OwnedDataset { pub fn new() -> OwnedDataset { OwnedDataset:: { actual_data: T::default(), @@ -79,7 +79,7 @@ impl OwnedDataset { } } -impl ReferencedDataset { +impl ReferencedDataset { pub fn new() -> ReferencedDataset { ReferencedDataset:: { actual_data: None, @@ -115,7 +115,7 @@ impl ReferencedDataset { } //pointer cast is safe because we checked the type_id //getting pointer to avoid lifetime check - self.actual_data = Some(other_set.get_actual_data() as *const dyn Reflection as *const T); + self.actual_data = Some(other_set.get_actual_data() as *const dyn HasTypeId as *const T); self.mutex = Some(other_set.get_mutex()); Ok(()) } diff --git a/mission_rust/src/lib.rs b/mission_rust/src/lib.rs index 4a56dbd..fa07f9b 100644 --- a/mission_rust/src/lib.rs +++ b/mission_rust/src/lib.rs @@ -56,7 +56,7 @@ struct HandlerData { y: f32 } -impl datasets::Reflection for HandlerData {} +impl datasets::HasTypeId for HandlerData {} struct Handler { id: objectmanager::ObjectId, From 7f7f57038c74149c92178e07f78b00b10e8ac5a9 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Tue, 12 Dec 2023 12:15:18 +0100 Subject: [PATCH 09/11] making dataset init more safe --- mission_rust/src/fsrc/datasets/mod.rs | 6 ++++-- mission_rust/src/lib.rs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/mission_rust/src/fsrc/datasets/mod.rs b/mission_rust/src/fsrc/datasets/mod.rs index bc04710..e9e652b 100644 --- a/mission_rust/src/fsrc/datasets/mod.rs +++ b/mission_rust/src/fsrc/datasets/mod.rs @@ -1,4 +1,4 @@ -use super::mutex; +use super::{mutex, objectmanager}; use super::objectmanager::SystemObjectIF; #[derive(Copy, Clone, PartialEq)] @@ -101,7 +101,9 @@ impl ReferencedDataset { } } - pub fn initialize(&mut self, owner: &dyn SystemObjectIF) -> Result<(), ()> { + //Note, passing the object_manager is per design, so that this call can only be used during init (when storing the address is valid) + pub fn initialize(&mut self, object_manager: &dyn objectmanager::ObjectManager, owner_of_the_set: objectmanager::ObjectId) -> Result<(), ()> { + let owner = object_manager.get_object(owner_of_the_set)?; let temp: T = T::default(); //TODO find nicer solution whithout local instance and trait bound to Default let type_id = temp.get_type_id(); let other_set: &dyn DataSetIF; diff --git a/mission_rust/src/lib.rs b/mission_rust/src/lib.rs index fa07f9b..462e633 100644 --- a/mission_rust/src/lib.rs +++ b/mission_rust/src/lib.rs @@ -163,7 +163,7 @@ impl SystemObjectIF for HandlerSender { }; self.other_handler_queue = other_handler.get_command_queue(); - self.other_data.initialize(other_handler)?; + self.other_data.initialize(object_manager, self.other_handler)?; Ok(()) } } From c3be10903b9df3fe87bd58ae6fc0453a45237c50 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Tue, 12 Dec 2023 12:15:39 +0100 Subject: [PATCH 10/11] some documentation --- mission_rust/doc/Static_Allocation.md | 64 +++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 mission_rust/doc/Static_Allocation.md diff --git a/mission_rust/doc/Static_Allocation.md b/mission_rust/doc/Static_Allocation.md new file mode 100644 index 0000000..9556dd5 --- /dev/null +++ b/mission_rust/doc/Static_Allocation.md @@ -0,0 +1,64 @@ +The framework is meant to be used without dynamic allocation. Currently the only supported RTOS is implemented in C, which adds additional constraints. + +# Static allocation and rust + +As far as rust goes, static allocation and multithreading with a C RTOS is not possible generally. No allocation means that almost all data will be located on the stack. References to data on the stack are per design unsafe in the most wide sense of the word. First, because they are on the stack which will be cleared after a function defining data returns. Secondly, rust specifies data to be ignorant of their location in memory, that is data can be moved in memory without any possibility of hooks which could update foreign references. + +In a multithreaded software, references to data need to be passed to enable communication between objects, to be able to execute a task (which requires passing a reference to the task's data (which contains information on other tasks/data) to the RTOS), to send messages (passing the reference to the Queue, however encapsulated, is a reference to the queue which needs to be located somewhere) or to acess shared data (mutexes, same as with queues). +All of these communication techniques are essential for this framework, so solutions need to be provided to be able to write safe code. + +While statically allocating all (shared) data is generally possible and might be a possible solution to static allocation, it is not consistent with either the object oriented style of this framework, nor with general rust coding style (which discourages static data). + +# The framework's approach + +## Task Executor + +Central element in running multithreaded is the task executor. It is borrowing references to all data to be used during runtime. This way, the references are guaranteed to be valid for the lifetime of the executor. By then coupling the availability of tasks to the executor (dropping the executor stops all tasks), the references can be guaranteed to be valid for the whole time tasks are available. + +The step where references to other objects is stored in structs is called initialization. This is (enforced by the compiler via layout of the corresponding functions) the only time where access to other objects is granted and references can be stored. The initialization is performed by the task executor as well, so at that time refereces are already fixed to their threading-time value. + +For an production software, as soon as all tasks are started, the initial task is stopped/deleted. As such, there is no way (and no intention) for the multithreading to ever stop. In that case, the references shared during initialization will be valid for the whole runtime of the program. + +As there might be use cases, for example in test cases, where multithreading is to be stopped, additional safeguards are implemented to ensure that the references shared during initialization are invalidated or their use is restricted. As running outside of the multithreaded environment is not meant for production, failing without corruption, ie panicking, is an acceptable way out. This is implemented by using a global semaphore indicating if the thread executor is alive. If that is not the case, all access to thared ressources will result in a panic. This adds an additional overhead to all access to shared data, namely checking the semaphore. + +## Shared references + +To be able to implement aforementioned safeguards, access to references is guarded by the framework. + +The only time where references to other objects can be acquired is the initialization step performed by the task executor. As the task executor borrows all objects mutably, no references (mutable or not) to other objects, can be stored within any object. + +Access to the other objects is granted via an object manager implementation, which will provide other objects as a nonmutable reference. Again, this reference can not be stored, only queried, without violating the borrow checker. + +The reference obtained by the object manager is typed as `dyn SystemObjectIF`, so the ways to obtain references is governed by this trait and its super traits. These traits only offer threadsafe references, ie ones protected by RTOS primitives, to be obtained. + +Those references are either protected by a mutex or implemented using queues, which are the two primitives used to implement threadsafety. As such all access to shared references must use either the mutex or the queue API which is protected by an additional semaphore as described above. + +## RTOS Metadata + +RTOS metadata is the data the RTOS needs to work on its provided primitives such as tasks, queues and mutexes. Those come in two variants, statically sized, which here are called descriptors, and dynamic data. Descriptors have a size known at compile time which is the same for all instances of the primitive. Dynamic data is for instance the backend of a queue or the stack of a task. These do generally differ in size for the different instances. Keeping with the general theme of object orientation, the dynamic information is encapsulated within the structs abstracting the RTOS primitives. + +### Descriptors + +Two options exist for storing the descriptors. Either they are stored in a preallocated static C array, or they are stored in memory allocated as part of the corresponding struct. + +The first option has the advantage that in C, the size of the descriptors is known at compile time (it is determined by the RTOS configuration which is implemented in C macros). Its disadvantage is that the size of the array needs to be adapted to the actual number ob instances used by the RTOS, which might not be trivially determined except for running the software. + +The second option does not have this disadvantage, as the memory is provided by the user of the API. The disadvantage here is that the size of the data needs to be encoded manually for the selected configuration of the RTOS, which again can only be verified during runtime, when rust an C interact. + +Both solutions lead to a detection of the configuration error (too few descriptors preallocated, too little memory allocated) only during runtime. As the configuration of the RTOS is expected to be more stable than the number of primitive instances, the second option is implemented. + +### Dynamic data + +Dynamic data is allocated on the stack as part of the encapsulating struct. Having dynamic data preallocated is not trivial as the actual size is determined by the instantiation of the corresponding struct, which is done at runtime. + +### Passing to the RTOS + +The metadata should only passed to the RTOS when the references are fixed, that is when the task executor is constructed. To make sure that no uninitialized primitives are used, they are created in an invalid state within `new()` of the encapsulating struct. Using an uninitialized struct does fail mostly silently, as this could be happen during runtime, when no panic is allowed. + +As the structs are used to facilitate inter task communication, the initialization is hidden in the call which copies the contained reference out to the using object. That way, structs which are used by other tasks are guaranteed to be initialized, and uninitialized structs can only be used locally, so they do not need to be protected by the primitives at all. + +The handling of the failure is delegated to the structs using the primitives, as the 'correct' way to fail is dependent on the usage: + +* Uninitialized `MessageQueue`s will return empty from `read()` calls +* Uninitialized `Mutex`es [TBCoded] will behave nominally, but not lock any actual mutex +* Uninitialized `OwnedDataset`s will behave nominally, but not lock any mutex (which is uninitialized) \ No newline at end of file From 07d9f52b8d752425d3c9fee3ed47f209b913fca9 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Tue, 12 Dec 2023 22:55:22 +0100 Subject: [PATCH 11/11] working on thread safety, preparing for static RTOS --- mission/freeRTOS_rust_helper.c | 23 ++++++++ mission_rust/src/fsrc/datasets/mod.rs | 7 ++- mission_rust/src/fsrc/mutex.rs | 42 +++++++++++--- mission_rust/src/fsrc/osal/mod.rs | 23 ++++++++ mission_rust/src/fsrc/queues/mod.rs | 80 +++++++++++++++++---------- mission_rust/src/lib.rs | 12 ++-- 6 files changed, 142 insertions(+), 45 deletions(-) diff --git a/mission/freeRTOS_rust_helper.c b/mission/freeRTOS_rust_helper.c index 21b5c51..6518fbb 100644 --- a/mission/freeRTOS_rust_helper.c +++ b/mission/freeRTOS_rust_helper.c @@ -4,6 +4,29 @@ // TODO namespace the names +SemaphoreHandle_t * global_threading_semaphore = NULL; + +uint8_t global_threading_available_c() { + if (global_threading_semaphore == NULL) { + + global_threading_semaphore = xSemaphoreCreateBinary(); + //xSemaphoreGive(global_threading_semaphore); + } + if (uxSemaphoreGetCount(global_threading_semaphore) == 1) { + return 1; + } else { + return 0; + } +} + +void enable_global_threading_c() { + xSemaphoreGive(global_threading_semaphore); +} + +void disable_global_threading_c() { + xSemaphoreTake(global_threading_semaphore, portMAX_DELAY); +} + const char *get_task_name() { return pcTaskGetName(NULL); } void stop_it() { taskENTER_CRITICAL(); } diff --git a/mission_rust/src/fsrc/datasets/mod.rs b/mission_rust/src/fsrc/datasets/mod.rs index e9e652b..a0c7841 100644 --- a/mission_rust/src/fsrc/datasets/mod.rs +++ b/mission_rust/src/fsrc/datasets/mod.rs @@ -39,7 +39,7 @@ pub struct ReferencedDataset { mutex: Option, } -impl DataSetIF for OwnedDataset { +impl DataSetIF for OwnedDataset { fn get_type_id(&self) -> TypeId { self.actual_data.get_type_id() } @@ -49,6 +49,7 @@ impl DataSetIF for OwnedDataset { } fn get_mutex(&self) -> mutex::RawMutex { + self.initialize(); return self.mutex; } } @@ -77,6 +78,10 @@ impl OwnedDataset { }; self.actual_data = data; } + + fn initialize(&mut self) { + + } } impl ReferencedDataset { diff --git a/mission_rust/src/fsrc/mutex.rs b/mission_rust/src/fsrc/mutex.rs index c1e926b..1770dfe 100644 --- a/mission_rust/src/fsrc/mutex.rs +++ b/mission_rust/src/fsrc/mutex.rs @@ -1,10 +1,7 @@ use super::osal; -// We allow copy because we protext our pointer via a global mutex -// when using static allocation (which is TBCoded) -#[derive(Clone, Copy)] pub struct RawMutex { - handle: *const core::ffi::c_void, + handle: Option<*const core::ffi::c_void> } pub struct RawMutexGuard { @@ -22,23 +19,50 @@ impl Drop for RawMutexGuard { // or maybe lazy later on, TBD if lazy is needed or we can guarantee init to be called impl RawMutex { pub fn new() -> Self { - let handle = unsafe {osal::create_mutex()}; Self { - handle: handle + handle: None + } + } + + pub fn clone(&self) -> Self { + let mut_self = self as *const Self as *mut Self; //oh look, a C developer wrote this + unsafe { (*mut_self).initialize() }; //TODO this might be safe (we are in init), but does not look very good + Self { + handle: self.handle } } pub fn take(&self) -> Result { - match unsafe {osal::take_mutex(self.handle)} { - 1 => Ok(RawMutexGuard { handle: self.handle }), + osal::check_global_threading_available(); + let handle = match self.handle { + None => return Err(()), //TODO plan was that this failes silently -> implement an empty mutex guard? + Some(handle) => handle + }; + match unsafe {osal::take_mutex(handle)} { + 1 => Ok(RawMutexGuard { handle: handle }), _ => Err(()) //TODO error code } } + // Mutex guard takes care of this in its drop + // Do not offer this in API to avoid duplicate give // pub fn give(&self) -> Result<(),()> { - // match unsafe {osal::give_mutex(self.handle)} { + // osal::check_global_threading_available(); + // let handle = match self.handle { + // None => return Ok(()), + // Some(handle) => handle + // }; + // match unsafe {osal::give_mutex(handle)} { // 1 => Ok(()), // _ => Err(()) //TODO error code // } // } + + pub fn initialize(&mut self) { + let handle = unsafe {osal::create_mutex()}; + if handle == 0 as *const core::ffi::c_void { + panic!("Could not create mutex") + } + self.handle = Some(handle); + } } \ No newline at end of file diff --git a/mission_rust/src/fsrc/osal/mod.rs b/mission_rust/src/fsrc/osal/mod.rs index 3710125..7c62870 100644 --- a/mission_rust/src/fsrc/osal/mod.rs +++ b/mission_rust/src/fsrc/osal/mod.rs @@ -1,6 +1,7 @@ type TaskFunction = unsafe extern "C" fn(*mut core::ffi::c_void); //TODO verify uXX == uintXX_t +//TODO safe API extern "C" { pub fn outbyte(c: u8); @@ -30,4 +31,26 @@ extern "C" { pub fn take_mutex(mutex: *const core::ffi::c_void) -> u8; pub fn give_mutex(mutex: *const core::ffi::c_void) -> u8; + fn global_threading_available_c() -> u8; + fn enable_global_threading_c(); + fn disable_global_threading_c(); + +} + +pub fn global_threading_available() -> bool { + unsafe{global_threading_available_c() == 1} +} + +pub fn check_global_threading_available() { + if !global_threading_available() { + panic!("using queue outside of threading environment") + } +} + +pub fn enable_global_threading(){ + unsafe{enable_global_threading_c()}; +} + +pub fn disable_global_threading(){ + unsafe{disable_global_threading_c()}; } \ No newline at end of file diff --git a/mission_rust/src/fsrc/queues/mod.rs b/mission_rust/src/fsrc/queues/mod.rs index 890d6b6..457843e 100644 --- a/mission_rust/src/fsrc/queues/mod.rs +++ b/mission_rust/src/fsrc/queues/mod.rs @@ -1,46 +1,63 @@ -pub struct MessageQueue { - queue_id: *const core::ffi::c_void, +use crate::osal; + +pub struct MessageQueue { + queue_data: [Message; DEPTH], + queue_id: Option<*const core::ffi::c_void>, } pub struct MessageQueueSender { queue_id: Option<*const core::ffi::c_void>, } -impl MessageQueue { - pub fn new(depth: usize) -> Self { - unsafe { - //TODO check cast of depth - let cast_depth: u32 = u32::try_from(depth).unwrap(); //TODO check these - let element_size: u32 = u32::try_from(core::mem::size_of::()).unwrap(); - let queue_id = crate::fsrc::osal::create_queue(cast_depth, element_size); - - if queue_id == 0 as *mut core::ffi::c_void { - panic!("could not create Queue"); - } - - Self { queue_id: queue_id } +impl MessageQueue { + pub fn new() -> Self { + Self { + queue_data: [Message::default(); DEPTH], + queue_id: None, } } - pub fn get_sender(&self) -> MessageQueueSender { - let instance: MessageQueueSender = MessageQueueSender { - queue_id: Some(self.queue_id), + pub fn receive(&self) -> Option { + osal::check_global_threading_available(); + let actual_id = match self.queue_id { + None => return None, + Some(id) => id, }; - instance - } - - pub fn receive(&self) -> Result { let mut message: Message = Message::default(); let res: u8; unsafe { //message = core::mem::MaybeUninit::zeroed().assume_init(); // We only return it if the queue received something - let message_pointer: *mut core::ffi::c_void = &mut message as *mut _ as *mut core::ffi::c_void; - res = crate::fsrc::osal::queue_receive(self.queue_id, message_pointer); + let message_pointer: *mut core::ffi::c_void = + &mut message as *mut _ as *mut core::ffi::c_void; + res = osal::queue_receive(actual_id, message_pointer); } if res == 1 { - Ok(message) + Some(message) } else { - Err(()) + None + } + } + + fn initialize(&mut self) { + if self.queue_id != None { + return; + } + let cast_depth: u32 = u32::try_from(DEPTH).unwrap(); //TODO check these + let element_size: u32 = u32::try_from(core::mem::size_of::()).unwrap(); + let queue_id = unsafe { osal::create_queue(cast_depth, element_size) }; + + if queue_id == 0 as *mut core::ffi::c_void { + panic!("could not create Queue"); + } + self.queue_id = Some(queue_id); + } + + pub fn get_sender(&self) -> MessageQueueSender { + let mut_self = self as *const Self as *mut Self; //oh look, a C developer wrote this + unsafe { (*mut_self).initialize() }; //TODO this might be safe (we are in init), but does not look very good + let queue_id = self.queue_id.expect("Queue uninitialized"); + MessageQueueSender { + queue_id: Some(queue_id), } } } @@ -51,11 +68,16 @@ impl MessageQueueSender { } pub fn send(&self, message: Message) -> Result<(), ()> { - let queue_id = self.queue_id.expect("unitialized Message Queue"); + osal::check_global_threading_available(); + let queue_id = match self.queue_id { + None => return Err(()), + Some(id) => id, + }; let res: u8; unsafe { - let message_pointer: *const core::ffi::c_void = &message as *const _ as *const core::ffi::c_void; - res = crate::fsrc::osal::queue_send(queue_id, message_pointer); + let message_pointer: *const core::ffi::c_void = + &message as *const _ as *const core::ffi::c_void; + res = osal::queue_send(queue_id, message_pointer); } if res == 1 { Ok(()) diff --git a/mission_rust/src/lib.rs b/mission_rust/src/lib.rs index 462e633..590aacc 100644 --- a/mission_rust/src/lib.rs +++ b/mission_rust/src/lib.rs @@ -1,6 +1,5 @@ #![no_std] -//TODO look into using core::ffi (some types do not seem to work) //TODO os errors in API calls //TODO look into a pattern for late initialized stuff, currently using Option (can we make it compile time safe?) @@ -60,7 +59,7 @@ impl datasets::HasTypeId for HandlerData {} struct Handler { id: objectmanager::ObjectId, - command_queue: queues::MessageQueue, + command_queue: queues::MessageQueue<10>, data: datasets::OwnedDataset } @@ -94,8 +93,8 @@ impl tasks::ExecutableObjectIF for Handler { sifln!("Handler {} performs", self.id); let result = self.command_queue.receive(); match result { - Ok(message) => self.handle_message(message), - Err(_) => { + Some(message) => self.handle_message(message), + None => { sifln!("Handler {} got nothing", self.id); } } @@ -149,8 +148,9 @@ impl DatapoolOwnerIF for HandlerSender {} impl SystemObjectIF for HandlerSender { fn get_command_queue(&self) -> crate::fsrc::queues::MessageQueueSender { - queues::MessageQueueSender::new() + queues::MessageQueueSender::new() //TODO } + fn get_id(&self) -> objectmanager::ObjectId { self.id } @@ -173,7 +173,7 @@ fn mission() { let mut h1 = Handler { id: 1, - command_queue: queues::MessageQueue::new(5), + command_queue: queues::MessageQueue::new(), data: datasets::OwnedDataset::new() }; let mut h2 = HandlerSender {