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(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}