From 581cb9c76ab1d7bc784512361ea31dfc199d5314 Mon Sep 17 00:00:00 2001 From: David Coles Date: Sun, 20 Oct 2024 22:42:39 -0700 Subject: [PATCH] Provide safe wrappers for thread notification API Furi provides access to [FreeRTOS Task Notification](https://freertos.org/Documentation/02-Kernel/02-Kernel-features/03-Direct-to-task-notifications/01-Task-notifications) feature which can be used for syncronization between threads. Task notifications act as `u32` bitmask, so can be used to signal multiple potential interesting states on the target thread. --- crates/flipperzero/src/furi/thread.rs | 94 +++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 4 deletions(-) diff --git a/crates/flipperzero/src/furi/thread.rs b/crates/flipperzero/src/furi/thread.rs index b143ddbc..90a593d2 100644 --- a/crates/flipperzero/src/furi/thread.rs +++ b/crates/flipperzero/src/furi/thread.rs @@ -17,7 +17,11 @@ use alloc::{ sync::Arc, }; -use flipperzero_sys as sys; +use flipperzero_sys::{ + self as sys, FuriFlag_FuriFlagNoClear, FuriFlag_FuriFlagWaitAll, FuriFlag_FuriFlagWaitAny, +}; + +use crate::furi::time::Duration; use crate::furi::time::Duration; @@ -233,11 +237,93 @@ pub fn sleep_ticks(duration: Duration) { } /// A unique identifier for a running thread. -#[cfg(feature = "alloc")] -#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] -#[allow(dead_code)] +#[derive(Copy, Clone, PartialEq, Eq)] pub struct ThreadId(sys::FuriThreadId); +impl ThreadId { + /// Get the `ThreadId` for the current thread. + pub fn current() -> Self { + ThreadId(unsafe { sys::furi_thread_get_current_id() }) + } + + pub unsafe fn from_furi_thread(thread: *mut sys::FuriThread) -> ThreadId { + ThreadId(sys::furi_thread_get_id(thread)) + } +} + +/// Set one-or-more notification flags on a thread. +/// +/// Returns the value of the thread's notification flags after the specified `flags` have been set. +pub fn set_flags(thread_id: ThreadId, flags: u32) -> Result { + let result = unsafe { sys::furi_thread_flags_set(thread_id.0, flags) }; + + if result & sys::FuriFlag_FuriFlagError != 0 { + return Err((result as i32).into()); + } + + Ok(result) +} + +/// Clear one-or-more of the current thread's notification flags. +/// +/// Returns the value of the current thread's notification flags after the specified `flags` have been cleared. +pub fn clear_flags(flags: u32) -> Result { + let result = unsafe { sys::furi_thread_flags_clear(flags) }; + + if result & sys::FuriFlag_FuriFlagError != 0 { + return Err((result as i32).into()); + } + + Ok(result) +} + +/// Get the value of the current thread's notification flags. +pub fn get_flags() -> Result { + let result = unsafe { sys::furi_thread_flags_get() }; + + if result & sys::FuriFlag_FuriFlagError != 0 { + return Err((result as i32).into()); + } + + Ok(result) +} + +/// Wait for up-to `timeout` for a change to any of the specified notification `flags` for the current thread. +/// +/// If `clear`, then the specified flags will be cleared after a notification is received. +pub fn wait_any_flags( + flags: u32, + clear: bool, + timeout: Duration, +) -> Result { + let options = FuriFlag_FuriFlagWaitAny | (if !clear { FuriFlag_FuriFlagNoClear } else { 0 }); + let result = unsafe { sys::furi_thread_flags_wait(flags, options, timeout.0) }; + + if result & sys::FuriFlag_FuriFlagError != 0 { + return Err((result as i32).into()); + } + + Ok(result) +} + +/// Wait for up-to `timeout` for a change to all of the specified notification `flags` for the current thread. +/// +/// If `clear`, then the specified flags will be cleared after a notification is received. +pub fn wait_all_flags( + flags: u32, + clear: bool, + timeout: Duration, +) -> Result { + let options = FuriFlag_FuriFlagWaitAll | (if !clear { FuriFlag_FuriFlagNoClear } else { 0 }); + let result = unsafe { sys::furi_thread_flags_wait(flags, options, timeout.0) }; + + if result & sys::FuriFlag_FuriFlagError != 0 { + return Err((result as i32).into()); + } + + Ok(result) +} + /// A handle to a thread. #[cfg(feature = "alloc")] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]