Skip to main content

leodos_libcfs/os/
queue.rs

1//! Safe, idiomatic wrappers for OSAL Message Queue APIs.
2//!
3//! This module provides a generic, type-safe `Queue<T>` for intra-application
4//! message passing. The `Queue` struct is an RAII wrapper that ensures the
5//! underlying OSAL resource is properly cleaned up.
6
7use crate::error::{CfsError, OsalError, Result};
8use crate::ffi;
9use crate::os::id::OsalId;
10use crate::os::util::c_name_from_str;
11use crate::string_from_c_buf;
12use crate::status::check;
13use core::marker::PhantomData;
14use core::mem::{self, MaybeUninit};
15use core::ops::Drop;
16use heapless::String;
17
18/// Properties of a message queue, returned by `Queue::get_info`.
19#[derive(Debug, Clone)]
20pub struct QueueProp {
21    /// The registered name of the queue.
22    pub name: String<{ ffi::OS_MAX_API_NAME as usize }>,
23    /// The OSAL ID of the task that created the queue.
24    pub creator: OsalId,
25}
26
27/// A type-safe message queue for communicating between tasks within an application.
28///
29/// The type `T` must be `Copy` and `Sized` to be sent over the queue, as the
30/// underlying OSAL API works with raw byte copies.
31#[derive(Debug)]
32pub struct Queue<T: Copy + Sized> {
33    id: OsalId,
34    _phantom: PhantomData<T>,
35}
36
37impl<T: Copy + Sized> Queue<T> {
38    /// Creates a new message queue.
39    ///
40    /// # Arguments
41    /// * `name`: A unique string to identify the queue.
42    /// * `depth`: The maximum number of messages the queue can hold.
43    pub fn new(name: &str, depth: usize) -> Result<Self> {
44        let c_name = c_name_from_str(name)?;
45        let mut queue_id = MaybeUninit::uninit();
46
47        let status = unsafe {
48            ffi::OS_QueueCreate(
49                queue_id.as_mut_ptr(),
50                c_name.as_ptr(),
51                depth,
52                mem::size_of::<T>(),
53                0,
54            )
55        };
56        check(status)?;
57
58        Ok(Self {
59            id: OsalId(unsafe { queue_id.assume_init() }),
60            _phantom: PhantomData,
61        })
62    }
63
64    /// Puts a message onto the queue.
65    ///
66    /// This operation is non-blocking. If the queue is full, an error is returned.
67    ///
68    /// # Arguments
69    /// * `message`: A reference to the message data to send.
70    pub fn put(&self, message: &T) -> Result<()> {
71        let status = unsafe {
72            ffi::OS_QueuePut(
73                self.id.0,
74                message as *const T as *const _,
75                mem::size_of::<T>(),
76                0,
77            )
78        };
79        check(status)?;
80        Ok(())
81    }
82
83    /// Retrieves a message from the queue.
84    ///
85    /// This operation can block until a message is available, depending on the timeout.
86    ///
87    /// # Arguments
88    /// * `timeout_ms`: Timeout in milliseconds. Can be a positive value,
89    ///   `ffi::OS_CHECK` (0) for a non-blocking poll, or `ffi::OS_PEND` (-1)
90    ///   to block indefinitely.
91    pub fn get(&self, timeout_ms: i32) -> Result<T> {
92        let mut message = MaybeUninit::<T>::uninit();
93        let mut size_copied = MaybeUninit::uninit();
94
95        let status = unsafe {
96            ffi::OS_QueueGet(
97                self.id.0,
98                message.as_mut_ptr() as *mut _,
99                mem::size_of::<T>(),
100                size_copied.as_mut_ptr(),
101                timeout_ms,
102            )
103        };
104        check(status)?;
105
106        let size_copied = unsafe { size_copied.assume_init() };
107        if size_copied != mem::size_of::<T>() {
108            return Err(CfsError::Osal(OsalError::QueueInvalidSize));
109        }
110
111        Ok(unsafe { message.assume_init() })
112    }
113
114    /// Returns the underlying OSAL ID of the queue.
115    pub fn id(&self) -> OsalId {
116        self.id
117    }
118
119    /// Finds an existing queue ID by its name.
120    pub fn get_id_by_name(name: &str) -> Result<OsalId> {
121        let c_name = c_name_from_str(name)?;
122        let mut queue_id = MaybeUninit::uninit();
123        check(unsafe { ffi::OS_QueueGetIdByName(queue_id.as_mut_ptr(), c_name.as_ptr()) })?;
124        Ok(OsalId(unsafe { queue_id.assume_init() }))
125    }
126
127    /// Retrieves information about this queue.
128    pub fn get_info(&self) -> Result<QueueProp> {
129        let mut prop = MaybeUninit::<ffi::OS_queue_prop_t>::uninit();
130        check(unsafe { ffi::OS_QueueGetInfo(self.id.0, prop.as_mut_ptr()) })?;
131        let prop = unsafe { prop.assume_init() };
132
133        Ok(QueueProp {
134            name: string_from_c_buf(&prop.name)?,
135            creator: OsalId(prop.creator),
136        })
137    }
138}
139
140impl<T: Copy + Sized> Drop for Queue<T> {
141    /// Deletes the OSAL queue when the `Queue` object goes out of scope.
142    fn drop(&mut self) {
143        let _ = unsafe { ffi::OS_QueueDelete(self.id.0) };
144    }
145}