Skip to content

Commit 4d64701

Browse files
committed
Make Queue::split const.
1 parent 1a33fec commit 4d64701

File tree

3 files changed

+152
-10
lines changed

3 files changed

+152
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
5555
- Changed `stable_deref_trait` to a platform-dependent dependency.
5656
- Changed `SortedLinkedList::pop` return type from `Result<T, ()>` to `Option<T>` to match `std::vec::pop`.
5757
- `Vec::capacity` is no longer a `const` function.
58+
- Changed `Queue::split` to be `const`.
5859

5960
### Fixed
6061

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ defmt-03 = ["dep:defmt"]
3939
# Enable larger MPMC sizes.
4040
mpmc_large = []
4141

42+
# Enable nightly features.
4243
nightly = []
4344

4445
[dependencies]
@@ -53,6 +54,7 @@ defmt = { version = ">=0.2.0,<0.4", optional = true }
5354
stable_deref_trait = { version = "1", default-features = false }
5455

5556
[dev-dependencies]
57+
critical-section = { version = "1.1", features = ["std"] }
5658
ufmt = "0.2"
5759
static_assertions = "1.1.0"
5860

src/spsc.rs

Lines changed: 149 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@
5151
//! // scope.
5252
//! let queue: &'static mut Queue<Event, 4> = {
5353
//! static mut Q: Queue<Event, 4> = Queue::new();
54+
//! // SAFETY: `Q` is only accessible in this scope
55+
/// // and `main` is only called once.
5456
//! unsafe { &mut Q }
5557
//! };
5658
//!
@@ -128,16 +130,21 @@ pub struct QueueInner<T, S: Storage> {
128130
pub(crate) buffer: S::Buffer<UnsafeCell<MaybeUninit<T>>>,
129131
}
130132

131-
/// A statically allocated single producer single consumer queue with a capacity of `N - 1` elements
133+
/// A statically allocated single producer, single consumer queue with a capacity of `N - 1` elements.
132134
///
133-
/// *IMPORTANT*: To get better performance use a value for `N` that is a power of 2 (e.g. `16`, `32`,
134-
/// etc.).
135+
/// >
136+
/// <div class="warning">
137+
///
138+
/// To get better performance use a value for `N` that is a power of 2, e.g. 16, 32, etc.
139+
///
140+
/// </div>
141+
///
142+
/// You will likely want to use [`split`](QueueInner::split) to create a producer and consumer handle.
135143
pub type Queue<T, const N: usize> = QueueInner<T, OwnedStorage<N>>;
136144

137-
/// Asingle producer single consumer queue
145+
/// A [`Queue`] with dynamic capacity.
138146
///
139-
/// *IMPORTANT*: To get better performance use a value for `N` that is a power of 2 (e.g. `16`, `32`,
140-
/// etc.).
147+
/// [`Queue`] coerces to `QueueView`. `QueueView` is `!Sized`, meaning it can only ever be used by reference.
141148
pub type QueueView<T> = QueueInner<T, ViewStorage>;
142149

143150
impl<T, const N: usize> Queue<T, N> {
@@ -352,8 +359,108 @@ impl<T, S: Storage> QueueInner<T, S> {
352359
self.inner_dequeue_unchecked()
353360
}
354361

355-
/// Splits a queue into producer and consumer endpoints
356-
pub fn split(&mut self) -> (ProducerInner<'_, T, S>, ConsumerInner<'_, T, S>) {
362+
/// Splits a queue into producer and consumer endpoints.
363+
///
364+
/// # Examples
365+
///
366+
/// Create a queue at compile time, split it at runtime,
367+
/// and pass it to an interrupt handler via a mutex.
368+
///
369+
/// ```
370+
/// use core::cell::RefCell;
371+
/// use critical_section::Mutex;
372+
/// use heapless::spsc::{Producer, Queue};
373+
///
374+
/// static PRODUCER: Mutex<RefCell<Option<Producer<'static, (), 4>>>> =
375+
/// Mutex::new(RefCell::new(None));
376+
///
377+
/// fn interrupt() {
378+
/// let mut producer = {
379+
/// static mut P: Option<Producer<'static, (), 4>> = None;
380+
/// // SAFETY: Mutable access to `P` is allowed exclusively in this scope
381+
/// // and `interrupt` cannot be called directly or preempt itself.
382+
/// unsafe { &mut P }
383+
/// }
384+
/// .get_or_insert_with(|| {
385+
/// critical_section::with(|cs| PRODUCER.borrow_ref_mut(cs).take().unwrap())
386+
/// });
387+
///
388+
/// producer.enqueue(()).unwrap();
389+
/// }
390+
///
391+
/// fn main() {
392+
/// let mut consumer = {
393+
/// let (p, c) = {
394+
/// static mut Q: Queue<(), 4> = Queue::new();
395+
/// // SAFETY: `Q` is only accessible in this scope
396+
/// // and `main` is only called once.
397+
/// #[allow(static_mut_refs)]
398+
/// unsafe { Q.split() }
399+
/// };
400+
///
401+
/// critical_section::with(move |cs| {
402+
/// let mut producer = PRODUCER.borrow_ref_mut(cs);
403+
/// *producer = Some(p);
404+
/// });
405+
///
406+
/// c
407+
/// };
408+
///
409+
/// // Interrupt occurs.
410+
/// # interrupt();
411+
///
412+
/// consumer.dequeue().unwrap();
413+
/// }
414+
/// ```
415+
///
416+
/// Create and split a queue at compile time, and pass it to the main
417+
/// function and an interrupt handler via a mutex at runtime.
418+
///
419+
/// ```
420+
/// use core::cell::RefCell;
421+
///
422+
/// use critical_section::Mutex;
423+
/// use heapless::spsc::{Consumer, Producer, Queue};
424+
///
425+
/// static PC: (
426+
/// Mutex<RefCell<Option<Producer<'_, (), 4>>>>,
427+
/// Mutex<RefCell<Option<Consumer<'_, (), 4>>>>,
428+
/// ) = {
429+
/// static mut Q: Queue<(), 4> = Queue::new();
430+
/// // SAFETY: `Q` is only accessible in this scope.
431+
/// #[allow(static_mut_refs)]
432+
/// let (p, c) = unsafe { Q.split() };
433+
///
434+
/// (
435+
/// Mutex::new(RefCell::new(Some(p))),
436+
/// Mutex::new(RefCell::new(Some(c))),
437+
/// )
438+
/// };
439+
///
440+
/// fn interrupt() {
441+
/// let mut producer = {
442+
/// static mut P: Option<Producer<'_, (), 4>> = None;
443+
/// // SAFETY: Mutable access to `P` is allowed exclusively in this scope
444+
/// // and `interrupt` cannot be called directly or preempt itself.
445+
/// unsafe { &mut P }
446+
/// }
447+
/// .get_or_insert_with(|| {
448+
/// critical_section::with(|cs| PC.0.borrow_ref_mut(cs).take().unwrap())
449+
/// });
450+
///
451+
/// producer.enqueue(()).unwrap();
452+
/// }
453+
///
454+
/// fn main() {
455+
/// let mut consumer = critical_section::with(|cs| PC.1.borrow_ref_mut(cs).take().unwrap());
456+
///
457+
/// // Interrupt occurs.
458+
/// # interrupt();
459+
///
460+
/// consumer.dequeue().unwrap();
461+
/// }
462+
/// ```
463+
pub const fn split(&mut self) -> (ProducerInner<'_, T, S>, ConsumerInner<'_, T, S>) {
357464
(ProducerInner { rb: self }, ConsumerInner { rb: self })
358465
}
359466
}
@@ -372,9 +479,9 @@ where
372479
let mut new: Self = Self::new();
373480

374481
for s in self.iter() {
482+
// SAFETY: `new.capacity() == self.capacity() >= self.len()`,
483+
// so no overflow is possible.
375484
unsafe {
376-
// NOTE(unsafe) new.capacity() == self.capacity() >= self.len()
377-
// no overflow possible
378485
new.enqueue_unchecked(s.clone());
379486
}
380487
}
@@ -734,6 +841,38 @@ mod tests {
734841
// Ensure a `Consumer` containing `!Send` values stays `!Send` itself.
735842
assert_not_impl_any!(Consumer<*const (), 4>: Send);
736843

844+
#[test]
845+
fn const_split() {
846+
use critical_section::Mutex;
847+
use std::cell::RefCell;
848+
849+
use super::{Consumer, Producer};
850+
851+
#[allow(clippy::type_complexity)]
852+
static PC: (
853+
Mutex<RefCell<Option<Producer<'_, (), 4>>>>,
854+
Mutex<RefCell<Option<Consumer<'_, (), 4>>>>,
855+
) = {
856+
static mut Q: Queue<(), 4> = Queue::new();
857+
// SAFETY: `Q` is only accessible in this scope.
858+
#[allow(static_mut_refs)]
859+
let (p, c) = unsafe { Q.split() };
860+
861+
(
862+
Mutex::new(RefCell::new(Some(p))),
863+
Mutex::new(RefCell::new(Some(c))),
864+
)
865+
};
866+
let producer = critical_section::with(|cs| PC.0.borrow_ref_mut(cs).take().unwrap());
867+
let consumer = critical_section::with(|cs| PC.1.borrow_ref_mut(cs).take().unwrap());
868+
869+
let mut producer: Producer<'static, (), 4> = producer;
870+
let mut consumer: Consumer<'static, (), 4> = consumer;
871+
872+
assert_eq!(producer.enqueue(()), Ok(()));
873+
assert_eq!(consumer.dequeue(), Some(()));
874+
}
875+
737876
#[test]
738877
fn full() {
739878
let mut rb: Queue<i32, 3> = Queue::new();

0 commit comments

Comments
 (0)