Skip to content

Commit dfe4629

Browse files
committed
Make Queue::split const.
# Conflicts: # Cargo.toml
1 parent 6ae82d5 commit dfe4629

File tree

3 files changed

+153
-10
lines changed

3 files changed

+153
-10
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
6666
- `Vec::capacity` is no longer a `const` function.
6767
- Relaxed bounds on `PartialEq` for `IndexMap` from `V: Eq` to `V1: PartialEq<V2>`.
6868
- Relaxed bounds on `PartialEq` for `LinearMap` from `V: PartialEq` to `V1: PartialEq<V2>`.
69+
- Changed `Queue::split` to be `const`.
6970

7071
### Fixed
7172

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ defmt = { version = "1.0.1", optional = true }
6060
stable_deref_trait = { version = "1", default-features = false }
6161

6262
[dev-dependencies]
63+
critical-section = { version = "1.1", features = ["std"] }
6364
static_assertions = "1.1.0"
6465

6566
[package.metadata.docs.rs]

src/spsc.rs

+151-10
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> {
@@ -362,8 +369,110 @@ impl<T, S: Storage> QueueInner<T, S> {
362369
self.inner_dequeue_unchecked()
363370
}
364371

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

384493
for s in self.iter() {
494+
// SAFETY: `new.capacity() == self.capacity() >= self.len()`,
495+
// so no overflow is possible.
385496
unsafe {
386-
// NOTE(unsafe) new.capacity() == self.capacity() >= self.len()
387-
// no overflow possible
388497
new.enqueue_unchecked(s.clone());
389498
}
390499
}
@@ -744,6 +853,38 @@ mod tests {
744853
// Ensure a `Consumer` containing `!Send` values stays `!Send` itself.
745854
assert_not_impl_any!(Consumer<*const (), 4>: Send);
746855

856+
#[test]
857+
fn const_split() {
858+
use critical_section::Mutex;
859+
use std::cell::RefCell;
860+
861+
use super::{Consumer, Producer};
862+
863+
#[allow(clippy::type_complexity)]
864+
static PC: (
865+
Mutex<RefCell<Option<Producer<'_, (), 4>>>>,
866+
Mutex<RefCell<Option<Consumer<'_, (), 4>>>>,
867+
) = {
868+
static mut Q: Queue<(), 4> = Queue::new();
869+
// SAFETY: `Q` is only accessible in this scope.
870+
#[allow(static_mut_refs)]
871+
let (p, c) = unsafe { Q.split() };
872+
873+
(
874+
Mutex::new(RefCell::new(Some(p))),
875+
Mutex::new(RefCell::new(Some(c))),
876+
)
877+
};
878+
let producer = critical_section::with(|cs| PC.0.borrow_ref_mut(cs).take().unwrap());
879+
let consumer = critical_section::with(|cs| PC.1.borrow_ref_mut(cs).take().unwrap());
880+
881+
let mut producer: Producer<'static, (), 4> = producer;
882+
let mut consumer: Consumer<'static, (), 4> = consumer;
883+
884+
assert_eq!(producer.enqueue(()), Ok(()));
885+
assert_eq!(consumer.dequeue(), Some(()));
886+
}
887+
747888
#[test]
748889
fn full() {
749890
let mut rb: Queue<i32, 3> = Queue::new();

0 commit comments

Comments
 (0)