From af09a22dcd25b70cf7224474b777ab665c07986d Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Thu, 6 Oct 2022 04:25:50 +0200 Subject: [PATCH] Make `Queue::split` `const`. --- CHANGELOG.md | 1 + Cargo.toml | 4 ++ src/lib.rs | 1 + src/spsc.rs | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 144 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff4ee28f26..f0fe3d16ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed - Changed `stable_deref_trait` to a platform-dependent dependency. +- Changed `Queue::split` to be `const` when `"nightly"` feature is enabled. ### Fixed diff --git a/Cargo.toml b/Cargo.toml index 0c45a6d0c3..32da25828d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,9 @@ defmt-03 = ["dep:defmt"] # Enable larger MPMC sizes. mpmc_large = [] +# Enable nightly features. +nightly = [] + [dependencies] portable-atomic = { version = "1.0", optional = true } hash32 = "0.3.0" @@ -51,6 +54,7 @@ defmt = { version = ">=0.2.0,<0.4", optional = true } stable_deref_trait = { version = "1", default-features = false } [dev-dependencies] +critical-section = { version = "1.1", features = ["std"] } ufmt = "0.2" [package.metadata.docs.rs] diff --git a/src/lib.rs b/src/lib.rs index baaef35d96..0724875d2d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,6 +76,7 @@ #![cfg_attr(docsrs, feature(doc_cfg), feature(doc_auto_cfg))] #![cfg_attr(not(test), no_std)] #![deny(missing_docs)] +#![cfg_attr(feature = "nightly", feature(const_mut_refs))] pub use binary_heap::BinaryHeap; pub use deque::Deque; diff --git a/src/spsc.rs b/src/spsc.rs index 3b9db75f6b..634688d0a7 100644 --- a/src/spsc.rs +++ b/src/spsc.rs @@ -305,10 +305,113 @@ impl Queue { self.inner_dequeue_unchecked() } - /// Splits a queue into producer and consumer endpoints + /// Splits a queue into producer and consumer endpoints. + /// + /// # Examples + /// + /// ``` + /// use core::cell::RefCell; + /// use critical_section::Mutex; + /// use heapless::spsc::{Producer, Queue}; + /// + /// static PRODUCER: Mutex>>> = + /// Mutex::new(RefCell::new(None)); + /// + /// fn interrupt() { + /// let mut producer = { + /// static mut P: Option> = None; + /// // SAFETY: Mutable access to `P` is allowed exclusively in this scope + /// // and `interrupt` cannot be called directly or preempt itself. + /// unsafe { &mut P } + /// } + /// .get_or_insert_with(|| { + /// critical_section::with(|cs| PRODUCER.borrow_ref_mut(cs).take().unwrap()) + /// }); + /// + /// producer.enqueue(()).unwrap(); + /// } + /// + /// fn main() { + /// let mut consumer = { + /// let (p, c) = { + /// static mut Q: Queue<(), 4> = Queue::new(); + /// // SAFETY: Mutable access to `Q` is allowed exclusively in this scope + /// // and `main` is only called once. + /// unsafe { Q.split() } + /// }; + /// + /// critical_section::with(move |cs| { + /// let mut producer = PRODUCER.borrow_ref_mut(cs); + /// *producer = Some(p); + /// }); + /// + /// c + /// }; + /// + /// // Interrupt occurs. + /// # interrupt(); + /// + /// consumer.dequeue().unwrap(); + /// } + /// ``` + #[cfg(not(feature = "nightly"))] pub fn split(&mut self) -> (Producer<'_, T, N>, Consumer<'_, T, N>) { (Producer { rb: self }, Consumer { rb: self }) } + + /// Splits a queue into producer and consumer endpoints. + /// + /// # Examples + /// + /// ``` + /// #![feature(const_mut_refs)] + /// + /// use core::cell::RefCell; + /// + /// use critical_section::Mutex; + /// use heapless::spsc::{Consumer, Producer, Queue}; + /// + /// static PC: ( + /// Mutex>>>, + /// Mutex>>>, + /// ) = { + /// static mut Q: Queue<(), 4> = Queue::new(); + /// // SAFETY: Mutable access to `Q` is allowed exclusively in this scope. + /// let (p, c) = unsafe { Q.split() }; + /// + /// ( + /// Mutex::new(RefCell::new(Some(p))), + /// Mutex::new(RefCell::new(Some(c))), + /// ) + /// }; + /// + /// fn interrupt() { + /// let mut producer = { + /// static mut P: Option> = None; + /// // SAFETY: Mutable access to `P` is allowed exclusively in this scope + /// // and `interrupt` cannot be called directly or preempt itself. + /// unsafe { &mut P } + /// } + /// .get_or_insert_with(|| { + /// critical_section::with(|cs| PC.0.borrow_ref_mut(cs).take().unwrap()) + /// }); + /// + /// producer.enqueue(()).unwrap(); + /// } + /// + /// fn main() { + /// let mut consumer = critical_section::with(|cs| PC.1.borrow_ref_mut(cs).take().unwrap()); + /// + /// // Interrupt occurs. + /// # interrupt(); + /// + /// consumer.dequeue().unwrap(); + /// } + /// ``` + #[cfg(feature = "nightly")] + pub const fn split(&mut self) -> (Producer<'_, T, N>, Consumer<'_, T, N>) { + (Producer { rb: self }, Consumer { rb: self }) + } } impl Default for Queue { @@ -632,7 +735,40 @@ impl<'a, T, const N: usize> Producer<'a, T, N> { mod tests { use std::hash::{Hash, Hasher}; - use crate::spsc::Queue; + use super::Queue; + + #[cfg(feature = "nightly")] + #[test] + fn const_split() { + use critical_section::Mutex; + use std::cell::RefCell; + + use super::{Consumer, Producer}; + + static PC: ( + Mutex>>>, + Mutex>>>, + ) = { + static mut Q: Queue<(), 4> = Queue::new(); + let (p, c) = unsafe { Q.split() }; + + ( + Mutex::new(RefCell::new(Some(p))), + Mutex::new(RefCell::new(Some(c))), + ) + }; + static PRODUCER: &Mutex>>> = &PC.0; + static CONSUMER: &Mutex>>> = &PC.1; + + let consumer = critical_section::with(|cs| CONSUMER.borrow_ref_mut(cs).take().unwrap()); + let producer = critical_section::with(|cs| PRODUCER.borrow_ref_mut(cs).take().unwrap()); + + let mut producer: Producer<'static, (), 4> = producer; + let mut consumer: Consumer<'static, (), 4> = consumer; + + assert_eq!(producer.enqueue(()), Ok(())); + assert_eq!(consumer.dequeue(), Some(())); + } #[test] fn full() {