Skip to content

Instantly share code, notes, and snippets.

@dcoles
Last active February 4, 2025 07:06
Show Gist options
  • Save dcoles/a9eb333be6a2b8c249fe31be360c3abb to your computer and use it in GitHub Desktop.
Save dcoles/a9eb333be6a2b8c249fe31be360c3abb to your computer and use it in GitHub Desktop.
C Handles in Rust
//! Furi Event Flag.
use core::cell::UnsafeCell;
use core::ops::Deref;
use core::ptr::NonNull;
use crate::furi::time::FuriDuration;
use crate::furi::Error;
use flipperzero_sys as sys;
use flipperzero_sys::furi::{Handle, Status};
/// Event Flag.
///
/// This type only exists in reference form. It isn't possible to directly construct it.
#[repr(transparent)]
pub struct EventFlag {
raw: UnsafeCell<sys::FuriEventFlag>,
}
impl EventFlag {
/// Set flags.
///
/// # Warning
/// The result of this function can be flags that you've just asked to
/// set or not if someone was waiting for them and asked to clear it.
/// It is highly recommended to read the `furi_event_flag_set`
/// and `xEventGroupSetBits`` source code.
pub fn set(&self, flags: u32) -> Result<u32, Error> {
Status::from(unsafe { sys::furi_event_flag_set(self.as_ptr(), flags) })
.into_result()
.map(|s| s as u32)
}
/// Clear flags
pub fn clear(&self, flags: u32) -> Result<u32, Error> {
Status::from(unsafe { sys::furi_event_flag_clear(self.as_ptr(), flags) })
.into_result()
.map(|s| s as u32)
}
/// Get flags.
pub fn get(&self) -> u32 {
unsafe { sys::furi_event_flag_get(self.as_ptr()) }
}
/// Wait for up-to `timeout` for a change to any of the specified notification `flags`.
///
/// If `clear`, then the specified flags will be cleared after a notification is received.
pub fn wait_any_flags(
&self,
flags: u32,
clear: bool,
timeout: FuriDuration,
) -> Result<u32, Error> {
let mut options = sys::FuriFlagWaitAny;
if !clear {
options |= sys::FuriFlagNoClear;
}
Status::from(unsafe {
sys::furi_event_flag_wait(self.as_ptr(), flags, options.0, timeout.0)
})
.into_result()
.map(|s| s as u32)
}
/// Wait for up-to `timeout` for a change to all of the specified notification `flags`.
///
/// If `clear`, then the specified flags will be cleared after a notification is received.
pub fn wait_all_flags(
&self,
flags: u32,
clear: bool,
timeout: FuriDuration,
) -> Result<u32, Error> {
let mut options = sys::FuriFlagWaitAll;
if !clear {
options |= sys::FuriFlagNoClear;
}
Status::from(unsafe {
sys::furi_event_flag_wait(self.as_ptr(), flags, options.0, timeout.0)
})
.into_result()
.map(|s| s as u32)
}
}
impl Handle for EventFlag {
type CType = sys::FuriEventFlag;
unsafe fn from_ptr<'a>(ptr: *mut Self::CType) -> &'a Self {
// SAFETY: pointer is non-null, points to a valid `CType`
// and `CType` has the same memory layout as `EventFlag`.
unsafe { &*ptr.cast() }
}
fn as_ptr(&self) -> *mut Self::CType {
self.as_ptr()
}
}
/// Owned Event Flag.
///
/// This is an Event Flag where we are responsible for deallocation.
pub struct OwnedEventFlag {
raw: NonNull<sys::FuriEventFlag>,
}
impl OwnedEventFlag {
pub fn new() -> Self {
Self {
// SAFETY: Alloc always returns valid non-null pointer or triggers `furi_crash`.
raw: unsafe { NonNull::new_unchecked(sys::furi_event_flag_alloc()) },
}
}
pub fn as_event_flag(&self) -> &'_ EventFlag {
unsafe { EventFlag::from_ptr(self.raw.as_ptr()) }
}
}
impl Deref for OwnedEventFlag {
type Target = EventFlag;
fn deref(&self) -> &Self::Target {
self.as_event_flag()
}
}
impl AsRef<EventFlag> for OwnedEventFlag {
fn as_ref(&self) -> &EventFlag {
self.as_event_flag()
}
}
impl Drop for OwnedEventFlag {
fn drop(&mut self) {
// SAFETY: Pointer is valid and non-null
unsafe { sys::furi_event_flag_free(self.as_ptr()) }
}
}
/// A C-API handle.
pub trait Handle {
/// C handle type (typically opaque).
type CType;
/// Convert raw C-handle into reference.
///
/// # Safety
/// The user must ensure that the pointer is non-null and points to a valid `CType`.
/// It must remain valid for the lifetime of the resulting reference.
///
/// Due to the nature of this API, it is very easy to create a reference with an unbound lifetime.
/// It should always inherit an explicit lifetime from wherever it obtains the pointer from.
unsafe fn from_ptr<'a>(ptr: *mut Self::CType) -> &'a Self;
/// Get pointer to raw C handle.
///
/// This pointer must not be `free`d or otherwise invalidated.
/// It must not be referenced after the `Self` has been dropped.
fn as_ptr(&self) -> *mut Self::CType;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment