Last active
February 4, 2025 07:06
-
-
Save dcoles/a9eb333be6a2b8c249fe31be360c3abb to your computer and use it in GitHub Desktop.
C Handles in Rust
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! 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()) } | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// 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