mutexes and datasets

This commit is contained in:
Ulrich Mohr 2024-02-01 17:10:42 +01:00
parent 2d8705917c
commit 3bf0667cbb
5 changed files with 57 additions and 52 deletions

View File

@ -4,7 +4,7 @@
// TODO namespace the names // TODO namespace the names
SemaphoreHandle_t * global_threading_semaphore = NULL; SemaphoreHandle_t global_threading_semaphore = NULL;
uint8_t global_threading_available_c() { uint8_t global_threading_available_c() {
if (global_threading_semaphore == NULL) { if (global_threading_semaphore == NULL) {

View File

@ -1,12 +1,14 @@
use super::{mutex, objectmanager}; use crate::check_global_threading_available;
use super::objectmanager::SystemObjectIF; use super::objectmanager::SystemObjectIF;
use super::{mutex, objectmanager};
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub struct TypeId { pub struct TypeId {
id: usize, id: usize,
} }
// inspired by https://github.com/jswrenn/deflect/ // inspired by Jack Wrenn at https://github.com/jswrenn/deflect/
pub trait HasTypeId { pub trait HasTypeId {
#[inline(never)] #[inline(never)]
fn get_type_id(&self) -> TypeId { fn get_type_id(&self) -> TypeId {
@ -28,33 +30,34 @@ pub trait DataSetIF {
fn get_mutex(&self) -> mutex::RawMutex; fn get_mutex(&self) -> mutex::RawMutex;
} }
pub struct OwnedDataset<T: HasTypeId + Copy> { pub struct OwnedDataset<T: HasTypeId + Clone> {
actual_data: T, actual_data: T,
mutex: mutex::RawMutex, mutex: mutex::RawMutex,
} }
pub struct ReferencedDataset<T: HasTypeId + Copy> { pub struct ReferencedDataset<T: HasTypeId + Clone> {
//we use a pointer here to avoid lifetimes //we use a pointer here to avoid lifetimes
actual_data: Option<*const T>, actual_data: Option<*const T>,
mutex: Option<mutex::RawMutex>, mutex: Option<mutex::RawMutex>,
} }
impl<T: HasTypeId + Copy + Default> DataSetIF for OwnedDataset<T> { impl<T: HasTypeId + Clone + 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()
} }
fn get_actual_data(&self) -> &dyn HasTypeId { fn get_actual_data(&self) -> &dyn HasTypeId {
// only return pointer when threading
check_global_threading_available!();
&self.actual_data &self.actual_data
} }
fn get_mutex(&self) -> mutex::RawMutex { fn get_mutex(&self) -> mutex::RawMutex {
self.initialize(); return self.mutex.clone();
return self.mutex;
} }
} }
impl<T: HasTypeId + Copy + Default> OwnedDataset<T> { impl<T: HasTypeId + Clone + Default> OwnedDataset<T> {
pub fn new() -> OwnedDataset<T> { pub fn new() -> OwnedDataset<T> {
OwnedDataset::<T> { OwnedDataset::<T> {
actual_data: T::default(), actual_data: T::default(),
@ -62,12 +65,19 @@ impl<T: HasTypeId + Copy + Default> OwnedDataset<T> {
} }
} }
pub fn new_default(default_values: T) -> OwnedDataset<T> {
OwnedDataset::<T> {
actual_data: default_values,
mutex: mutex::RawMutex::new(),
}
}
pub fn read(&mut self) -> Result<T, ()> { pub fn read(&mut self) -> Result<T, ()> {
let _mutex_guard = match self.mutex.take() { let _mutex_guard = match self.mutex.take() {
Err(()) => return Err(()), Err(()) => return Err(()),
Ok(guard) => guard, Ok(guard) => guard,
}; };
Ok(self.actual_data) Ok(self.actual_data.clone())
} }
//TODO do we want to know if it fails? //TODO do we want to know if it fails?
@ -79,12 +89,10 @@ impl<T: HasTypeId + Copy + Default> OwnedDataset<T> {
self.actual_data = data; self.actual_data = data;
} }
fn initialize(&mut self) { fn initialize(&mut self) {}
}
} }
impl<T: HasTypeId + Copy + Default> ReferencedDataset<T> { impl<T: HasTypeId + Clone + Default> ReferencedDataset<T> {
pub fn new() -> ReferencedDataset<T> { pub fn new() -> ReferencedDataset<T> {
ReferencedDataset::<T> { ReferencedDataset::<T> {
actual_data: None, actual_data: None,
@ -93,33 +101,36 @@ impl<T: HasTypeId + Copy + Default> ReferencedDataset<T> {
} }
pub fn read(&mut self) -> Result<T, ()> { pub fn read(&mut self) -> Result<T, ()> {
let _mutex_guard = match self.mutex { let _mutex_guard = match &self.mutex {
None => return Err(()), None => return Err(()),
Some(mutex) => match mutex.take() { Some(mutex) => match mutex.take() {
Err(()) => return Err(()), Err(()) => return Err(()),
Ok(guard) => guard, Ok(guard) => guard,
}, },
}; };
match self.actual_data { let pointer = match self.actual_data {
None => Err(()), None => return Err(()),
Some(data) => Ok(unsafe { *data }), Some(data) => data,
} };
// we are only allowed to use the pointer during threading
check_global_threading_available!();
Ok(unsafe { (*pointer).clone() })
} }
//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(
pub fn initialize(&mut self, object_manager: &dyn objectmanager::ObjectManager, owner_of_the_set: objectmanager::ObjectId) -> Result<(), ()> { &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 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 temp: T = T::default(); //TODO find nicer solution whithout local instance and trait bound to Default
let type_id = temp.get_type_id(); let type_id = temp.get_type_id();
let other_set: &dyn DataSetIF; let other_set = match owner.get_set(type_id) {
match owner.get_set(type_id) {
None => { None => {
return Err(()); return Err(());
} }
Some(set) => { Some(set) => set,
other_set = set; };
}
}
//pointer cast is safe because we checked the type_id //pointer cast is safe because we checked the type_id
//getting pointer to avoid lifetime check //getting pointer to avoid lifetime check
self.actual_data = Some(other_set.get_actual_data() as *const dyn HasTypeId as *const T); self.actual_data = Some(other_set.get_actual_data() as *const dyn HasTypeId as *const T);

View File

@ -1,7 +1,11 @@
use super::osal; use super::osal;
// for now, this is an implementation based on FreeRTOS, where we can preallocate mutexes
// this allows us to assume that mutexes are valid starting from their creation until end of runtime
// and so we do not use the global_threading lock
pub struct RawMutex { pub struct RawMutex {
handle: Option<*const core::ffi::c_void> handle: *const core::ffi::c_void
} }
pub struct RawMutexGuard { pub struct RawMutexGuard {
@ -15,31 +19,20 @@ impl Drop for RawMutexGuard {
} }
} }
//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 { impl RawMutex {
pub fn new() -> Self { pub fn new() -> Self {
Self { let handle = unsafe {osal::create_mutex()};
handle: None if handle == 0 as *const core::ffi::c_void {
panic!("Could not create mutex")
} }
}
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 { Self {
handle: self.handle handle: handle
} }
} }
pub fn take(&self) -> Result<RawMutexGuard,()> { pub fn take(&self) -> Result<RawMutexGuard,()> {
osal::check_global_threading_available(); match unsafe {osal::take_mutex(self.handle)} {
let handle = match self.handle { 1 => Ok(RawMutexGuard { handle: 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
} }
} }
@ -57,12 +50,12 @@ impl RawMutex {
// _ => Err(()) //TODO error code // _ => Err(()) //TODO error code
// } // }
// } // }
}
pub fn initialize(&mut self) { impl Clone for RawMutex {
let handle = unsafe {osal::create_mutex()}; fn clone(&self) -> Self {
if handle == 0 as *const core::ffi::c_void { Self {
panic!("Could not create mutex") handle: self.handle
} }
self.handle = Some(handle);
} }
} }

View File

@ -81,7 +81,7 @@ impl MessageQueueSender {
}; };
let res: u8; let res: u8;
unsafe { unsafe {
// safe beacuse: // safe because:
// OS will read not more than length of message queue elements // OS will read not more than length of message queue elements
// queue was created with size_of::<Message> as length of message queue elements // queue was created with size_of::<Message> as length of message queue elements
// in MessageQueue::initialize // in MessageQueue::initialize

View File

@ -103,11 +103,12 @@ impl<'a> TaskExecutor<'a> {
let object_manager = TaskObjectManager { let object_manager = TaskObjectManager {
tasks: unsafe { slice::from_raw_parts(self.tasks.as_ptr(), self.tasks.len()) }, tasks: unsafe { slice::from_raw_parts(self.tasks.as_ptr(), self.tasks.len()) },
}; };
// init uses unsafe methods and checks against the lock, so we need to enable it here
crate::fsrc::osal::enable_global_threading();
for task in self.tasks.iter_mut() { for task in self.tasks.iter_mut() {
let _ = task.initialize(&object_manager).unwrap(); let _ = task.initialize(&object_manager).unwrap();
} }
drop(object_manager); drop(object_manager);
crate::fsrc::osal::enable_global_threading();
for task in self.tasks.iter_mut() { for task in self.tasks.iter_mut() {
// we give away a raw pointer, to be called by an OS task // we give away a raw pointer, to be called by an OS task
// while this is generally very broken, we use a reference tied // while this is generally very broken, we use a reference tied