use core::ffi::c_void;
use core::marker::PhantomData;
use core::mem::{size_of, MaybeUninit};
use crate::kernel::queue::{
queueQUEUE_TYPE_BASE, queueSEND_TO_BACK, queueSEND_TO_FRONT, uxQueueMessagesWaitingFromISR,
xQueueGenericCreateStatic, xQueueGenericSend, xQueueIsQueueEmptyFromISR,
xQueueIsQueueFullFromISR, xQueuePeek, xQueuePeekFromISR, xQueueReceive, xQueueReceiveFromISR,
xQueueSendToBackFromISR, xQueueSendToFrontFromISR, QueueHandle_t, StaticQueue_t,
};
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
use crate::kernel::queue::{vQueueDelete, xQueueGenericCreate};
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
use crate::sync::is_in_isr;
use crate::sync::task::TaskContext;
use crate::sync::{assert_can_block, assert_task_context};
use crate::types::*;
pub struct Queue<T: Copy> {
handle: QueueHandle_t,
capacity: UBaseType_t,
owns_handle: bool,
_marker: PhantomData<fn(T) -> T>,
}
unsafe impl<T: Copy + Send> Sync for Queue<T> {}
unsafe impl<T: Copy + Send> Send for Queue<T> {}
impl<T: Copy> Queue<T> {
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
pub fn new(_context: &TaskContext, length: usize) -> Option<Self> {
assert_task_context("Queue::new");
let (length, item_size) = Self::checked_dimensions(length)?;
let handle = unsafe { xQueueGenericCreate(length, item_size, queueQUEUE_TYPE_BASE) };
if handle.is_null() {
None
} else {
Some(Self {
handle,
capacity: length,
owns_handle: true,
_marker: PhantomData,
})
}
}
pub fn new_static(
_context: &TaskContext,
storage: &'static mut [T],
queue_buffer: &'static mut StaticQueue_t,
) -> Option<Self> {
assert_task_context("Queue::new_static");
let (length, item_size) = Self::checked_dimensions(storage.len())?;
let handle = unsafe {
xQueueGenericCreateStatic(
length,
item_size,
storage.as_mut_ptr() as *mut u8,
queue_buffer as *mut StaticQueue_t,
queueQUEUE_TYPE_BASE,
)
};
if handle.is_null() {
None
} else {
Some(Self {
handle,
capacity: length,
owns_handle: true,
_marker: PhantomData,
})
}
}
fn checked_dimensions(length: usize) -> Option<(UBaseType_t, UBaseType_t)> {
if length == 0 || size_of::<T>() == 0 {
return None;
}
let length = UBaseType_t::try_from(length).ok()?;
let item_size = UBaseType_t::try_from(size_of::<T>()).ok()?;
Some((length, item_size))
}
pub fn send(&self, context: &TaskContext, item: &T) -> bool {
self.send_timeout(context, item, portMAX_DELAY)
}
pub fn try_send(&self, context: &TaskContext, item: &T) -> bool {
self.send_timeout(context, item, 0)
}
pub fn send_timeout(&self, _context: &TaskContext, item: &T, ticks: TickType_t) -> bool {
assert_can_block("Queue::send_timeout", ticks);
unsafe {
xQueueGenericSend(
self.handle,
item as *const T as *const c_void,
ticks,
queueSEND_TO_BACK,
) == pdTRUE
}
}
pub fn send_to_front(&self, context: &TaskContext, item: &T) -> bool {
self.send_to_front_timeout(context, item, portMAX_DELAY)
}
pub fn send_to_front_timeout(
&self,
_context: &TaskContext,
item: &T,
ticks: TickType_t,
) -> bool {
assert_can_block("Queue::send_to_front_timeout", ticks);
unsafe {
xQueueGenericSend(
self.handle,
item as *const T as *const c_void,
ticks,
queueSEND_TO_FRONT,
) == pdTRUE
}
}
pub fn receive(&self, context: &TaskContext) -> Option<T> {
self.receive_timeout(context, portMAX_DELAY)
}
pub fn try_receive(&self, context: &TaskContext) -> Option<T> {
self.receive_timeout(context, 0)
}
pub fn receive_timeout(&self, _context: &TaskContext, ticks: TickType_t) -> Option<T> {
assert_can_block("Queue::receive_timeout", ticks);
let mut item = MaybeUninit::<T>::uninit();
let result = unsafe { xQueueReceive(self.handle, item.as_mut_ptr() as *mut c_void, ticks) };
if result == pdTRUE {
Some(unsafe { item.assume_init() })
} else {
None
}
}
pub fn peek(&self, context: &TaskContext) -> Option<T> {
self.peek_timeout(context, portMAX_DELAY)
}
pub fn try_peek(&self, context: &TaskContext) -> Option<T> {
self.peek_timeout(context, 0)
}
pub fn peek_timeout(&self, _context: &TaskContext, ticks: TickType_t) -> Option<T> {
assert_can_block("Queue::peek_timeout", ticks);
let mut item = MaybeUninit::<T>::uninit();
let result = unsafe { xQueuePeek(self.handle, item.as_mut_ptr() as *mut c_void, ticks) };
if result == pdTRUE {
Some(unsafe { item.assume_init() })
} else {
None
}
}
pub fn len(&self, _context: &TaskContext) -> usize {
assert_task_context("Queue::len");
unsafe { crate::kernel::queue::uxQueueMessagesWaiting(self.handle) as usize }
}
pub fn is_empty(&self, context: &TaskContext) -> bool {
self.len(context) == 0
}
pub fn available(&self, _context: &TaskContext) -> usize {
assert_task_context("Queue::available");
unsafe { crate::kernel::queue::uxQueueSpacesAvailable(self.handle) as usize }
}
pub fn is_full(&self, context: &TaskContext) -> bool {
self.available(context) == 0
}
pub fn reset(&self, _context: &TaskContext) -> bool {
assert_task_context("Queue::reset");
unsafe { crate::kernel::queue::xQueueGenericReset(self.handle, pdFALSE) == pdPASS }
}
pub unsafe fn send_from_isr(
&self,
item: &T,
higher_priority_task_woken: &mut BaseType_t,
) -> bool {
unsafe {
xQueueSendToBackFromISR(
self.handle,
item as *const T as *const c_void,
higher_priority_task_woken,
) == pdPASS
}
}
pub unsafe fn send_to_front_from_isr(
&self,
item: &T,
higher_priority_task_woken: &mut BaseType_t,
) -> bool {
unsafe {
xQueueSendToFrontFromISR(
self.handle,
item as *const T as *const c_void,
higher_priority_task_woken,
) == pdPASS
}
}
pub unsafe fn receive_from_isr(
&self,
higher_priority_task_woken: &mut BaseType_t,
) -> Option<T> {
let mut item = MaybeUninit::<T>::uninit();
let result = unsafe {
xQueueReceiveFromISR(
self.handle,
item.as_mut_ptr() as *mut c_void,
higher_priority_task_woken,
)
};
if result == pdPASS {
Some(unsafe { item.assume_init() })
} else {
None
}
}
pub unsafe fn peek_from_isr(&self) -> Option<T> {
let mut item = MaybeUninit::<T>::uninit();
let result = unsafe { xQueuePeekFromISR(self.handle, item.as_mut_ptr() as *mut c_void) };
if result == pdPASS {
Some(unsafe { item.assume_init() })
} else {
None
}
}
pub unsafe fn len_from_isr(&self) -> usize {
unsafe { uxQueueMessagesWaitingFromISR(self.handle) as usize }
}
pub unsafe fn is_empty_from_isr(&self) -> bool {
unsafe { xQueueIsQueueEmptyFromISR(self.handle) != pdFALSE }
}
pub unsafe fn is_full_from_isr(&self) -> bool {
unsafe { xQueueIsQueueFullFromISR(self.handle) != pdFALSE }
}
pub unsafe fn available_from_isr(&self) -> usize {
let waiting = unsafe { uxQueueMessagesWaitingFromISR(self.handle) };
self.capacity.saturating_sub(waiting) as usize
}
pub unsafe fn raw_handle(&self) -> QueueHandle_t {
self.handle
}
pub fn into_raw(mut self) -> QueueHandle_t {
self.owns_handle = false;
self.handle
}
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
pub fn delete(mut self, _context: &TaskContext) {
assert_task_context("Queue::delete");
unsafe { vQueueDelete(self.handle) };
self.owns_handle = false;
}
}
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
impl<T: Copy> Drop for Queue<T> {
fn drop(&mut self) {
if self.owns_handle && !is_in_isr() {
unsafe { vQueueDelete(self.handle) };
self.owns_handle = false;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn queue_dimensions_reject_zero_length_and_zero_sized_items() {
assert!(Queue::<u8>::checked_dimensions(0).is_none());
assert!(Queue::<()>::checked_dimensions(1).is_none());
assert_eq!(Queue::<u32>::checked_dimensions(3), Some((3, 4)));
}
#[test]
fn queue_send_sync_tracks_item_send() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<Queue<u32>>();
}
}