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 Jan 21, 2024
1 parent 9a2e0af commit db0e134
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

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 @@ -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;
Expand Down
118 changes: 116 additions & 2 deletions src/spsc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,10 +305,95 @@ 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::{Queue, Producer};
///
/// static PRODUCER: Mutex<RefCell<Option<Producer<'static, (), 4>>>> =
/// 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<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()
/// })
/// });
/// }
/// ```
#[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<RefCell<(Option<Producer<'static, (), 4>>, Option<Consumer<'static, (), 4>>)>> = {
/// 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<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| {
/// (*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<T, const N: usize> Default for Queue<T, N> {
Expand Down Expand Up @@ -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<Producer<'static, (), 4>>,
Option<Consumer<'static, (), 4>>,
)>,
> = {
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() {
Expand Down

0 comments on commit db0e134

Please sign in to comment.