Skip to main content

nblf_queue/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(not(any(feature = "std", test)), no_std)]
3#![deny(missing_docs)]
4#![deny(clippy::missing_safety_doc, clippy::undocumented_unsafe_blocks)]
5#![warn(unsafe_op_in_unsafe_fn)]
6
7#[cfg(any(feature = "alloc", test))]
8extern crate alloc;
9
10#[cfg(any(feature = "std", test))]
11extern crate std;
12
13#[macro_use]
14pub(crate) mod utils;
15mod array;
16pub mod core;
17#[cfg(any(feature = "alloc", test))]
18mod owned;
19#[cfg(feature = "pool")]
20mod pool;
21mod sync;
22#[cfg(test)]
23mod tests;
24
25#[cfg(feature = "pool")]
26pub use array::PooledStaticQueue;
27pub use array::StaticQueue;
28#[cfg(all(any(feature = "alloc", test), feature = "pool"))]
29pub use owned::PooledQueue;
30#[cfg(any(feature = "alloc", test))]
31pub use owned::Queue;
32
33/// The main trait used to interface with a MPMCQueue.
34/// All implementations provided by this crate are atomic and non-blocking.
35/// Fallible operations of this trait may fail spuriously.
36///
37/// # Examples
38///
39/// ```rust
40/// use nblf_queue::{StaticQueue, MPMCQueue};
41///
42/// let q: StaticQueue<_, 2> = StaticQueue::new();
43///
44/// assert!(q.push(&42).is_ok());
45/// assert!(q.push(&2).is_ok());
46///
47/// assert_eq!(q.len(), 2);
48/// assert!(q.is_full());
49///
50/// assert_eq!(q.force_push(&0), Some(&42));
51/// assert!(q.is_full());
52///
53/// assert_eq!(q.pop(), Some(&2));
54/// assert_eq!(q.pop(), Some(&0));
55/// assert_eq!(q.len(), 0);
56/// assert!(q.is_empty());
57/// ```
58pub trait MPMCQueue {
59    /// The item stored in the queue
60    type Item;
61
62    /// Attempts to push an item into the queue.
63    /// Returns the item as an error if the queue is full.
64    ///
65    /// # Examples
66    ///
67    /// ```rust
68    /// use nblf_queue::{StaticQueue, MPMCQueue};
69    ///
70    /// let q: StaticQueue<_, 2> = StaticQueue::new();
71    ///
72    /// assert!(q.push(&10).is_ok());
73    /// assert!(q.push(&20).is_ok());
74    /// assert_eq!(q.push(&30), Err(&30));
75    /// assert_eq!(q.pop(), Some(&10));
76    /// ```
77    fn push(&self, item: Self::Item) -> Result<(), Self::Item>;
78    /// Attempts to pop an item from the queue.
79    /// Returns `None` if the queue was empty.
80    ///
81    /// # Examples
82    ///
83    /// ```rust
84    /// use nblf_queue::{StaticQueue, MPMCQueue};
85    ///
86    /// let q: StaticQueue<_, 2> = StaticQueue::new();
87    ///
88    /// assert!(q.push(&10).is_ok());
89    /// assert!(q.push(&42).is_ok());
90    /// assert_eq!(q.pop(), Some(&10));
91    /// assert_eq!(q.pop(), Some(&42));
92    /// assert!(q.pop().is_none());
93    /// ```
94    fn pop(&self) -> Option<Self::Item>;
95    /// Returns the current len of the queue.
96    /// The returned value may be stale under concurrent access and should not be used for synchronization.
97    fn len(&self) -> usize;
98    /// Returns the total capacity of the queue.
99    fn capacity(&self) -> usize;
100
101    /// Indicates whether the queue is empty.
102    /// The returned value may be stale under concurrent access and should not be used for synchronization.
103    fn is_empty(&self) -> bool {
104        self.len() == 0
105    }
106
107    /// Indicates whether the queue is full.
108    /// The returned value may be stale under concurrent access and should not be used for synchronization.
109    fn is_full(&self) -> bool {
110        self.len() == self.capacity()
111    }
112
113    /// Pushes an item into the queue, removing an existing item if the queue is full.
114    ///
115    /// If the queue is full, this method will remove items until space becomes available.
116    /// The last removed item is returned.
117    ///
118    /// Under contention this method may spin for some time, however it will never block.
119    ///
120    /// # Examples
121    ///
122    /// ```rust
123    /// use nblf_queue::{StaticQueue, MPMCQueue};
124    ///
125    /// let q: StaticQueue<_, 2> = StaticQueue::new();
126    ///
127    /// assert!(q.force_push(&10).is_none());
128    /// assert!(q.force_push(&20).is_none());
129    /// assert_eq!(q.force_push(&30), Some(&10));
130    /// assert_eq!(q.pop(), Some(&20));
131    /// ```
132    fn force_push(&self, item: Self::Item) -> Option<Self::Item> {
133        let mut item_container = None;
134        self.force_push_and_do(item, |item| {
135            item_container.replace(item);
136        });
137        item_container
138    }
139
140    /// Pushes an item into the queue, removing an existing item if the queue is full.
141    ///
142    /// If the queue is full, this method will remove items until space becomes available.
143    /// The provided closure will be called on each removed item.
144    ///
145    /// Under contention this method may spin for some time, however it will never block, provided the passed closure does not block.
146    ///
147    /// # Examples
148    ///
149    /// ```rust
150    /// use nblf_queue::{StaticQueue, MPMCQueue};
151    ///
152    /// let q: StaticQueue<_, 2> = StaticQueue::new();
153    ///
154    /// q.force_push_and_do(&10, |item| {});
155    /// q.force_push_and_do(&20, |item| {});
156    /// q.force_push_and_do(&30, |item| {
157    ///     assert_eq!(item, &10)
158    /// });
159    /// assert_eq!(q.pop(), Some(&20));
160    /// ```
161    fn force_push_and_do<F>(&self, mut item: Self::Item, mut f: F)
162    where
163        F: FnMut(Self::Item),
164    {
165        let mut backoff = crate::utils::Backoff::new();
166        while let Err(item_) = self.push(item) {
167            item = item_;
168            backoff.backoff();
169            if let Some(next_popped_item) = self.pop() {
170                f(next_popped_item);
171            }
172        }
173    }
174}