Skip to main content

async_priority_lock/queue/
boxed.rs

1//! Provides [BoxQueue] - where entries are each allocated via [Box].
2//!
3//! This is usually slower than [ArenaQueue], however it is usually a bit more memory efficient, as
4//! nodes are deallocated when they are removed.
5use core::{
6    cell::UnsafeCell,
7    marker::{PhantomData, PhantomPinned},
8    mem::replace,
9    pin::Pin,
10    ptr::NonNull,
11};
12
13#[cfg(not(feature = "std"))]
14extern crate alloc;
15use super::*;
16
17#[cfg(not(feature = "std"))]
18use alloc::boxed::Box;
19#[cfg(feature = "std")]
20use std::boxed::Box;
21
22#[cfg(feature = "const-default")]
23impl<N: BoxQueueNode> const_default::ConstDefault for BoxQueue<N> {
24    const DEFAULT: Self = Self { head: None };
25}
26
27/// A queue where nodes are independently allocated via Box.
28///
29/// This is usually much slower than arena queue, but is more memory efficient as the
30/// memory used for each node is deallocated once the node is removed.
31///
32/// Usage is only recommended when memory efficiency is needed.  If possible, [DualLinkBoxQueue]
33/// should be preferred over [SingleLinkBoxQueue], as it almost always faster.
34///
35/// (difference being that [DualLinkBoxQueueNode] has an extra [usize] worth of bytes per node)
36pub struct BoxQueue<N: BoxQueueNode> {
37    head: Option<Pin<Box<N>>>,
38}
39
40/// A single linked (next only) [BoxQueue].
41///
42/// Short for [`BoxQueue`]`<`[`SingleLinkBoxQueueNode<P>`]`>`
43pub type SingleLinkBoxQueue<P> = BoxQueue<SingleLinkBoxQueueNode<P>>;
44/// A dual linked [BoxQueue].
45///
46/// Short for [`ArenaQueue`]`<`[`DualLinkBoxQueueNode<P>`]`>`
47pub type DualLinkBoxQueue<P> = BoxQueue<DualLinkBoxQueueNode<P>>;
48
49impl<N: BoxQueueNode> BoxQueue<N> {
50    #[inline]
51    fn insert(&mut self, new_node: Pin<Box<N>>) -> <Self as PriorityQueue<N::Data>>::Handle {
52        let ptr = BoxQueueHandle(NonNull::from_ref(&*new_node));
53        if self.head.is_none() {
54            self.head = Some(new_node);
55            return ptr;
56        }
57
58        if new_node
59            .data()
60            // again, we run into a bug with the rust borrow checker where it fails to realize that
61            // we don't hold a reference to curr in this scope after we return, so we can't use the
62            // same reference we get from as_ref() -> unwrap as we do below...
63            .compare_new(&self.head.as_ref().unwrap().data())
64            .is_ge()
65        {
66            self.head.as_ref().unwrap().set_prev(Some(&*new_node));
67            *new_node.next() = self.head.take();
68            self.head = Some(new_node);
69
70            return ptr;
71        }
72
73        let mut curr = self.head.as_ref().unwrap();
74        while let Some(next) = curr.next() {
75            if new_node.data().compare_new(&next.data()).is_ge() {
76                break;
77            }
78
79            curr = next;
80        }
81
82        let next = curr.next().take();
83        if let Some(nxt) = &next {
84            nxt.set_prev(Some(&*new_node));
85        }
86        *new_node.next() = next;
87        new_node.set_prev(Some(&*curr));
88        *curr.next() = Some(new_node);
89
90        ptr
91    }
92}
93
94impl<N: BoxQueueNode> Default for BoxQueue<N> {
95    #[inline]
96    fn default() -> Self {
97        Self {
98            head: Default::default(),
99        }
100    }
101}
102
103impl<N: BoxQueueNode> BoxQueueHandle<N> {
104    #[inline(always)]
105    fn load(&self) -> &N::Data {
106        unsafe { self.0.as_ref() }.data()
107    }
108}
109
110impl<N: BoxQueueNode> From<&Pin<Box<N>>> for SharedBoxQueueHandle<N> {
111    fn from(value: &Pin<Box<N>>) -> Self {
112        Self(BoxQueueHandle(NonNull::from_ref(&value)))
113    }
114}
115
116impl<N: BoxQueueNode> PriorityQueueHandle<N::Data> for BoxQueueHandle<N> {
117    const LOAD_PURE: Option<unsafe fn(&Self) -> &N::Data> = Some(Self::load);
118}
119
120unsafe impl<N: BoxQueueNode> PriorityQueue<N::Data> for BoxQueue<N> {
121    type Handle = BoxQueueHandle<N>;
122    type SharedHandle = SharedBoxQueueHandle<N>;
123
124    fn enqueue(&mut self, data: N::Data) -> Self::Handle {
125        let new_node = Box::pin(N::new(data));
126
127        self.insert(new_node)
128    }
129
130    #[inline(always)]
131    fn get_by_handle(&self, handle: &Self::Handle) -> &N::Data {
132        unsafe { handle.0.as_ref() }.data()
133    }
134
135    #[inline(always)]
136    fn iter_at<'a>(&'a self, after: Option<&Self::Handle>) -> impl Iterator<Item = &'a N::Data>
137    where
138        N::Data: 'a,
139    {
140        if let Some(after) = after {
141            return BoxQueueIterator(after.0.as_ptr().cast_const(), PhantomData);
142        }
143
144        BoxQueueIterator(
145            self.head
146                .as_ref()
147                .map(|x| &raw const **x)
148                .unwrap_or_default(),
149            PhantomData,
150        )
151    }
152
153    #[inline(always)]
154    fn dequeue(
155        &mut self,
156        BoxQueueHandle(ptr): Self::Handle,
157    ) -> (Option<&N::Data>, Option<Self::SharedHandle>) {
158        let ptr = ptr.as_ptr().cast_const();
159        if N::HAS_PREV {
160            let node = unsafe { &*ptr };
161
162            let next = node.next().take();
163            if let Some(nxt) = &next {
164                nxt.set_prev(node.prev());
165            }
166
167            let handle = next.as_ref().map(|x| x.into());
168            if let Some(prev) = node.prev() {
169                *prev.next() = next;
170                return (Some(prev.data()), handle);
171            }
172
173            // if we didn't have a prev, we must have the head node
174            self.head = next;
175            return (None, handle);
176        }
177
178        if let Some(head) = self.head.as_ref() {
179            if ptr == &**head {
180                self.head = head.next().take();
181                return (None, self.head.as_ref().map(|x| x.into()));
182            }
183        }
184
185        if let Some(mut node) = self.head.as_ref() {
186            while let Some(next) = node.next() {
187                if ptr == &**next {
188                    *node.next() = next.next().take();
189
190                    return (Some(node.data()), node.next().as_ref().map(|x| x.into()));
191                }
192
193                node = &*next;
194            }
195        }
196
197        (None, None)
198    }
199
200    #[inline]
201    fn is_empty(&self) -> bool {
202        self.head.is_none()
203    }
204
205    fn update_node(&mut self, handle: &Self::Handle, update: impl FnOnce(&mut N::Data) -> bool) {
206        let ptr = handle.0.as_ptr();
207
208        if !update(unsafe { &mut *ptr }.data_mut()) {
209            return;
210        }
211
212        if N::HAS_PREV {
213            let rf = unsafe { &*ptr };
214            // is the current location good? (priority: prev >= curr >= next)
215            if rf
216                .prev()
217                .is_none_or(|x| rf.data().compare(x.data()).is_le())
218                && rf
219                    .next()
220                    .as_ref()
221                    .is_none_or(|nxt| rf.data().compare(nxt.data()).is_ge())
222            {
223                return;
224            }
225
226            let next = rf.next().take();
227
228            if let Some(nxt) = &next {
229                nxt.set_prev(rf.prev());
230            }
231
232            let old_loc = rf.prev().map(|x| x.next()).unwrap_or(&mut self.head);
233
234            rf.set_prev(None);
235            let node = replace(old_loc, next).unwrap();
236
237            self.insert(node);
238            return;
239        }
240
241        if let Some(head) = self.head.as_mut() {
242            if &raw const **head == ptr {
243                if head
244                    .next()
245                    .as_ref()
246                    .is_some_and(|next| head.data().compare(&next.data()).is_le())
247                {
248                    let next = head.next().take();
249                    let node = replace(&mut self.head, next).unwrap();
250                    self.insert(node);
251                }
252
253                return;
254            }
255        }
256
257        if let Some(mut node) = self.head.as_mut() {
258            while let Some(next) = node.next() {
259                if &raw const **next == ptr {
260                    // Check that the updated node is still <= the previous one and >= the next one
261                    if !(next.data().compare(&node.data()).is_le()
262                        && next
263                            .next()
264                            .as_ref()
265                            .is_none_or(|next_next| next.data().compare(&next_next.data()).is_ge()))
266                    {
267                        let to_insert = replace(node.next(), next.next().take()).unwrap();
268
269                        self.insert(to_insert);
270                    }
271
272                    return;
273                }
274
275                node = &mut *next;
276            }
277        }
278
279        // this check isn't needed for compiling but:
280        // 1. we should probably panic if we can't find the node
281        // 2. this may allow the compiler to optimize a bit better
282        unreachable!("handle must be valid")
283    }
284
285    #[inline(always)]
286    fn head_handle(&self) -> Option<Self::SharedHandle> {
287        self.head.as_ref().map(|x| x.into())
288    }
289
290    #[inline]
291    fn get_next_handle(&self, handle: &Self::Handle) -> Option<Self::SharedHandle> {
292        unsafe { handle.0.as_ref() }
293            .next()
294            .as_ref()
295            .map(|x| x.into())
296    }
297}
298
299/// An opaque handle to a [BoxQueue] node.
300///
301/// See [PriorityQueue::Handle].
302pub struct BoxQueueHandle<N: BoxQueueNode>(NonNull<N>);
303
304unsafe impl<N: BoxQueueNode> Send for BoxQueueHandle<N> {}
305unsafe impl<N: BoxQueueNode> Sync for BoxQueueHandle<N> {}
306
307struct BoxQueueIterator<'a, N: BoxQueueNode>(*const N, PhantomData<&'a ()>);
308
309/// An opaque "shared" handle to a [BoxQueue] node.
310///
311/// See [PriorityQueue::SharedHandle].
312pub struct SharedBoxQueueHandle<N: BoxQueueNode>(BoxQueueHandle<N>);
313
314impl<N: BoxQueueNode> AsRef<BoxQueueHandle<N>> for SharedBoxQueueHandle<N> {
315    #[inline(always)]
316    fn as_ref(&self) -> &BoxQueueHandle<N> {
317        &self.0
318    }
319}
320
321impl<'a, N: 'a + BoxQueueNode> Iterator for BoxQueueIterator<'a, N> {
322    type Item = &'a N::Data;
323
324    #[inline(always)]
325    fn next(&mut self) -> Option<Self::Item> {
326        let curr = unsafe { self.0.as_ref()? };
327        let rf = &*curr;
328
329        self.0 = curr
330            .next()
331            .as_ref()
332            .map(|x| &raw const **x)
333            .unwrap_or_default();
334        Some(rf.data())
335    }
336}
337
338/// A helper trait for [BoxQueue] to allow for both single and dual-link queues.
339///
340/// This is not intended to be used publicly. Use [DualLinkBoxQueueNode] and
341/// [SingleLinkBoxQueueNode].
342#[doc(hidden)]
343pub trait BoxQueueNode {
344    type Data: Priority;
345    const HAS_PREV: bool;
346
347    fn new(data: Self::Data) -> Self;
348    fn data(&self) -> &Self::Data;
349    fn data_mut(self: &mut Self) -> &mut Self::Data;
350
351    fn next<'a>(&self) -> &'a mut Option<Pin<Box<Self>>>;
352
353    fn prev<'a>(&self) -> Option<&'a Self>;
354    fn set_prev(&self, prev: Option<&Self>);
355}
356
357/// A node for [BoxQueue] which only links to next.
358///
359/// Direct usuage is likely to be a bit verbose; usage of the [SingleLinkBoxQueue] is recommended.
360pub struct SingleLinkBoxQueueNode<P: Priority> {
361    data: P,
362    next: UnsafeCell<Option<Pin<Box<Self>>>>,
363    _must_pin: core::marker::PhantomPinned,
364}
365
366unsafe impl<P: Priority + Sync> Sync for SingleLinkBoxQueueNode<P> {}
367
368impl<P: Priority> BoxQueueNode for SingleLinkBoxQueueNode<P> {
369    type Data = P;
370    const HAS_PREV: bool = false;
371
372    #[inline(always)]
373    fn new(data: Self::Data) -> Self {
374        Self {
375            data,
376            next: UnsafeCell::new(None),
377            _must_pin: PhantomPinned,
378        }
379    }
380
381    #[inline(always)]
382    fn next<'a>(&self) -> &'a mut Option<Pin<Box<Self>>> {
383        unsafe { &mut *self.next.get() }
384    }
385
386    #[inline(always)]
387    fn data_mut(&mut self) -> &mut P {
388        &mut self.data
389    }
390
391    #[inline(always)]
392    fn data(&self) -> &Self::Data {
393        &self.data
394    }
395
396    #[inline(always)]
397    fn prev<'a>(&self) -> Option<&'a Self> {
398        unreachable!("single linked box queues do not have prev")
399    }
400
401    #[inline(always)]
402    fn set_prev<'a>(&self, _: Option<&Self>) {}
403}
404
405/// A node for [BoxQueue] which links to both the previous and next nodes.
406///
407/// Direct usuage is likely to be a bit verbose; usage of the [DualLinkBoxQueue] is recommended.
408pub struct DualLinkBoxQueueNode<P: Priority> {
409    data: P,
410    next: UnsafeCell<Option<Pin<Box<Self>>>>,
411    prev: UnsafeCell<Option<NonNull<Self>>>,
412    _must_pin: core::marker::PhantomPinned,
413}
414
415unsafe impl<P: Priority + Send> Send for DualLinkBoxQueueNode<P> {}
416unsafe impl<P: Priority + Sync> Sync for DualLinkBoxQueueNode<P> {}
417
418impl<P: Priority> BoxQueueNode for DualLinkBoxQueueNode<P> {
419    type Data = P;
420    const HAS_PREV: bool = true;
421
422    #[inline(always)]
423    fn new(data: Self::Data) -> Self {
424        Self {
425            data,
426            next: UnsafeCell::new(None),
427            prev: UnsafeCell::new(None),
428            _must_pin: PhantomPinned,
429        }
430    }
431
432    #[inline(always)]
433    fn next<'a>(&self) -> &'a mut Option<Pin<Box<Self>>> {
434        unsafe { &mut *self.next.get() }
435    }
436
437    #[inline(always)]
438    fn data_mut(&mut self) -> &mut Self::Data {
439        &mut self.data
440    }
441
442    #[inline(always)]
443    fn data(&self) -> &Self::Data {
444        &self.data
445    }
446
447    #[inline(always)]
448    fn prev<'a>(&self) -> Option<&'a Self> {
449        let rf = unsafe { &*self.prev.get() };
450
451        rf.map(|x| unsafe { x.as_ref() })
452    }
453
454    fn set_prev(&self, prev: Option<&Self>) {
455        let rf = unsafe { &mut *self.prev.get() };
456
457        *rf = prev.map(|x| NonNull::from_ref(x))
458    }
459}