working on thread safety, preparing for static RTOS

This commit is contained in:
Ulrich Mohr 2023-12-12 22:55:22 +01:00
parent c3be10903b
commit 07d9f52b8d
6 changed files with 142 additions and 45 deletions

View File

@ -4,6 +4,29 @@
// TODO namespace the names // 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); } const char *get_task_name() { return pcTaskGetName(NULL); }
void stop_it() { taskENTER_CRITICAL(); } void stop_it() { taskENTER_CRITICAL(); }

View File

@ -39,7 +39,7 @@ pub struct ReferencedDataset<T: HasTypeId + Copy> {
mutex: Option<mutex::RawMutex>, mutex: Option<mutex::RawMutex>,
} }
impl<T: HasTypeId + Copy> DataSetIF for OwnedDataset<T> { impl<T: HasTypeId + Copy + Default> DataSetIF for OwnedDataset<T> {
fn get_type_id(&self) -> TypeId { fn get_type_id(&self) -> TypeId {
self.actual_data.get_type_id() self.actual_data.get_type_id()
} }
@ -49,6 +49,7 @@ impl<T: HasTypeId + Copy> DataSetIF for OwnedDataset<T> {
} }
fn get_mutex(&self) -> mutex::RawMutex { fn get_mutex(&self) -> mutex::RawMutex {
self.initialize();
return self.mutex; return self.mutex;
} }
} }
@ -77,6 +78,10 @@ impl<T: HasTypeId + Copy + Default> OwnedDataset<T> {
}; };
self.actual_data = data; self.actual_data = data;
} }
fn initialize(&mut self) {
}
} }
impl<T: HasTypeId + Copy + Default> ReferencedDataset<T> { impl<T: HasTypeId + Copy + Default> ReferencedDataset<T> {

View File

@ -1,10 +1,7 @@
use super::osal; 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 { pub struct RawMutex {
handle: *const core::ffi::c_void, handle: Option<*const core::ffi::c_void>
} }
pub struct RawMutexGuard { 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 // or maybe lazy later on, TBD if lazy is needed or we can guarantee init to be called
impl RawMutex { impl RawMutex {
pub fn new() -> Self { pub fn new() -> Self {
let handle = unsafe {osal::create_mutex()};
Self { 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<RawMutexGuard,()> { pub fn take(&self) -> Result<RawMutexGuard,()> {
match unsafe {osal::take_mutex(self.handle)} { osal::check_global_threading_available();
1 => Ok(RawMutexGuard { handle: self.handle }), 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 _ => 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<(),()> { // 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(()), // 1 => Ok(()),
// _ => Err(()) //TODO error code // _ => 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);
}
} }

View File

@ -1,6 +1,7 @@
type TaskFunction = unsafe extern "C" fn(*mut core::ffi::c_void); type TaskFunction = unsafe extern "C" fn(*mut core::ffi::c_void);
//TODO verify uXX == uintXX_t //TODO verify uXX == uintXX_t
//TODO safe API
extern "C" { extern "C" {
pub fn outbyte(c: u8); pub fn outbyte(c: u8);
@ -30,4 +31,26 @@ extern "C" {
pub fn take_mutex(mutex: *const core::ffi::c_void) -> u8; pub fn take_mutex(mutex: *const core::ffi::c_void) -> u8;
pub fn give_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()};
} }

View File

@ -1,46 +1,63 @@
pub struct MessageQueue { use crate::osal;
queue_id: *const core::ffi::c_void,
pub struct MessageQueue<const DEPTH: usize> {
queue_data: [Message; DEPTH],
queue_id: Option<*const core::ffi::c_void>,
} }
pub struct MessageQueueSender { pub struct MessageQueueSender {
queue_id: Option<*const core::ffi::c_void>, queue_id: Option<*const core::ffi::c_void>,
} }
impl MessageQueue { impl<const DEPTH: usize> MessageQueue<DEPTH> {
pub fn new(depth: usize) -> Self { pub fn new() -> Self {
unsafe { Self {
//TODO check cast of depth queue_data: [Message::default(); DEPTH],
let cast_depth: u32 = u32::try_from(depth).unwrap(); //TODO check these queue_id: None,
let element_size: u32 = u32::try_from(core::mem::size_of::<Message>()).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 }
} }
} }
pub fn get_sender(&self) -> MessageQueueSender { pub fn receive(&self) -> Option<Message> {
let instance: MessageQueueSender = MessageQueueSender { osal::check_global_threading_available();
queue_id: Some(self.queue_id), let actual_id = match self.queue_id {
None => return None,
Some(id) => id,
}; };
instance
}
pub fn receive(&self) -> Result<Message, ()> {
let mut message: Message = Message::default(); let mut message: Message = Message::default();
let res: u8; let res: u8;
unsafe { unsafe {
//message = core::mem::MaybeUninit::zeroed().assume_init(); // We only return it if the queue received something //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; let message_pointer: *mut core::ffi::c_void =
res = crate::fsrc::osal::queue_receive(self.queue_id, message_pointer); &mut message as *mut _ as *mut core::ffi::c_void;
res = osal::queue_receive(actual_id, message_pointer);
} }
if res == 1 { if res == 1 {
Ok(message) Some(message)
} else { } 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::<Message>()).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<(), ()> { 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; let res: u8;
unsafe { unsafe {
let message_pointer: *const core::ffi::c_void = &message as *const _ as *const core::ffi::c_void; let message_pointer: *const core::ffi::c_void =
res = crate::fsrc::osal::queue_send(queue_id, message_pointer); &message as *const _ as *const core::ffi::c_void;
res = osal::queue_send(queue_id, message_pointer);
} }
if res == 1 { if res == 1 {
Ok(()) Ok(())

View File

@ -1,6 +1,5 @@
#![no_std] #![no_std]
//TODO look into using core::ffi (some types do not seem to work)
//TODO os errors in API calls //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?) //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 { struct Handler {
id: objectmanager::ObjectId, id: objectmanager::ObjectId,
command_queue: queues::MessageQueue, command_queue: queues::MessageQueue<10>,
data: datasets::OwnedDataset<HandlerData> data: datasets::OwnedDataset<HandlerData>
} }
@ -94,8 +93,8 @@ impl tasks::ExecutableObjectIF for Handler {
sifln!("Handler {} performs", self.id); sifln!("Handler {} performs", self.id);
let result = self.command_queue.receive(); let result = self.command_queue.receive();
match result { match result {
Ok(message) => self.handle_message(message), Some(message) => self.handle_message(message),
Err(_) => { None => {
sifln!("Handler {} got nothing", self.id); sifln!("Handler {} got nothing", self.id);
} }
} }
@ -149,8 +148,9 @@ impl DatapoolOwnerIF for HandlerSender {}
impl SystemObjectIF for HandlerSender { impl SystemObjectIF for HandlerSender {
fn get_command_queue(&self) -> crate::fsrc::queues::MessageQueueSender { fn get_command_queue(&self) -> crate::fsrc::queues::MessageQueueSender {
queues::MessageQueueSender::new() queues::MessageQueueSender::new() //TODO
} }
fn get_id(&self) -> objectmanager::ObjectId { fn get_id(&self) -> objectmanager::ObjectId {
self.id self.id
} }
@ -173,7 +173,7 @@ fn mission() {
let mut h1 = Handler { let mut h1 = Handler {
id: 1, id: 1,
command_queue: queues::MessageQueue::new(5), command_queue: queues::MessageQueue::new(),
data: datasets::OwnedDataset::new() data: datasets::OwnedDataset::new()
}; };
let mut h2 = HandlerSender { let mut h2 = HandlerSender {