use core::ffi::c_void;
use core::ptr::NonNull;
use flipperzero_sys as sys;
use flipperzero_sys::furi::Status;
use crate::furi;
use crate::furi::time::FuriDuration;
pub struct MessageQueue<M: Sized> {
hnd: NonNull<sys::FuriMessageQueue>,
_marker: core::marker::PhantomData<M>,
}
impl<M: Sized> MessageQueue<M> {
pub fn new(capacity: usize) -> Self {
Self {
hnd: unsafe {
NonNull::new_unchecked(sys::furi_message_queue_alloc(
capacity as u32,
core::mem::size_of::<M>() as u32,
))
},
_marker: core::marker::PhantomData::<M>,
}
}
pub fn put(&self, msg: M, timeout: FuriDuration) -> furi::Result<()> {
let mut msg = core::mem::ManuallyDrop::new(msg);
let status: Status = unsafe {
sys::furi_message_queue_put(
self.hnd.as_ptr(),
&mut msg as *mut _ as *const c_void,
timeout.as_ticks(),
)
.into()
};
let _ = status.into_result()?;
Ok(())
}
pub fn get(&self, timeout: FuriDuration) -> furi::Result<M> {
let mut out = core::mem::MaybeUninit::<M>::uninit();
let status: Status = unsafe {
sys::furi_message_queue_get(
self.hnd.as_ptr(),
out.as_mut_ptr() as *mut c_void,
timeout.as_ticks(),
)
.into()
};
let _ = status.into_result()?;
Ok(unsafe { out.assume_init() })
}
pub fn capacity(&self) -> usize {
unsafe { sys::furi_message_queue_get_capacity(self.hnd.as_ptr()) as usize }
}
pub fn len(&self) -> usize {
unsafe { sys::furi_message_queue_get_count(self.hnd.as_ptr()) as usize }
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn space(&self) -> usize {
unsafe { sys::furi_message_queue_get_space(self.hnd.as_ptr()) as usize }
}
}
impl<M: Sized> Drop for MessageQueue<M> {
fn drop(&mut self) {
while !self.is_empty() {
match self.get(FuriDuration::MAX) {
Ok(msg) => drop(msg),
Err(_) => break, }
}
unsafe { sys::furi_message_queue_free(self.hnd.as_ptr()) }
}
}
#[flipperzero_test::tests]
mod tests {
use super::*;
use super::MessageQueue;
#[test]
fn capacity() {
let queue = MessageQueue::new(3);
assert_eq!(queue.len(), 0);
assert_eq!(queue.space(), 3);
assert_eq!(queue.capacity(), 3);
queue.put(2, FuriDuration::from_millis(1)).unwrap();
assert_eq!(queue.len(), 1);
assert_eq!(queue.space(), 2);
assert_eq!(queue.capacity(), 3);
queue.put(4, FuriDuration::from_millis(1)).unwrap();
queue.put(6, FuriDuration::from_millis(1)).unwrap();
assert_eq!(queue.len(), 3);
assert_eq!(queue.space(), 0);
assert_eq!(queue.capacity(), 3);
assert_eq!(
queue.put(7, FuriDuration::from_millis(1)),
Err(furi::Error::TimedOut),
);
assert_eq!(queue.get(FuriDuration::from_millis(1)), Ok(2));
assert_eq!(queue.len(), 2);
assert_eq!(queue.space(), 1);
assert_eq!(queue.capacity(), 3);
}
}