embedded_async_helpers/
ssq.rs

1//! A single slot queue.
2//!
3//! Especially useful for sharing wakers between async HAL drivers and their futures.
4
5use atomic_polyfill::{AtomicBool, Ordering};
6use core::{cell::UnsafeCell, mem::MaybeUninit, ptr, task::Waker};
7
8/// Short-hand for a single waker queue.
9pub type WakerQueue = SingleSlotQueue<Waker>;
10
11/// Short-hand for a single waker queue's consumer.
12pub type WakerConsumer = Consumer<'static, Waker>;
13
14/// Short-hand for a single waker queue's producer.
15pub type WakerProducer = Consumer<'static, Waker>;
16
17/// Single slot queue.
18pub struct SingleSlotQueue<T> {
19    full: AtomicBool,
20    val: UnsafeCell<MaybeUninit<T>>,
21}
22
23impl<T> SingleSlotQueue<T> {
24    /// Create a new SSQ.
25    pub const fn new() -> Self {
26        SingleSlotQueue {
27            full: AtomicBool::new(false),
28            val: UnsafeCell::new(MaybeUninit::uninit()),
29        }
30    }
31
32    /// Split the queue into producer and comsumer.
33    pub fn split<'a>(&'a mut self) -> (Consumer<'a, T>, Producer<'a, T>) {
34        (Consumer { ssq: self }, Producer { ssq: self })
35    }
36}
37
38impl<T> Drop for SingleSlotQueue<T> {
39    fn drop(&mut self) {
40        if self.full.load(Ordering::Relaxed) {
41            unsafe {
42                ptr::drop_in_place(self.val.get() as *mut T);
43            }
44        }
45    }
46}
47
48/// Read handle to a single slot queue.
49pub struct Consumer<'a, T> {
50    ssq: &'a SingleSlotQueue<T>,
51}
52
53impl<'a, T> Consumer<'a, T> {
54    /// Try reading a value from the queue.
55    #[inline]
56    pub fn dequeue(&mut self) -> Option<T> {
57        if self.ssq.full.load(Ordering::Acquire) {
58            let r = Some(unsafe { ptr::read(self.ssq.val.get().cast()) });
59            self.ssq.full.store(false, Ordering::Release);
60            r
61        } else {
62            None
63        }
64    }
65
66    /// Check if there is a value in the queue.
67    #[inline]
68    pub fn is_empty(&self) -> bool {
69        !self.ssq.full.load(Ordering::Relaxed)
70    }
71}
72
73/// Safety: We gurarantee the safety using an `AtomicBool` to gate the read of the `UnsafeCell`.
74unsafe impl<'a, T> Send for Consumer<'a, T> {}
75
76/// Write handle to a single slot queue.
77pub struct Producer<'a, T> {
78    ssq: &'a SingleSlotQueue<T>,
79}
80
81impl<'a, T> Producer<'a, T> {
82    /// Write a value into the queue. If there is a value already in the queue this will
83    /// return the value given to this method.
84    #[inline]
85    pub fn enqueue(&mut self, val: T) -> Option<T> {
86        if !self.ssq.full.load(Ordering::Acquire) {
87            unsafe { ptr::write(self.ssq.val.get().cast(), val) };
88            self.ssq.full.store(true, Ordering::Release);
89            None
90        } else {
91            Some(val)
92        }
93    }
94
95    /// Check if there is a value in the queue.
96    #[inline]
97    pub fn is_empty(&self) -> bool {
98        !self.ssq.full.load(Ordering::Relaxed)
99    }
100}
101
102/// Safety: We gurarantee the safety using an `AtomicBool` to gate the write of the
103/// `UnsafeCell`.
104unsafe impl<'a, T> Send for Producer<'a, T> {}