Skip to content

Commit

Permalink
Conversion from/to raw handle (#57)
Browse files Browse the repository at this point in the history
* Add methods to get raw handle back

* Remove unsafe mutex deconstruction

* Mutex conversion from/to inner parts and raw handle

* Add safety doc comment to mutex `from_raw_handle`

* Remove 'unsafe' where it is not necessary

* Add safety section to all public unsafe functions
  • Loading branch information
agerasev committed Mar 28, 2023
1 parent f8e80c0 commit bbacc44
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 49 deletions.
2 changes: 1 addition & 1 deletion freertos-rust/src/isr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ impl InterruptContext {
}
}

pub unsafe fn get_task_field_mut(&self) -> FreeRtosBaseTypeMutPtr {
pub fn get_task_field_mut(&self) -> FreeRtosBaseTypeMutPtr {
self.x_higher_priority_task_woken as *mut _
}
}
Expand Down
96 changes: 61 additions & 35 deletions freertos-rust/src/mutex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,30 +26,15 @@ where
}
}

impl<T> MutexImpl<T, MutexNormal> {
/// Create a new mutex with the given inner value
pub fn new(t: T) -> Result<Self, FreeRtosError> {
Ok(MutexImpl {
mutex: MutexNormal::create()?,
data: UnsafeCell::new(t),
})
}
}

impl<T> MutexImpl<T, MutexRecursive> {
/// Create a new recursive mutex with the given inner value
pub fn new(t: T) -> Result<Self, FreeRtosError> {
Ok(MutexImpl {
mutex: MutexRecursive::create()?,
data: UnsafeCell::new(t),
})
}
}

impl<T, M> MutexImpl<T, M>
where
M: MutexInnerImpl,
{
/// Create a new mutex with the given inner value
pub fn new(value: T) -> Result<Self, FreeRtosError> {
Ok(Self::from_parts(M::create()?, value))
}

/// Try to obtain a lock and mutable access to our inner value
pub fn lock<D: DurationTicks>(&self, max_wait: D) -> Result<MutexGuard<T, M>, FreeRtosError> {
self.mutex.take(max_wait)?;
Expand All @@ -62,23 +47,35 @@ where

/// Consume the mutex and return its inner value
pub fn into_inner(self) -> T {
// Manually deconstruct the structure, because it implements Drop
// and we cannot move the data value out of it.
unsafe {
let (mutex, data) = {
let Self {
ref mutex,
ref data,
} = self;
(ptr::read(mutex), ptr::read(data))
};
mem::forget(self);

drop(mutex);

data.into_inner()
self.into_parts().1
}

/// Get mutable reference to inner value.
///
/// This method does not lock the mutex because mutable reference guarantees exclusive access.
pub fn get_mut(&mut self) -> &mut T {
self.data.get_mut()
}

/// Create owning mutex from non-owning mutex and inner value.
///
/// It is safe to pass an already locked `mutex` although it is not recommended.
pub fn from_parts(mutex: M, value: T) -> Self {
Self {
mutex,
data: UnsafeCell::new(value),
}
}

/// Split owning mutex into non-owning mutex and inner value.
pub fn into_parts(self) -> (M, T) {
(self.mutex, self.data.into_inner())
}

/// Get mutable reference to inner non-owning mutex.
pub fn inner_mutex_mut(&mut self) -> &mut M {
&mut self.mutex
}
}

/// Holds the mutex until we are dropped
Expand Down Expand Up @@ -126,6 +123,15 @@ where
fn create() -> Result<Self, FreeRtosError>;
fn take<D: DurationTicks>(&self, max_wait: D) -> Result<(), FreeRtosError>;
fn give(&self);

/// # Safety
///
/// `handle` must be a valid FreeRTOS mutex handle.
///
/// The type of `handle` (normal or recursive mutex) must match the type
/// of instance being created ([`MutexNormal`] or [`MutexRecursive`] respectively).
unsafe fn from_raw_handle(handle: FreeRtosSemaphoreHandle) -> Self;
fn raw_handle(&self) -> FreeRtosSemaphoreHandle;
}

pub struct MutexNormal(FreeRtosSemaphoreHandle);
Expand Down Expand Up @@ -154,6 +160,16 @@ impl MutexInnerImpl for MutexNormal {
freertos_rs_give_semaphore(self.0);
}
}

#[inline]
unsafe fn from_raw_handle(handle: FreeRtosSemaphoreHandle) -> Self {
Self(handle)
}

#[inline]
fn raw_handle(&self) -> FreeRtosSemaphoreHandle {
self.0
}
}

impl Drop for MutexNormal {
Expand Down Expand Up @@ -194,6 +210,16 @@ impl MutexInnerImpl for MutexRecursive {
freertos_rs_give_recursive_semaphore(self.0);
}
}

#[inline]
unsafe fn from_raw_handle(handle: FreeRtosSemaphoreHandle) -> Self {
Self(handle)
}

#[inline]
fn raw_handle(&self) -> FreeRtosSemaphoreHandle {
self.0
}
}

impl Drop for MutexRecursive {
Expand Down
9 changes: 9 additions & 0 deletions freertos-rust/src/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,22 @@ impl<T: Sized + Copy> Queue<T> {
})
}

/// # Safety
///
/// `handle` must be a valid FreeRTOS regular queue handle (not semaphore or mutex).
///
/// The item size of the queue must match the size of `T`.
#[inline]
pub unsafe fn from_raw_handle(handle: FreeRtosQueueHandle) -> Self {
Self {
queue: handle,
item_type: PhantomData,
}
}
#[inline]
pub fn raw_handle(&self) -> FreeRtosQueueHandle {
self.queue
}

/// Send an item to the end of the queue. Wait for the queue to have empty space for it.
pub fn send<D: DurationTicks>(&self, item: T, max_wait: D) -> Result<(), FreeRtosError> {
Expand Down
10 changes: 10 additions & 0 deletions freertos-rust/src/semaphore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,20 @@ impl Semaphore {
}
}

/// # Safety
///
/// `handle` must be a valid FreeRTOS semaphore handle.
///
/// Only binary or counting semaphore is expected here.
/// To create mutex from raw handle use [`crate::mutex::MutexInnerImpl::from_raw_handle`].
#[inline]
pub unsafe fn from_raw_handle(handle: FreeRtosSemaphoreHandle) -> Self {
Self { semaphore: handle }
}
#[inline]
pub fn raw_handle(&self) -> FreeRtosSemaphoreHandle {
self.semaphore
}

/// Lock this semaphore in a RAII fashion
pub fn lock<D: DurationTicks>(&self, max_wait: D) -> Result<SemaphoreGuard, FreeRtosError> {
Expand Down
7 changes: 7 additions & 0 deletions freertos-rust/src/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,17 @@ impl Task {
}
}

/// # Safety
///
/// `handle` must be a valid FreeRTOS task handle.
#[inline]
pub unsafe fn from_raw_handle(handle: FreeRtosTaskHandle) -> Self {
Self { task_handle: handle }
}
#[inline]
pub fn raw_handle(&self) -> FreeRtosTaskHandle {
self.task_handle
}

pub fn suspend_all() {
unsafe {
Expand Down
28 changes: 15 additions & 13 deletions freertos-rust/src/timers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ unsafe impl Sync for Timer {}
/// for that queue to get unblocked.
pub struct Timer {
handle: FreeRtosTimerHandle,
detached: bool,
}

/// Helper builder for a new software timer.
Expand Down Expand Up @@ -71,8 +70,17 @@ impl Timer {
}

/// Create a timer from a raw handle.
///
/// # Safety
///
/// `handle` must be a valid FreeRTOS timer handle.
#[inline]
pub unsafe fn from_raw_handle(handle: FreeRtosTimerHandle) -> Self {
Self { handle, detached: false }
Self { handle }
}
#[inline]
pub fn raw_handle(&self) -> FreeRtosTimerHandle {
self.handle
}

unsafe fn spawn_inner<'a>(
Expand Down Expand Up @@ -109,10 +117,7 @@ impl Timer {
extern "C" fn timer_callback(handle: FreeRtosTimerHandle) -> () {
unsafe {
{
let timer = Timer {
handle: handle,
detached: true,
};
let timer = Timer { handle };
if let Ok(callback_ptr) = timer.get_id() {
let b = Box::from_raw(callback_ptr as *mut Box<dyn Fn(Timer)>);
b(timer);
Expand All @@ -124,7 +129,6 @@ impl Timer {

Ok(Timer {
handle: timer_handle as *const _,
detached: false,
})
}

Expand Down Expand Up @@ -198,8 +202,10 @@ impl Timer {
/// will consume the memory.
///
/// Can be used for timers that will never be changed and don't need to stay in scope.
pub unsafe fn detach(mut self) {
self.detached = true;
///
/// This method is safe because resource leak is safe in Rust.
pub fn detach(self) {
mem::forget(self);
}

fn get_id(&self) -> Result<FreeRtosVoidPtr, FreeRtosError> {
Expand All @@ -210,10 +216,6 @@ impl Timer {
impl Drop for Timer {
#[allow(unused_must_use)]
fn drop(&mut self) {
if self.detached == true {
return;
}

unsafe {
if let Ok(callback_ptr) = self.get_id() {
// free the memory
Expand Down
3 changes: 3 additions & 0 deletions freertos-rust/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ pub fn shim_sanity_check() -> Result<(), TypeSizeError> {
Ok(())
}

/// # Safety
///
/// `str` must be a pointer to the beginning of nul-terminated sequence of bytes.
#[cfg(any(feature = "time", feature = "hooks", feature = "sync"))]
pub unsafe fn str_from_c_string(str: *const u8) -> Result<String, FreeRtosError> {
let mut buf = Vec::new();
Expand Down

0 comments on commit bbacc44

Please sign in to comment.