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
SemaphoreHandle_t * global_threading_semaphore = NULL;
SemaphoreHandle_t global_threading_semaphore = NULL;
uint8_t global_threading_available_c() {
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::{mutex, objectmanager};
#[derive(Copy, Clone, PartialEq)]
pub struct TypeId {
id: usize,
}
// inspired by https://github.com/jswrenn/deflect/
// inspired by Jack Wrenn at https://github.com/jswrenn/deflect/
pub trait HasTypeId {
#[inline(never)]
fn get_type_id(&self) -> TypeId {
@ -28,33 +30,34 @@ pub trait DataSetIF {
fn get_mutex(&self) -> mutex::RawMutex;
}
pub struct OwnedDataset<T: HasTypeId + Copy> {
pub struct OwnedDataset<T: HasTypeId + Clone> {
actual_data: T,
mutex: mutex::RawMutex,
}
pub struct ReferencedDataset<T: HasTypeId + Copy> {
pub struct ReferencedDataset<T: HasTypeId + Clone> {
//we use a pointer here to avoid lifetimes
actual_data: Option<*const T>,
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 {
self.actual_data.get_type_id()
}
fn get_actual_data(&self) -> &dyn HasTypeId {
// only return pointer when threading
check_global_threading_available!();
&self.actual_data
}
fn get_mutex(&self) -> mutex::RawMutex {
self.initialize();
return self.mutex;
return self.mutex.clone();
}
}
impl<T: HasTypeId + Copy + Default> OwnedDataset<T> {
impl<T: HasTypeId + Clone + Default> OwnedDataset<T> {
pub fn new() -> OwnedDataset<T> {
OwnedDataset::<T> {
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, ()> {
let _mutex_guard = match self.mutex.take() {
Err(()) => return Err(()),
Ok(guard) => guard,
};
Ok(self.actual_data)
Ok(self.actual_data.clone())
}
//TODO do we want to know if it fails?
@ -79,12 +89,10 @@ impl<T: HasTypeId + Copy + Default> OwnedDataset<T> {
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> {
ReferencedDataset::<T> {
actual_data: None,
@ -93,33 +101,36 @@ impl<T: HasTypeId + Copy + Default> ReferencedDataset<T> {
}
pub fn read(&mut self) -> Result<T, ()> {
let _mutex_guard = match self.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 }),
}
let pointer = match self.actual_data {
None => return Err(()),
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(&mut self, object_manager: &dyn objectmanager::ObjectManager, owner_of_the_set: objectmanager::ObjectId) -> Result<(), ()> {
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;
match owner.get_set(type_id) {
let other_set = match owner.get_set(type_id) {
None => {
return Err(());
}
Some(set) => {
other_set = set;
}
}
Some(set) => set,
};
//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 HasTypeId as *const T);

View File

@ -1,7 +1,11 @@
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 {
handle: Option<*const core::ffi::c_void>
handle: *const core::ffi::c_void
}
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 {
pub fn new() -> Self {
Self {
handle: None
let handle = unsafe {osal::create_mutex()};
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 {
handle: self.handle
handle: handle
}
}
pub fn take(&self) -> Result<RawMutexGuard,()> {
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 }),
match unsafe {osal::take_mutex(self.handle)} {
1 => Ok(RawMutexGuard { handle: self.handle }),
_ => Err(()) //TODO error code
}
}
@ -57,12 +50,12 @@ impl RawMutex {
// _ => 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")
impl Clone for RawMutex {
fn clone(&self) -> Self {
Self {
handle: self.handle
}
self.handle = Some(handle);
}
}

View File

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

View File

@ -103,11 +103,12 @@ impl<'a> TaskExecutor<'a> {
let object_manager = TaskObjectManager {
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() {
let _ = task.initialize(&object_manager).unwrap();
}
drop(object_manager);
crate::fsrc::osal::enable_global_threading();
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