Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Install embedded target
# Note: once https://github.com/hawkw/mycelium/pull/538 lands we can test on
# thumbv6m-none-eabi
run: rustup target add thumbv7em-none-eabi
run: rustup target add thumbv6m-none-eabi
#
# BUILD + TEST
#
Expand All @@ -39,8 +37,8 @@ jobs:

# no features, on mcu
- name: Check bbq2 (no features, on mcu)
run: cargo build --no-default-features --target=thumbv7em-none-eabi
run: cargo build --no-default-features --target=thumbv6m-none-eabi
# default features, on mcu
- name: Check bbq2 (no features, on mcu)
run: cargo build --target=thumbv7em-none-eabi
run: cargo build --target=thumbv6m-none-eabi

1 change: 1 addition & 0 deletions bbq2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ default = [
]
critical-section = [
"dep:critical-section",
"maitake-sync?/critical-section",
]
disable-cache-padding = [
"maitake-sync?/no-cache-pad",
Expand Down
120 changes: 106 additions & 14 deletions bbq2/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,103 @@
//! bbq2
//! # BBQueue
//!
//! A new and improved bipbuffer queue.
//! BBQueue, short for "BipBuffer Queue", is a Single Producer Single Consumer,
//! lockless, no_std, thread safe, queue, based on [BipBuffers]. For more info on
//! the design of the lock-free algorithm used by bbqueue, see [this blog post].
//!
//! NOTE: This will soon be moved into the `bbqueue` crate, and `bbq2` will be
//! deprecated!
//! [BipBuffers]: https://www.codeproject.com/Articles/3479/%2FArticles%2F3479%2FThe-Bip-Buffer-The-Circular-Buffer-with-a-Twist
//! [this blog post]: https://ferrous-systems.com/blog/lock-free-ring-buffer/
//!
//! BBQueue is designed (primarily) to be a First-In, First-Out queue for use with DMA on embedded
//! systems.
//!
//! While Circular/Ring Buffers allow you to send data between two threads (or from an interrupt to
//! main code), you must push the data one piece at a time. With BBQueue, you instead are granted a
//! block of contiguous memory, which can be filled (or emptied) by a DMA engine.
//!
//! ## Local usage
//!
//! ```rust
//! // The "Churrasco" flavor has inline storage, hardware atomic
//! // support, no async support, and is not reference counted.
//! use bbq2::nicknames::Churrasco;
//!
//! // Create a buffer with six elements
//! let bb: Churrasco<6> = Churrasco::new();
//! let prod = bb.stream_producer();
//! let cons = bb.stream_consumer();
//!
//! // Request space for one byte
//! let mut wgr = prod.grant_exact(1).unwrap();
//!
//! // Set the data
//! wgr[0] = 123;
//!
//! assert_eq!(wgr.len(), 1);
//!
//! // Make the data ready for consuming
//! wgr.commit(1);
//!
//! // Read all available bytes
//! let rgr = cons.read().unwrap();
//!
//! assert_eq!(rgr[0], 123);
//!
//! // Release the space for later writes
//! rgr.release(1);
//! ```
//!
//! ## Static usage
//!
//! ```rust
//! use bbq2::nicknames::Churrasco;
//! use std::{thread::{sleep, spawn}, time::Duration};
//!
//! // Create a buffer with six elements
//! static BB: Churrasco<6> = Churrasco::new();
//!
//! fn receiver() {
//! let cons = BB.stream_consumer();
//! loop {
//! if let Ok(rgr) = cons.read() {
//! assert_eq!(rgr.len(), 1);
//! assert_eq!(rgr[0], 123);
//! rgr.release(1);
//! break;
//! }
//! // don't do this in real code, use Notify!
//! sleep(Duration::from_millis(10));
//! }
//! }
//!
//! fn main() {
//! let prod = BB.stream_producer();
//!
//! // spawn the consumer
//! let hdl = spawn(receiver);
//!
//! // Request space for one byte
//! let mut wgr = prod.grant_exact(1).unwrap();
//!
//! // Set the data
//! wgr[0] = 123;
//!
//! assert_eq!(wgr.len(), 1);
//!
//! // Make the data ready for consuming
//! wgr.commit(1);
//!
//! // make sure the receiver terminated
//! hdl.join().unwrap();
//! }
//! ```
//!
//! ## Features
//!
//! TODO

#![cfg_attr(not(any(test, feature = "std")), no_std)]
#![deny(missing_docs)]
#![deny(warnings)]

#[cfg(feature = "alloc")]
extern crate alloc;
Expand Down Expand Up @@ -48,30 +140,30 @@ mod test {
#[cfg(all(target_has_atomic = "ptr", feature = "alloc"))]
#[test]
fn ux() {
use crate::traits::{notifier::blocking::Blocking, storage::BoxedSlice};
use crate::traits::{notifier::polling::Polling, storage::BoxedSlice};

static BBQ: BBQueue<Inline<64>, AtomicCoord, Blocking> = BBQueue::new();
static BBQ: BBQueue<Inline<64>, AtomicCoord, Polling> = BBQueue::new();
let _ = BBQ.stream_producer();
let _ = BBQ.stream_consumer();

let buf2 = Inline::<64>::new();
let bbq2: BBQueue<_, AtomicCoord, Blocking> = BBQueue::new_with_storage(&buf2);
let bbq2: BBQueue<_, AtomicCoord, Polling> = BBQueue::new_with_storage(&buf2);
let _ = bbq2.stream_producer();
let _ = bbq2.stream_consumer();

let buf3 = BoxedSlice::new(64);
let bbq3: BBQueue<_, AtomicCoord, Blocking> = BBQueue::new_with_storage(buf3);
let bbq3: BBQueue<_, AtomicCoord, Polling> = BBQueue::new_with_storage(buf3);
let _ = bbq3.stream_producer();
let _ = bbq3.stream_consumer();
}

#[cfg(target_has_atomic = "ptr")]
#[test]
fn smoke() {
use crate::traits::notifier::blocking::Blocking;
use crate::traits::notifier::polling::Polling;
use core::ops::Deref;

static BBQ: BBQueue<Inline<64>, AtomicCoord, Blocking> = BBQueue::new();
static BBQ: BBQueue<Inline<64>, AtomicCoord, Polling> = BBQueue::new();
let prod = BBQ.stream_producer();
let cons = BBQ.stream_consumer();

Expand All @@ -94,10 +186,10 @@ mod test {
#[cfg(target_has_atomic = "ptr")]
#[test]
fn smoke_framed() {
use crate::traits::notifier::blocking::Blocking;
use crate::traits::notifier::polling::Polling;
use core::ops::Deref;

static BBQ: BBQueue<Inline<64>, AtomicCoord, Blocking> = BBQueue::new();
static BBQ: BBQueue<Inline<64>, AtomicCoord, Polling> = BBQueue::new();
let prod = BBQ.framed_producer();
let cons = BBQ.framed_consumer();

Expand All @@ -116,9 +208,9 @@ mod test {
#[cfg(target_has_atomic = "ptr")]
#[test]
fn framed_misuse() {
use crate::traits::notifier::blocking::Blocking;
use crate::traits::notifier::polling::Polling;

static BBQ: BBQueue<Inline<64>, AtomicCoord, Blocking> = BBQueue::new();
static BBQ: BBQueue<Inline<64>, AtomicCoord, Polling> = BBQueue::new();
let prod = BBQ.stream_producer();
let cons = BBQ.framed_consumer();

Expand Down
50 changes: 25 additions & 25 deletions bbq2/src/nicknames.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@
//!
//! | Storage | Coordination | Notifier | Arc? | Nickname | Source |
//! | :--- | :--- | :--- | :--- | :--- | :--- |
//! | Inline | Critical Section | Blocking | No | Jerk | Jamaica |
//! | Inline | Critical Section | Blocking | Yes | Asado | Argentina |
//! | Inline | Critical Section | Polling | No | Jerk | Jamaica |
//! | Inline | Critical Section | Polling | Yes | Asado | Argentina |
//! | Inline | Critical Section | Async | No | Memphis | USA |
//! | Inline | Critical Section | Async | Yes | Carolina | USA |
//! | Inline | Atomic | Blocking | No | Churrasco | Brazil |
//! | Inline | Atomic | Blocking | Yes | Barbacoa | Mexico |
//! | Inline | Atomic | Polling | No | Churrasco | Brazil |
//! | Inline | Atomic | Polling | Yes | Barbacoa | Mexico |
//! | Inline | Atomic | Async | No | Texas | USA |
//! | Inline | Atomic | Async | Yes | KansasCity | USA |
//! | Heap | Critical Section | Blocking | No | Braai | South Africa |
//! | Heap | Critical Section | Blocking | Yes | Kebab | Türkiye |
//! | Heap | Critical Section | Polling | No | Braai | South Africa |
//! | Heap | Critical Section | Polling | Yes | Kebab | Türkiye |
//! | Heap | Critical Section | Async | No | SiuMei | Hong Kong |
//! | Heap | Critical Section | Async | Yes | Satay | SE Asia |
//! | Heap | Atomic | Blocking | No | YakiNiku | Japan |
//! | Heap | Atomic | Blocking | Yes | GogiGui | South Korea |
//! | Heap | Atomic | Polling | No | YakiNiku | Japan |
//! | Heap | Atomic | Polling | Yes | GogiGui | South Korea |
//! | Heap | Atomic | Async | No | Tandoori | India |
//! | Heap | Atomic | Async | Yes | Lechon | Philippines |

Expand All @@ -31,68 +31,68 @@ use crate::traits::coordination::cs::CsCoord;
use crate::traits::storage::BoxedSlice;
use crate::{
queue::BBQueue,
traits::{notifier::blocking::Blocking, storage::Inline},
traits::{notifier::polling::Polling, storage::Inline},
};

/// Inline Storage, Critical Section, Blocking, Borrowed
/// Inline Storage, Critical Section, Polling, Borrowed
#[cfg(feature = "critical-section")]
pub type Jerk<const N: usize> = BBQueue<Inline<N>, CsCoord, Blocking>;
pub type Jerk<const N: usize> = BBQueue<Inline<N>, CsCoord, Polling>;

/// Inline Storage, Critical Section, Async, Borrowed
#[cfg(feature = "critical-section")]
pub type Memphis<const N: usize, A> = BBQueue<Inline<N>, CsCoord, A>;

/// Inline Storage, Atomics, Blocking, Borrowed
/// Inline Storage, Atomics, Polling, Borrowed
#[cfg(target_has_atomic = "ptr")]
pub type Churrasco<const N: usize> = BBQueue<Inline<N>, AtomicCoord, Blocking>;
pub type Churrasco<const N: usize> = BBQueue<Inline<N>, AtomicCoord, Polling>;

/// Inline Storage, Atomics, Async, Borrowed
#[cfg(target_has_atomic = "ptr")]
pub type Texas<const N: usize, A> = BBQueue<Inline<N>, AtomicCoord, A>;

/// Heap Buffer, Critical Section, Blocking, Borrowed
/// Heap Buffer, Critical Section, Polling, Borrowed
#[cfg(all(feature = "alloc", feature = "critical-section"))]
pub type Braai = BBQueue<BoxedSlice, CsCoord, Blocking>;
pub type Braai = BBQueue<BoxedSlice, CsCoord, Polling>;

/// Heap Buffer, Critical Section, Async, Borrowed
#[cfg(all(feature = "alloc", feature = "critical-section"))]
pub type SiuMei<A> = BBQueue<BoxedSlice, CsCoord, A>;

/// Heap Buffer, Atomics, Blocking, Borrowed
/// Heap Buffer, Atomics, Polling, Borrowed
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
pub type YakiNiku = BBQueue<BoxedSlice, AtomicCoord, Blocking>;
pub type YakiNiku = BBQueue<BoxedSlice, AtomicCoord, Polling>;

/// Heap Buffer, Atomics, Async, Borrowed
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
pub type Tandoori<A> = BBQueue<BoxedSlice, AtomicCoord, A>;

/// Inline Storage, Critical Section, Blocking, Arc
/// Inline Storage, Critical Section, Polling, Arc
#[cfg(all(feature = "alloc", feature = "critical-section"))]
pub type Asado<const N: usize> = ArcBBQueue<Inline<N>, CsCoord, Blocking>;
pub type Asado<const N: usize> = ArcBBQueue<Inline<N>, CsCoord, Polling>;

/// Inline Storage, Critical Section, Async, Arc
#[cfg(all(feature = "alloc", feature = "critical-section"))]
pub type Carolina<const N: usize, A> = ArcBBQueue<Inline<N>, CsCoord, A>;

/// Inline Storage, Atomics, Blocking, Arc
/// Inline Storage, Atomics, Polling, Arc
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
pub type Barbacoa<const N: usize> = ArcBBQueue<Inline<N>, AtomicCoord, Blocking>;
pub type Barbacoa<const N: usize> = ArcBBQueue<Inline<N>, AtomicCoord, Polling>;

/// Inline Storage, Atomics, Async, Arc
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
pub type KansasCity<const N: usize, A> = ArcBBQueue<Inline<N>, AtomicCoord, A>;

/// Heap Buffer, Critical Section, Blocking, Arc
/// Heap Buffer, Critical Section, Polling, Arc
#[cfg(all(feature = "alloc", feature = "critical-section"))]
pub type Kebab = ArcBBQueue<BoxedSlice, CsCoord, Blocking>;
pub type Kebab = ArcBBQueue<BoxedSlice, CsCoord, Polling>;

/// Heap Buffer, Critical Section, Async, Arc
#[cfg(all(feature = "alloc", feature = "critical-section"))]
pub type Satay<A> = ArcBBQueue<BoxedSlice, CsCoord, A>;

/// Heap Buffer, Atomics, Blocking, Arc
/// Heap Buffer, Atomics, Polling, Arc
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
pub type GogiGui = ArcBBQueue<BoxedSlice, AtomicCoord, Blocking>;
pub type GogiGui = ArcBBQueue<BoxedSlice, AtomicCoord, Polling>;

/// Heap Buffer, Atomics, Async, Arc
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
Expand Down
14 changes: 10 additions & 4 deletions bbq2/src/prod_cons/framed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ where
/// a smaller size may be committed. Dropping the grant without calling
/// commit means that no data will be made visible to the consumer.
pub fn grant(&self, sz: H) -> Result<FramedGrantW<Q, H>, WriteGrantError> {
let (ptr, cap) = self.bbq.sto.ptr_len();
let (ptr, cap) = unsafe { self.bbq.sto.ptr_len() };
let needed = sz.into() + core::mem::size_of::<H>();

let offset = self.bbq.cor.grant_exact(cap, needed)?;
Expand Down Expand Up @@ -182,7 +182,7 @@ where
///
/// The returned grant must be released to free the space in the buffer.
pub fn read(&self) -> Result<FramedGrantR<Q, H>, ReadGrantError> {
let (ptr, _cap) = self.bbq.sto.ptr_len();
let (ptr, _cap) = unsafe { self.bbq.sto.ptr_len() };
let (offset, grant_len) = self.bbq.cor.read()?;

// Calculate the size so we can figure out where the body
Expand Down Expand Up @@ -230,6 +230,12 @@ where
Q::Notifier: AsyncNotifier,
H: LenHeader,
{
/// Wait for a single frame
///
/// The FramedConsumer has no control over the size of the read grant,
/// we see whatever size was written by the FramedProducer.
///
/// The returned grant must be released to free the space in the buffer.
pub async fn wait_read(&self) -> FramedGrantR<Q, H> {
self.bbq.not.wait_for_not_empty(|| self.read().ok()).await
}
Expand All @@ -247,7 +253,7 @@ where
/// If `used` is greater than the `sz` used to create this grant, the
/// amount will be clamped to `sz`.
pub fn commit(self, used: H) {
let (_ptr, cap) = self.bbq.sto.ptr_len();
let (_ptr, cap) = unsafe { self.bbq.sto.ptr_len() };
let hdrlen: usize = const { core::mem::size_of::<H>() };
let grant_len = hdrlen + self.hdr.into();
let clamp_hdr = self.hdr.min(used);
Expand Down Expand Up @@ -313,7 +319,7 @@ where
{
fn drop(&mut self) {
// Default drop performs an "abort"
let (_ptr, cap) = self.bbq.sto.ptr_len();
let (_ptr, cap) = unsafe { self.bbq.sto.ptr_len() };
let hdrlen: usize = const { core::mem::size_of::<H>() };
let grant_len = hdrlen + self.hdr.into();
self.bbq.cor.commit_inner(cap, grant_len, 0);
Expand Down
Loading