Skip to main content

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;