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(feature = "dynamic")]
18mod growable;
19#[cfg(any(feature = "alloc", test))]
20mod owned;
21#[cfg(feature = "pool")]
22mod pool;
23mod sync;
24#[cfg(test)]
25mod tests;
26
27#[cfg(feature = "pool")]
28pub use array::PooledStaticQueue;
29pub use array::StaticQueue;
30#[cfg(feature = "dynamic")]
31pub use growable::DynamicQueue;
32#[cfg(all(feature = "dynamic", feature = "pool"))]
33pub use growable::PooledDynamicQueue;
34#[cfg(all(feature = "alloc", feature = "pool"))]
35pub use owned::PooledQueue;
36#[cfg(any(feature = "alloc", test))]
37pub use owned::Queue;
38
39/// The main trait used to interface with a `MPMCQueue`.
40/// All implementations provided by this crate are atomic and non-blocking.
41/// Fallible operations of this trait may fail spuriously.
42///
43/// # Examples
44///
45/// ```rust
46/// use nblf_queue::{StaticQueue, MPMCQueue};
47///
48/// let q: StaticQueue<_, 2> = StaticQueue::new();
49///
50/// assert!(q.push(&42).is_ok());
51/// assert!(q.push(&2).is_ok());
52///
53/// assert_eq!(q.len(), 2);
54/// assert!(q.is_full());
55///
56/// assert_eq!(q.force_push(&0), Some(&42));
57/// assert!(q.is_full());
58///
59/// assert_eq!(q.pop(), Some(&2));
60/// assert_eq!(q.pop(), Some(&0));
61/// assert_eq!(q.len(), 0);
62/// assert!(q.is_empty());
63/// ```
64pub trait MPMCQueue {
65 /// The item stored in the queue
66 type Item;
67
68 /// Attempts to push an item into the queue.
69 /// Returns the item as an error if the queue is full.
70 ///
71 /// # Examples
72 ///
73 /// ```rust
74 /// use nblf_queue::{StaticQueue, MPMCQueue};
75 ///
76 /// let q: StaticQueue<_, 2> = StaticQueue::new();
77 ///
78 /// assert!(q.push(&10).is_ok());
79 /// assert!(q.push(&20).is_ok());
80 /// assert_eq!(q.push(&30), Err(&30));
81 /// assert_eq!(q.pop(), Some(&10));
82 /// ```
83 fn push(&self, item: Self::Item) -> Result<(), Self::Item>;
84 /// Attempts to pop an item from the queue.
85 /// Returns `None` if the queue was empty.
86 ///
87 /// # Examples
88 ///
89 /// ```rust
90 /// use nblf_queue::{StaticQueue, MPMCQueue};
91 ///
92 /// let q: StaticQueue<_, 2> = StaticQueue::new();
93 ///
94 /// assert!(q.push(&10).is_ok());
95 /// assert!(q.push(&42).is_ok());
96 /// assert_eq!(q.pop(), Some(&10));
97 /// assert_eq!(q.pop(), Some(&42));
98 /// assert!(q.pop().is_none());
99 /// ```
100 fn pop(&self) -> Option<Self::Item>;
101 /// Returns the current len of the queue.
102 /// The returned value may be stale under concurrent access and should not be used for synchronization.
103 fn len(&self) -> usize;
104 /// Returns the total capacity of the queue.
105 fn capacity(&self) -> usize;
106
107 /// Indicates whether the queue is empty.
108 /// The returned value may be stale under concurrent access and should not be used for synchronization.
109 fn is_empty(&self) -> bool {
110 self.len() == 0
111 }
112
113 /// Indicates whether the queue is full.
114 /// The returned value may be stale under concurrent access and should not be used for synchronization.
115 fn is_full(&self) -> bool {
116 self.len() == self.capacity()
117 }
118
119 /// Pushes an item into the queue, removing an existing item if the queue is full.
120 ///
121 /// If the queue is full, this method will remove items until space becomes available.
122 /// The last removed item is returned.
123 ///
124 /// Under contention this method may spin for some time, however it will never block.
125 ///
126 /// # Examples
127 ///
128 /// ```rust
129 /// use nblf_queue::{StaticQueue, MPMCQueue};
130 ///
131 /// let q: StaticQueue<_, 2> = StaticQueue::new();
132 ///
133 /// assert!(q.force_push(&10).is_none());
134 /// assert!(q.force_push(&20).is_none());
135 /// assert_eq!(q.force_push(&30), Some(&10));
136 /// assert_eq!(q.pop(), Some(&20));
137 /// ```
138 fn force_push(&self, item: Self::Item) -> Option<Self::Item> {
139 let mut item_container = None;
140 self.force_push_and_do(item, |item| {
141 item_container.replace(item);
142 });
143 item_container
144 }
145
146 /// Pushes an item into the queue, removing an existing item if the queue is full.
147 ///
148 /// If the queue is full, this method will remove items until space becomes available.
149 /// The provided closure will be called on each removed item.
150 ///
151 /// Under contention this method may spin for some time, however it will never block, provided the passed closure does not block.
152 ///
153 /// # Examples
154 ///
155 /// ```rust
156 /// use nblf_queue::{StaticQueue, MPMCQueue};
157 ///
158 /// let q: StaticQueue<_, 2> = StaticQueue::new();
159 ///
160 /// q.force_push_and_do(&10, |item| {});
161 /// q.force_push_and_do(&20, |item| {});
162 /// q.force_push_and_do(&30, |item| {
163 /// assert_eq!(item, &10)
164 /// });
165 /// assert_eq!(q.pop(), Some(&20));
166 /// ```
167 fn force_push_and_do<F>(&self, mut item: Self::Item, mut f: F)
168 where
169 F: FnMut(Self::Item),
170 {
171 let mut backoff = utils::Backoff::new();
172 while let Err(item_) = self.push(item) {
173 item = item_;
174 backoff.backoff();
175 if let Some(next_popped_item) = self.pop() {
176 f(next_popped_item);
177 }
178 }
179 }
180}
181
182/// An extension trait for `MPMCQueue` that allows dynamic growth of the queue.
183///
184/// This trait makes **no** guarantees regarding the synchronization model or
185/// blocking behavior of the `grow_by` method itself. It only guarantees that
186/// the core `MPMCQueue` operations maintain their original guarantees.
187///
188/// # Examples
189///
190/// ```rust
191/// #[cfg(feature = "dynamic")]
192/// fn run() {
193/// use nblf_queue::{DynamicQueue, MPMCQueue, Growable};
194///
195/// let q = DynamicQueue::new(1);
196///
197/// assert!(q.push(1).is_ok());
198/// assert!(q.is_full());
199///
200/// assert!(q.grow_by(2));
201///
202/// assert_eq!(q.capacity(), 3);
203/// assert!(!q.is_full());
204///
205/// assert!(q.push(2).is_ok());
206/// assert!(q.push(3).is_ok());
207///
208/// assert_eq!(q.pop(), Some(1));
209/// assert_eq!(q.pop(), Some(2));
210/// assert_eq!(q.pop(), Some(3));
211///
212/// assert!(q.is_empty());
213/// }
214///
215/// #[cfg(feature = "dynamic")]
216/// run();
217/// ```
218pub trait Growable: MPMCQueue {
219 /// Attempts to grow the capacity of the queue by `by` slots.
220 ///
221 /// **Note:** This method may block or fail spuriously.
222 /// Further a growth event may not be considered finished until some time after the call to `grow_by`.
223 ///
224 /// Returns `true` if the resize was successfull, or `false` if
225 /// it failed. Failure can occur due to allocator exhaustion, thread
226 /// contention, or other implementation-specific conditions.
227 fn grow_by(&self, by: usize) -> bool;
228}