From 07d9f52b8d752425d3c9fee3ed47f209b913fca9 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Tue, 12 Dec 2023 22:55:22 +0100 Subject: [PATCH] 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 {