diff --git a/CHANGELOG.md b/CHANGELOG.md index 848a81a507..29439c5c78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,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 3d7b0bd0aa..443bded6c8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -77,6 +77,7 @@ #![cfg_attr(not(test), no_std)] #![deny(missing_docs)] #![deny(warnings)] +#![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..13db469cd2 100644 --- a/src/spsc.rs +++ b/src/spsc.rs @@ -305,10 +305,95 @@ 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::{Queue, Producer}; + /// + /// static PRODUCER: Mutex>>> = + /// Mutex::new(RefCell::new(None)); + /// + /// 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 + /// }; + /// } + /// + /// 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() + /// }) + /// }); + /// } + /// ``` + #[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::{Queue, Producer, Consumer}; + /// + /// static PC: Mutex>, Option>)>> = { + /// static mut Q: Queue<(), 4> = Queue::new(); + /// let (p, c) = unsafe { Q.split() }; + /// Mutex::new(RefCell::new((Some(p), Some(c)))) + /// }; + /// + /// fn main() { + /// let mut consumer = critical_section::with(|cs| { + /// (*PC.borrow_ref_mut(cs)).1.take().unwrap() + /// }); + /// } + /// + /// 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.borrow_ref_mut(cs)).0.take().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 +717,36 @@ 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; + + static PC: Mutex< + RefCell<( + Option>, + Option>, + )>, + > = { + static mut Q: Queue<(), 4> = Queue::new(); + let (p, c) = unsafe { Q.split() }; + Mutex::new(RefCell::new((Some(p), Some(c)))) + }; + + let (producer, consumer) = critical_section::with(|cs| { + let mut pc = PC.borrow_ref_mut(cs); + (pc.0.take().unwrap(), pc.1.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() {