Skip to main content

nblf_queue/growable/
queue.rs

1use alloc::boxed::Box;
2use core::{marker::PhantomData, ptr::null_mut};
3
4use crossbeam_utils::CachePadded;
5
6use crate::{
7    MPMCQueue,
8    Resize,
9    core::{
10        AsPackedValue,
11        queue::QueueCore,
12        slots::{Auto, SlotType},
13    },
14    growable::NewSized,
15    owned::buffer::BoxedBuffer,
16    sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering},
17    utils::Backoff,
18};
19
20/// A lock-free non blocking queue, that may dynamically grow.
21pub(crate) struct GrowableQueueCore<T, Q, S = Auto> {
22    cores: [AtomicPtr<Q>; 2],
23    push_epoch: CachePadded<AtomicUsize>,
24    pop_epoch: CachePadded<AtomicUsize>,
25    active_pushes: CachePadded<[AtomicUsize; 2]>,
26    active_reads: CachePadded<[AtomicUsize; 2]>,
27    is_resizing: AtomicBool,
28    _slot: PhantomData<(S, T)>,
29}
30
31impl<T, Q> GrowableQueueCore<T, Q, Auto>
32where
33    Q: NewSized,
34{
35    /// Constructs a new `Queue` with capacity `size` and slot type `S`.
36    /// `T` must fit into the slot type `S`
37    pub(crate) fn with_slot<S>(size: usize) -> GrowableQueueCore<T, Q, S> {
38        GrowableQueueCore {
39            cores: [
40                AtomicPtr::new(Box::into_raw(Box::new(Q::with_size(size)))),
41                AtomicPtr::new(Box::into_raw(Box::new(Q::with_size(1)))),
42            ],
43            active_pushes: [AtomicUsize::new(0), AtomicUsize::new(0)].into(),
44            active_reads: [AtomicUsize::new(0), AtomicUsize::new(0)].into(),
45            push_epoch: AtomicUsize::new(0).into(),
46            pop_epoch: AtomicUsize::new(0).into(),
47            is_resizing: AtomicBool::new(false),
48            _slot: PhantomData,
49        }
50    }
51}
52
53impl<T, Q, S> Drop for GrowableQueueCore<T, Q, S> {
54    fn drop(&mut self) {
55        let left = self.cores[0].swap(null_mut(), Ordering::Acquire);
56        // Safety:
57        // No concurrent drops of this ds can happen.
58        // This queue was allocated in `new` or in `grow_by` with `Box::into_raw` and was not deallocated since then.
59        _ = unsafe { Box::from_raw(left) };
60
61        let right = self.cores[1].swap(null_mut(), Ordering::Acquire);
62        // Safety:
63        // No concurrent drops of this ds can happen.
64        // This queue was allocated in `new` or in `grow_by` with `Box::into_raw` and was not deallocated since then.
65        _ = unsafe { Box::from_raw(right) };
66    }
67}
68
69impl<T, Q, S> Resize for GrowableQueueCore<T, Q, S>
70where
71    Q: NewSized + MPMCQueue<Item = T>,
72{
73    fn resize(&self, size: usize) -> bool {
74        if size == 0 {
75            return false;
76        }
77        let pop_epoch = self.pop_epoch.load(Ordering::Acquire);
78        let push_epoch = self.push_epoch.load(Ordering::Acquire);
79
80        if pop_epoch != push_epoch {
81            return false;
82        }
83
84        if self.active_reads[(push_epoch + 1) % 2].load(Ordering::Acquire) != 0 {
85            // could happen if some thread started reading before pop_epoch got updated
86            return false;
87        }
88
89        if self.is_resizing.swap(true, Ordering::AcqRel) {
90            return false;
91        }
92
93        if self.push_epoch.load(Ordering::Acquire) != push_epoch {
94            // could happen if an entire resize happens between load and this check
95            self.is_resizing.store(false, Ordering::Release);
96            return false;
97        }
98
99        // at this poitn we know that
100        // a) no concurrent resize is happening
101        // b) since pop_epoch == push_epoch the old queue is empty.
102        // c) since pop_epoch == push_epoch AND active_reads == 0, we know that active_reads is STILL 0, becasue noone will acces the stale queue
103
104        let old_idx = (push_epoch + 1) % 2;
105        let mut backoff = Backoff::new();
106
107        while self.active_reads[old_idx].load(Ordering::Acquire) != 0 {
108            backoff.backoff();
109        }
110
111        debug_assert_eq!(
112            self.active_pushes[(push_epoch + 1) % 2].load(Ordering::SeqCst),
113            0
114        );
115
116        let new_queue = Box::into_raw(Box::new(Q::with_size(size)));
117
118        // Safety:
119        // since pop_epoch == push_epoch all concurrent threads acces the queue at push_epoch % 2.
120        // pop ensures that no pushes are in flight to the old queue anymore and that it is empty. We can safely drop it.
121        let old_queue = self.cores[(push_epoch + 1) % 2].swap(new_queue, Ordering::AcqRel);
122        self.push_epoch.fetch_add(1, Ordering::Release);
123
124        // Safety:
125        // old_queue was ocnstucted from a Box::into_raw and is dropped only once, as ensured by epoch guards
126        let q = unsafe { Box::from_raw(old_queue) };
127
128        debug_assert!(q.pop().is_none());
129
130        self.is_resizing.store(false, Ordering::Release);
131        true
132    }
133}
134
135impl<T, Q, S> GrowableQueueCore<T, Q, S> {
136    fn get_queue(&self, epoch: usize) -> &Q {
137        let queue = self.cores[epoch % 2].load(Ordering::Acquire);
138        // Safety:
139        // It is guranteed by `grow_by` that no concurrent mutable access can happen to any queue in cores.
140        // It is safe to access it concurrently via shared ref, as long as queue core is Sync.
141        unsafe { &*queue }
142    }
143
144    fn register_reader(&self, target_epoch: usize) -> bool {
145        self.active_reads[target_epoch % 2].fetch_add(1, Ordering::Release);
146
147        let current_push = self.push_epoch.load(Ordering::SeqCst);
148        let current_pop = self.pop_epoch.load(Ordering::SeqCst);
149
150        // It is safe to read if the target epoch is still structurally active
151        if target_epoch != current_push && target_epoch != current_pop {
152            self.deregister_reader(target_epoch);
153            return false;
154        }
155        true
156    }
157
158    fn deregister_reader(&self, epoch: usize) {
159        self.active_reads[epoch % 2].fetch_sub(1, Ordering::Release);
160    }
161}
162
163impl<T, Q, S> MPMCQueue for GrowableQueueCore<T, Q, S>
164where
165    Q: MPMCQueue<Item = T>,
166{
167    type Item = T;
168
169    fn push(&self, item: Self::Item) -> Result<(), Self::Item> {
170        loop {
171            let push_epoch = self.push_epoch.load(Ordering::Acquire);
172            self.active_pushes[push_epoch % 2].fetch_add(1, Ordering::Release);
173
174            if self.push_epoch.load(Ordering::SeqCst) == push_epoch {
175                let r = self.get_queue(push_epoch).push(item);
176
177                self.active_pushes[push_epoch % 2].fetch_sub(1, Ordering::Release);
178                return r;
179            }
180            self.active_pushes[push_epoch % 2].fetch_sub(1, Ordering::Release);
181        }
182    }
183
184    fn pop(&self) -> Option<Self::Item> {
185        loop {
186            let pop_epoch = self.pop_epoch.load(Ordering::Acquire);
187            let push_epoch = self.push_epoch.load(Ordering::Acquire);
188
189            if pop_epoch != push_epoch {
190                // drain old buffer
191
192                if !self.register_reader(pop_epoch) {
193                    continue;
194                }
195
196                // it is safe to call get_queue on pop_epoch here, since no resize can happen while we have not updated pop_epoch and reads on this epoch are happening
197                let item = self.get_queue(pop_epoch).pop();
198
199                self.deregister_reader(pop_epoch);
200
201                if item.is_some() {
202                    return item;
203                }
204
205                if self.active_pushes[pop_epoch % 2].load(Ordering::Acquire) == 0 {
206                    if !self.register_reader(pop_epoch) {
207                        continue;
208                    }
209
210                    let final_item = self.get_queue(pop_epoch).pop();
211
212                    self.deregister_reader(pop_epoch);
213
214                    if final_item.is_some() {
215                        return final_item;
216                    }
217
218                    _ = self.pop_epoch.compare_exchange_weak(
219                        pop_epoch,
220                        pop_epoch + 1,
221                        Ordering::AcqRel,
222                        Ordering::Relaxed,
223                    );
224                }
225
226                continue;
227            }
228
229            if !self.register_reader(push_epoch) {
230                continue;
231            }
232
233            let item = self.get_queue(push_epoch).pop();
234
235            self.deregister_reader(push_epoch);
236
237            return item;
238        }
239    }
240
241    fn capacity(&self) -> usize {
242        // the capacity of the currently active queue, i.e. the number of elements that can be pushed directly after resize
243        loop {
244            let push_epoch = self.push_epoch.load(Ordering::Acquire);
245            if !self.register_reader(push_epoch) {
246                continue;
247            }
248            let cap = self.get_queue(push_epoch).capacity();
249            self.deregister_reader(push_epoch);
250            return cap;
251        }
252    }
253
254    fn len(&self) -> usize {
255        // the total elements in the queue. Note that len can be > capacity.
256        loop {
257            let push_epoch = self.push_epoch.load(Ordering::Acquire);
258            if !self.register_reader(push_epoch) {
259                continue;
260            }
261
262            let pop_epoch = self.pop_epoch.load(Ordering::Acquire);
263            let pop_len = if pop_epoch != push_epoch {
264                if !self.register_reader(pop_epoch) {
265                    self.deregister_reader(push_epoch);
266                    continue;
267                }
268
269                let pop_len = self.get_queue(pop_epoch).len();
270                self.deregister_reader(pop_epoch);
271                pop_len
272            } else {
273                0
274            };
275
276            let len = self.get_queue(push_epoch).len() + pop_len;
277            self.deregister_reader(push_epoch);
278            return len;
279        }
280    }
281
282    fn is_empty(&self) -> bool {
283        // the queue is empty if pop() returns None
284        loop {
285            let push_epoch = self.push_epoch.load(Ordering::Acquire);
286            if !self.register_reader(push_epoch) {
287                continue;
288            }
289
290            let pop_epoch = self.pop_epoch.load(Ordering::Acquire);
291            let pop_is_empty = if pop_epoch != push_epoch {
292                if !self.register_reader(pop_epoch) {
293                    self.deregister_reader(push_epoch);
294                    continue;
295                }
296
297                let pop_is_empty = self.get_queue(pop_epoch).is_empty();
298                self.deregister_reader(pop_epoch);
299                pop_is_empty
300            } else {
301                true
302            };
303
304            let is_empty = self.get_queue(push_epoch).is_empty() && pop_is_empty;
305            self.deregister_reader(push_epoch);
306            return is_empty;
307        }
308    }
309
310    fn is_full(&self) -> bool {
311        // the queue is full if push() fails
312        loop {
313            let push_epoch = self.push_epoch.load(Ordering::Acquire);
314            if !self.register_reader(push_epoch) {
315                continue;
316            }
317            let is_full = self.get_queue(push_epoch).is_full();
318            self.deregister_reader(push_epoch);
319
320            return is_full;
321        }
322    }
323}
324
325impl<T, Q, S> NewSized for GrowableQueueCore<T, Q, S>
326where
327    Q: NewSized,
328{
329    fn with_size(size: usize) -> GrowableQueueCore<T, Q, S> {
330        GrowableQueueCore::with_slot(size)
331    }
332}
333
334impl<S> NewSized for QueueCore<BoxedBuffer<S>>
335where
336    S: Default,
337{
338    fn with_size(size: usize) -> Self {
339        Self::new_in(BoxedBuffer::new(size))
340    }
341}
342
343/// A lock-free, non-blocking queue, that may dynamically resize its capacity.
344pub struct DynamicQueue<T, S = Auto>
345where
346    S: SlotType<T>,
347    T: AsPackedValue,
348{
349    inner: GrowableQueueCore<T, QueueCore<BoxedBuffer<S::Slot>>, S>,
350}
351
352impl<T> DynamicQueue<T, Auto>
353where
354    T: AsPackedValue,
355{
356    /// Constructs a new `DynamicQueue` with capacity `size` and slot type `Auto`.
357    /// `T` must fit into the chosen slot type
358    pub fn new(size: usize) -> Self {
359        Self::with_slot::<Auto>(size)
360    }
361
362    /// Constructs a new `DynamicQueue` with capacity `size` and slot type `S`.
363    /// `T` must fit into the slot type `S`
364    pub fn with_slot<S>(size: usize) -> DynamicQueue<T, S>
365    where
366        S: SlotType<T>,
367    {
368        DynamicQueue {
369            inner: GrowableQueueCore::with_slot::<S>(size),
370        }
371    }
372}
373
374impl<T, S> MPMCQueue for DynamicQueue<T, S>
375where
376    T: AsPackedValue,
377    S: SlotType<T>,
378{
379    type Item = T;
380
381    fn push(&self, item: Self::Item) -> Result<(), Self::Item> {
382        self.inner.push(item)
383    }
384
385    fn pop(&self) -> Option<Self::Item> {
386        self.inner.pop()
387    }
388
389    fn len(&self) -> usize {
390        self.inner.len()
391    }
392
393    fn capacity(&self) -> usize {
394        self.inner.capacity()
395    }
396
397    fn is_empty(&self) -> bool {
398        self.inner.is_empty()
399    }
400
401    fn is_full(&self) -> bool {
402        self.inner.is_full()
403    }
404}
405
406impl<T, S> Resize for DynamicQueue<T, S>
407where
408    T: AsPackedValue,
409    S: SlotType<T>,
410{
411    fn resize(&self, size: usize) -> bool {
412        self.inner.resize(size)
413    }
414}