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}