prosa_utils/queue.rs
1//! Module for ProSA internal queueing utilitary
2
3pub use crate::hash::IsInteger;
4
5/// Atomic queue implementation
6pub(crate) mod lockfree;
7
8/// Queue for single producer and multiple consumers
9pub mod spmc;
10
11/// Queue for Multiple producers and single consumer
12pub mod mpsc;
13
14/// Error define for Queues
15/// Use by utilitary queues an their implementation in ProSA.
16#[derive(Debug, Eq, thiserror::Error, PartialOrd, PartialEq)]
17pub enum QueueError<T> {
18 /// Error indicating that the queue is empty
19 #[error("The queue is empty")]
20 Empty,
21 /// Error indicating that the queue is full
22 #[error("The queue is full, it contain {1} items")]
23 Full(T, usize),
24 /// Can't retrieve the element
25 #[error("Can't retrieve the element {0}")]
26 Retrieve(usize),
27}
28
29/// Trait to define all information getter from the queue
30///
31/// ```no_run
32/// use prosa_utils::queue::QueueChecker;
33///
34/// fn queue_checker<Q>(queue: Q)
35/// where
36/// Q: QueueChecker<usize>,
37/// {
38/// if queue.is_empty() {
39/// assert!(!queue.is_full());
40/// assert_eq!(0, queue.len());
41/// } else if queue.is_full() {
42/// assert!(!queue.is_empty());
43/// assert_eq!(queue.max_capacity(), queue.len());
44/// }
45/// }
46/// ```
47pub trait QueueChecker<P: IsInteger> {
48 /// Checks if the queue is empty.
49 /// Prefer this method over `len() != 0`
50 fn is_empty(&self) -> bool;
51 /// Checks if the queue is full.
52 /// Prefer this method over `len() != max_capacity()`
53 fn is_full(&self) -> bool;
54 /// Returns the number of item in the queue.
55 fn len(&self) -> P;
56 /// Returns the maximum item capacity of the queue.
57 fn max_capacity(&self) -> P;
58}
59
60/// Macro to define queue inner method related to `QueueChecker` trait
61macro_rules! impl_queue_checker {
62 ( $p:ty ) => {
63 fn is_empty(&self) -> bool {
64 self.get_head() == self.get_tail()
65 }
66
67 fn is_full(&self) -> bool {
68 (self.get_tail() + 1) % (N as $p) == self.get_head()
69 }
70
71 fn len(&self) -> $p {
72 let head = self.get_head();
73 let tail = self.get_tail();
74
75 if tail >= head {
76 tail - head
77 } else {
78 (self.max_capacity() - head) + tail
79 }
80 }
81
82 fn max_capacity(&self) -> $p {
83 N as $p
84 }
85 };
86}
87pub(crate) use impl_queue_checker;
88
89#[macro_export]
90/// Macro of an expression to know if an id is still contain by the queue that have a circular buffer
91///
92/// The buffer have two pointer that indicate head and tail.
93/// Every ID in this range is considered for the queue.
94///
95/// In the following example:
96/// - `o` represent active items
97/// - `-` represent inactive items
98///
99/// If the head is before the tail:
100/// ```text
101/// [ |start |end ]
102/// [------oooooooooooo------]
103/// ```
104///
105/// If the head is after the tail:
106/// ```text
107/// [ |end |start]
108/// [oooooo------------oooooo]
109/// ```
110macro_rules! id_in_queue {
111 ( $id:ident, $head:ident, $tail:ident ) => {
112 ($head > $tail && ($id >= $head || $id < $tail)) || ($id >= $head && $id < $tail)
113 };
114}
115pub use id_in_queue;