Skip to content

Commit

Permalink
Make Queue::split const.
Browse files Browse the repository at this point in the history
  • Loading branch information
reitermarkus committed Feb 19, 2024
1 parent c593fa5 commit af09a22
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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]
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
140 changes: 138 additions & 2 deletions src/spsc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,10 +305,113 @@ impl<T, const N: usize> Queue<T, N> {
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<RefCell<Option<Producer<'static, (), 4>>>> =
/// Mutex::new(RefCell::new(None));
///
/// fn interrupt() {
/// let mut producer = {
/// static mut P: Option<Producer<'static, (), 4>> = 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<RefCell<Option<Producer<'_, (), 4>>>>,
/// Mutex<RefCell<Option<Consumer<'_, (), 4>>>>,
/// ) = {
/// 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<Producer<'_, (), 4>> = 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<T, const N: usize> Default for Queue<T, N> {
Expand Down Expand Up @@ -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<RefCell<Option<Producer<'_, (), 4>>>>,
Mutex<RefCell<Option<Consumer<'_, (), 4>>>>,
) = {
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<RefCell<Option<Producer<'_, (), 4>>>> = &PC.0;
static CONSUMER: &Mutex<RefCell<Option<Consumer<'_, (), 4>>>> = &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() {
Expand Down

0 comments on commit af09a22

Please sign in to comment.