ove/
queue.rs

1// Copyright (C) 2026 Kamil Lulko <kamil.lulko@gmail.com>
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4//
5// This file is part of oveRTOS.
6
7//! Type-safe FIFO queue for oveRTOS.
8//!
9//! [`Queue<T, N>`] is a fixed-capacity, thread-safe FIFO that transfers items of
10//! type `T` (which must be `Copy`) between threads or between ISR and thread
11//! contexts. `N` is the maximum number of items the queue can hold.
12
13use core::fmt;
14use core::marker::PhantomData;
15use core::mem;
16
17use crate::bindings;
18use crate::error::{Error, Result};
19
20/// Type-safe FIFO queue with compile-time capacity.
21///
22/// `T` must be a plain-old-data type (Copy) because items are transferred
23/// through the C API via raw byte copies. `N` is the queue capacity.
24pub struct Queue<T: Copy, const N: usize> {
25    handle: bindings::ove_queue_t,
26    _marker: PhantomData<T>,
27}
28
29impl<T: Copy, const N: usize> Queue<T, N> {
30    /// Create a queue via heap allocation (only in heap mode).
31    #[cfg(not(zero_heap))]
32    pub fn new() -> Result<Self> {
33        let mut handle: bindings::ove_queue_t = core::ptr::null_mut();
34        let rc = unsafe {
35            bindings::ove_queue_create(&mut handle, mem::size_of::<T>(), N as u32)
36        };
37        Error::from_code(rc)?;
38        Ok(Self {
39            handle,
40            _marker: PhantomData,
41        })
42    }
43
44    /// Create from caller-provided static storage and buffer.
45    ///
46    /// # Safety
47    /// - `storage` must outlive the `Queue` and not be shared.
48    /// - `buffer` must point to at least `N * size_of::<T>()` bytes
49    ///   and outlive the `Queue`.
50    #[cfg(zero_heap)]
51    pub unsafe fn from_static(
52        storage: *mut bindings::ove_queue_storage_t,
53        buffer: *mut core::ffi::c_void,
54    ) -> Result<Self> {
55        let mut handle: bindings::ove_queue_t = core::ptr::null_mut();
56        let rc = unsafe {
57            bindings::ove_queue_init(
58                &mut handle,
59                storage,
60                buffer,
61                mem::size_of::<T>(),
62                N as u32,
63            )
64        };
65        Error::from_code(rc)?;
66        Ok(Self {
67            handle,
68            _marker: PhantomData,
69        })
70    }
71
72    /// Send an item to the queue, blocking up to `timeout_ms` if the queue is full.
73    ///
74    /// # Errors
75    /// Returns [`Error::QueueFull`] or [`Error::Timeout`] if the item cannot be
76    /// enqueued within `timeout_ms`.
77    pub fn send(&self, item: &T, timeout_ms: u32) -> Result<()> {
78        let rc = unsafe {
79            bindings::ove_queue_send(
80                self.handle,
81                item as *const T as *const _,
82                timeout_ms,
83            )
84        };
85        Error::from_code(rc)
86    }
87
88    /// Receive an item from the queue, blocking up to `timeout_ms` if the queue is empty.
89    ///
90    /// # Errors
91    /// Returns [`Error::Timeout`] if no item is available within `timeout_ms`.
92    pub fn receive(&self, timeout_ms: u32) -> Result<T> {
93        let mut item: mem::MaybeUninit<T> = mem::MaybeUninit::uninit();
94        let rc = unsafe {
95            bindings::ove_queue_receive(
96                self.handle,
97                item.as_mut_ptr() as *mut _,
98                timeout_ms,
99            )
100        };
101        Error::from_code(rc)?;
102        Ok(unsafe { item.assume_init() })
103    }
104
105    /// Send an item from an ISR context (non-blocking, returns immediately if full).
106    ///
107    /// # Errors
108    /// Returns [`Error::QueueFull`] if the queue has no space.
109    pub fn send_from_isr(&self, item: &T) -> Result<()> {
110        let rc = unsafe {
111            bindings::ove_queue_send_from_isr(self.handle, item as *const T as *const _)
112        };
113        Error::from_code(rc)
114    }
115
116    /// Receive an item from an ISR context (non-blocking, returns immediately if empty).
117    ///
118    /// # Errors
119    /// Returns [`Error::Timeout`] if the queue is empty.
120    pub fn receive_from_isr(&self) -> Result<T> {
121        let mut item: mem::MaybeUninit<T> = mem::MaybeUninit::uninit();
122        let rc = unsafe {
123            bindings::ove_queue_receive_from_isr(self.handle, item.as_mut_ptr() as *mut _)
124        };
125        Error::from_code(rc)?;
126        Ok(unsafe { item.assume_init() })
127    }
128}
129
130impl<T: Copy, const N: usize> fmt::Debug for Queue<T, N> {
131    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132        f.debug_struct("Queue")
133            .field("handle", &format_args!("{:p}", self.handle))
134            .finish()
135    }
136}
137
138impl<T: Copy, const N: usize> Drop for Queue<T, N> {
139    fn drop(&mut self) {
140        if self.handle.is_null() { return; }
141        #[cfg(not(zero_heap))]
142        unsafe { bindings::ove_queue_destroy(self.handle) }
143        #[cfg(zero_heap)]
144        unsafe { bindings::ove_queue_deinit(self.handle) }
145    }
146}
147
148// SAFETY: Queue wraps a ove_queue_t handle. Send/receive are thread-safe
149// RTOS calls. Create/destroy are single-threaded (lifecycle guarantee).
150unsafe impl<T: Copy + Send, const N: usize> Send for Queue<T, N> {}
151unsafe impl<T: Copy + Send, const N: usize> Sync for Queue<T, N> {}