Skip to main content

bbqueue/
queue.rs

1use core::marker::PhantomData;
2
3use crate::{
4    prod_cons::{
5        framed::{FramedConsumer, FramedProducer},
6        stream::{StreamConsumer, StreamProducer},
7    },
8    traits::{
9        coordination::Coord,
10        notifier::Notifier,
11        storage::{ConstStorage, Storage},
12    },
13};
14
15#[cfg(feature = "alloc")]
16use crate::traits::bbqhdl::BbqHandle;
17
18/// A standard bbqueue
19pub struct BBQueue<S, C, N> {
20    pub(crate) sto: S,
21    pub(crate) cor: C,
22    pub(crate) not: N,
23}
24
25impl<S: Storage, C: Coord, N: Notifier> BBQueue<S, C, N> {
26    /// Create a new [`BBQueue`] with the given [`Storage`] impl
27    pub fn new_with_storage(sto: S) -> Self {
28        Self {
29            sto,
30            cor: C::INIT,
31            not: N::INIT,
32        }
33    }
34}
35
36/// A BBQueue wrapped in an Arc
37#[cfg(feature = "alloc")]
38pub struct ArcBBQueue<S, C, N>(pub(crate) alloc::sync::Arc<BBQueue<S, C, N>>);
39
40#[cfg(feature = "alloc")]
41impl<S: Storage, C: Coord, N: Notifier> ArcBBQueue<S, C, N> {
42    /// Create a new [`BBQueue`] with the given [`Storage`] impl
43    pub fn new_with_storage(sto: S) -> Self {
44        Self(alloc::sync::Arc::new(BBQueue::new_with_storage(sto)))
45    }
46}
47
48#[allow(clippy::new_without_default)]
49impl<S: ConstStorage, C: Coord, N: Notifier> BBQueue<S, C, N> {
50    /// Create a new `BBQueue` in a const context
51    pub const fn new() -> Self {
52        Self {
53            sto: S::INIT,
54            cor: C::INIT,
55            not: N::INIT,
56        }
57    }
58}
59
60impl<S: Storage, C: Coord, N: Notifier> BBQueue<S, C, N> {
61    /// Create a new [`FramedProducer`] for this [`BBQueue`]
62    ///
63    /// Although mixing stream and framed consumer/producers will not result in UB,
64    /// it will also not work correctly.
65    pub const fn framed_producer(&self) -> FramedProducer<&'_ Self> {
66        FramedProducer {
67            bbq: self,
68            pd: PhantomData,
69        }
70    }
71
72    /// Create a new [`FramedConsumer`] for this [`BBQueue`]
73    ///
74    /// Although mixing stream and framed consumer/producers will not result in UB,
75    /// it will also not work correctly.
76    pub const fn framed_consumer(&self) -> FramedConsumer<&'_ Self> {
77        FramedConsumer {
78            bbq: self,
79            pd: PhantomData,
80        }
81    }
82
83    /// Create a new [`StreamProducer`] for this [`BBQueue`]
84    ///
85    /// Although mixing stream and framed consumer/producers will not result in UB,
86    /// it will also not work correctly.
87    pub const fn stream_producer(&self) -> StreamProducer<&'_ Self> {
88        StreamProducer { bbq: self }
89    }
90
91    /// Create a new [`StreamConsumer`] for this [`BBQueue`]
92    ///
93    /// Although mixing stream and framed consumer/producers will not result in UB,
94    /// it will also not work correctly.
95    pub const fn stream_consumer(&self) -> StreamConsumer<&'_ Self> {
96        StreamConsumer { bbq: self }
97    }
98
99    /// Get the total capacity of the buffer, e.g. how much space is present in [`Storage`]
100    #[inline(always)]
101    pub fn capacity(&self) -> usize {
102        // SAFETY: capacity never changes, therefore reading the len is safe
103        unsafe {
104            self.sto.ptr_len().1
105        }
106    }
107
108    /// Get access to the internal storage implementation details
109    ///
110    /// NOTE: Although this method is safe, use of the `Storage` methods are not.
111    /// You should *never* attempt to access or modify the underlying data contained
112    /// in a storage implementation while the bbqueue is live. That will IMMEDIATELY
113    /// lead to undefined behavior.
114    ///
115    /// As far as I am aware, the only reasonable use for this is for cases where you
116    /// have a custom `Storage` implementation that has unique teardown/drop in place
117    /// requirements. Treat any uses of this function with *extreme* caution!
118    #[inline(always)]
119    pub fn storage(&self) -> &S {
120        &self.sto
121    }
122}
123
124#[cfg(feature = "alloc")]
125impl<S: Storage, C: Coord, N: Notifier> crate::queue::ArcBBQueue<S, C, N> {
126    /// Create a new [`FramedProducer`] for this [`BBQueue`]
127    ///
128    /// Although mixing stream and framed consumer/producers will not result in UB,
129    /// it will also not work correctly.
130    pub fn framed_producer(&self) -> FramedProducer<alloc::sync::Arc<BBQueue<S, C, N>>> {
131        FramedProducer {
132            bbq: self.0.bbq_ref(),
133            pd: PhantomData,
134        }
135    }
136
137    /// Create a new [`FramedConsumer`] for this [`BBQueue`]
138    ///
139    /// Although mixing stream and framed consumer/producers will not result in UB,
140    /// it will also not work correctly.
141    pub fn framed_consumer(&self) -> FramedConsumer<alloc::sync::Arc<BBQueue<S, C, N>>> {
142        FramedConsumer {
143            bbq: self.0.bbq_ref(),
144            pd: PhantomData,
145        }
146    }
147
148    /// Create a new [`StreamProducer`] for this [`BBQueue`]
149    ///
150    /// Although mixing stream and framed consumer/producers will not result in UB,
151    /// it will also not work correctly.
152    pub fn stream_producer(&self) -> StreamProducer<alloc::sync::Arc<BBQueue<S, C, N>>> {
153        StreamProducer {
154            bbq: self.0.bbq_ref(),
155        }
156    }
157
158    /// Create a new [`StreamConsumer`] for this [`BBQueue`]
159    ///
160    /// Although mixing stream and framed consumer/producers will not result in UB,
161    /// it will also not work correctly.
162    pub fn stream_consumer(&self) -> StreamConsumer<alloc::sync::Arc<BBQueue<S, C, N>>> {
163        StreamConsumer {
164            bbq: self.0.bbq_ref(),
165        }
166    }
167
168    /// Get the total capacity of the buffer, e.g. how much space is present in [`Storage`]
169    #[inline(always)]
170    pub fn capacity(&self) -> usize {
171        // SAFETY: capacity never changes, therefore reading the len is safe
172        unsafe {
173            self.0.sto.ptr_len().1
174        }
175    }
176
177    /// Get access to the internal storage implementation details
178    ///
179    /// NOTE: Although this method is safe, use of the `Storage` methods are not.
180    /// You should *never* attempt to access or modify the underlying data contained
181    /// in a storage implementation while the bbqueue is live. That will IMMEDIATELY
182    /// lead to undefined behavior.
183    ///
184    /// As far as I am aware, the only reasonable use for this is for cases where you
185    /// have a custom `Storage` implementation that has unique teardown/drop in place
186    /// requirements. Treat any uses of this function with *extreme* caution!
187    #[inline(always)]
188    pub fn storage(&self) -> &S {
189        &self.0.sto
190    }
191}
192
193#[cfg(test)]
194mod test {
195    use crate::traits::{
196        coordination::cas::AtomicCoord, notifier::polling::Polling, storage::Inline,
197    };
198
199    use super::*;
200
201    type Queue = BBQueue<Inline<4096>, AtomicCoord, Polling>;
202    static QUEUE: Queue = BBQueue::new();
203    static PRODUCER: FramedProducer<&'static Queue, u16> = QUEUE.framed_producer();
204    static CONSUMER: FramedConsumer<&'static Queue, u16> = QUEUE.framed_consumer();
205
206    #[test]
207    fn handles() {
208        let mut wgr = PRODUCER.grant(16).unwrap();
209        wgr.iter_mut().for_each(|w| *w = 123);
210        wgr.commit(16);
211
212        let rgr = CONSUMER.read().unwrap();
213        assert_eq!(rgr.len(), 16);
214        for b in rgr.iter() {
215            assert_eq!(*b, 123);
216        }
217        rgr.release();
218    }
219}