Skip to content

Commit

Permalink
Migrate to portable-atomic
Browse files Browse the repository at this point in the history
  • Loading branch information
ithinuel committed Nov 27, 2023
1 parent 3dff810 commit 6b5ac37
Show file tree
Hide file tree
Showing 6 changed files with 15 additions and 126 deletions.
10 changes: 1 addition & 9 deletions .github/workflows/doctests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,10 @@ name: Documentation Tests
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- features: cortex-m
nodefault: "--no-default-features"
- features: ""
nodefault: ""

steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: actions-rs/cargo@v1
with:
command: test
args: ${{ matrix.nodefault }} --features=${{ matrix.features }} --manifest-path core/Cargo.toml
args: --manifest-path core/Cargo.toml
2 changes: 1 addition & 1 deletion .github/workflows/embedded-builds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
include:
- features: ""
target: thumbv7em-none-eabihf
- feature: cortex-m
- feature: portable-atomic/critical-section
target: thumbv6m-none-eabi

steps:
Expand Down
7 changes: 2 additions & 5 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,8 @@ categories = [
license = "MIT OR Apache-2.0"

[dependencies]
cortex-m = { version = "0.6.0", optional = true }

[dependencies.defmt]
version = "0.3.0"
optional = true
portable-atomic = { version = "1.5.1", default-features = false, features = ["require-cas"] }
defmt = { version = "0.3.0", optional = true }

[package.metadata.docs.rs]
all-features = true
110 changes: 11 additions & 99 deletions core/src/bbbuffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ use core::{
ptr::NonNull,
result::Result as CoreResult,
slice::from_raw_parts_mut,
sync::atomic::{
AtomicBool, AtomicUsize,
Ordering::{AcqRel, Acquire, Release},
},
sync::atomic::Ordering::{AcqRel, Acquire, Release},
};
use portable_atomic::{AtomicBool, AtomicUsize};

#[derive(Debug)]
/// A backing structure for a BBQueue. Can be used to create either
/// a BBQueue or a split Producer/Consumer pair
Expand Down Expand Up @@ -63,9 +62,6 @@ impl<'a, const N: usize> BBBuffer<N> {
/// is placed at `static` scope within the `.bss` region, the explicit initialization
/// will be elided (as it is already performed as part of memory initialization)
///
/// NOTE: If the `cortex-m` feature is selected, this function takes a short critical section
/// while splitting.
///
/// ```rust
/// # // bbqueue test shim!
/// # fn bbqtest() {
Expand All @@ -81,12 +77,11 @@ impl<'a, const N: usize> BBBuffer<N> {
/// # }
/// #
/// # fn main() {
/// # #[cfg(not(feature = "cortex-m"))]
/// # bbqtest();
/// # }
/// ```
pub fn try_split(&'a self) -> Result<(Producer<'a, N>, Consumer<'a, N>)> {
if atomic::swap(&self.already_split, true, AcqRel) {
if self.already_split.swap(true, AcqRel) {
return Err(Error::AlreadySplit);
}

Expand Down Expand Up @@ -122,9 +117,6 @@ impl<'a, const N: usize> BBBuffer<N> {
/// of the buffer. This is necessary to prevent undefined behavior. If the buffer
/// is placed at `static` scope within the `.bss` region, the explicit initialization
/// will be elided (as it is already performed as part of memory initialization)
///
/// NOTE: If the `cortex-m` feature is selected, this function takes a short critical
/// section while splitting.
pub fn try_split_framed(&'a self) -> Result<(FrameProducer<'a, N>, FrameConsumer<'a, N>)> {
let (producer, consumer) = self.try_split()?;
Ok((FrameProducer { producer }, FrameConsumer { consumer }))
Expand Down Expand Up @@ -159,7 +151,6 @@ impl<'a, const N: usize> BBBuffer<N> {
/// # }
/// #
/// # fn main() {
/// # #[cfg(not(feature = "cortex-m"))]
/// # bbqtest();
/// # }
/// ```
Expand Down Expand Up @@ -345,14 +336,13 @@ impl<'a, const N: usize> Producer<'a, N> {
/// # }
/// #
/// # fn main() {
/// # #[cfg(not(feature = "cortex-m"))]
/// # bbqtest();
/// # }
/// ```
pub fn grant_exact(&mut self, sz: usize) -> Result<GrantW<'a, N>> {
let inner = unsafe { &self.bbq.as_ref() };

if atomic::swap(&inner.write_in_progress, true, AcqRel) {
if inner.write_in_progress.swap(true, AcqRel) {
return Err(Error::GrantInProgress);
}

Expand Down Expand Up @@ -443,14 +433,13 @@ impl<'a, const N: usize> Producer<'a, N> {
/// # }
/// #
/// # fn main() {
/// # #[cfg(not(feature = "cortex-m"))]
/// # bbqtest();
/// # }
/// ```
pub fn grant_max_remaining(&mut self, mut sz: usize) -> Result<GrantW<'a, N>> {
let inner = unsafe { &self.bbq.as_ref() };

if atomic::swap(&inner.write_in_progress, true, AcqRel) {
if inner.write_in_progress.swap(true, AcqRel) {
return Err(Error::GrantInProgress);
}

Expand Down Expand Up @@ -548,14 +537,13 @@ impl<'a, const N: usize> Consumer<'a, N> {
/// # }
/// #
/// # fn main() {
/// # #[cfg(not(feature = "cortex-m"))]
/// # bbqtest();
/// # }
/// ```
pub fn read(&mut self) -> Result<GrantR<'a, N>> {
let inner = unsafe { &self.bbq.as_ref() };

if atomic::swap(&inner.read_in_progress, true, AcqRel) {
if inner.read_in_progress.swap(true, AcqRel) {
return Err(Error::GrantInProgress);
}

Expand Down Expand Up @@ -607,7 +595,7 @@ impl<'a, const N: usize> Consumer<'a, N> {
pub fn split_read(&mut self) -> Result<SplitGrantR<'a, N>> {
let inner = unsafe { &self.bbq.as_ref() };

if atomic::swap(&inner.read_in_progress, true, AcqRel) {
if inner.read_in_progress.swap(true, AcqRel) {
return Err(Error::GrantInProgress);
}

Expand Down Expand Up @@ -674,7 +662,6 @@ impl<const N: usize> BBBuffer<N> {
/// # }
/// #
/// # fn main() {
/// # #[cfg(not(feature = "cortex-m"))]
/// # bbqtest();
/// # }
/// ```
Expand All @@ -690,9 +677,6 @@ impl<const N: usize> BBBuffer<N> {
/// the contents, or by setting a the number of bytes to
/// automatically be committed with `to_commit()`, then no bytes
/// will be comitted for writing.
///
/// If the `cortex-m` feature is selected, dropping the grant
/// without committing it takes a short critical section,
#[derive(Debug, PartialEq)]
pub struct GrantW<'a, const N: usize> {
pub(crate) buf: &'a mut [u8],
Expand All @@ -710,10 +694,6 @@ unsafe impl<'a, const N: usize> Send for GrantW<'a, N> {}
/// the contents, or by setting the number of bytes to automatically
/// be released with `to_release()`, then no bytes will be released
/// as read.
///
///
/// If the `cortex-m` feature is selected, dropping the grant
/// without releasing it takes a short critical section,
#[derive(Debug, PartialEq)]
pub struct GrantR<'a, const N: usize> {
pub(crate) buf: &'a mut [u8],
Expand Down Expand Up @@ -743,9 +723,6 @@ impl<'a, const N: usize> GrantW<'a, N> {
///
/// If `used` is larger than the given grant, the maximum amount will
/// be commited
///
/// NOTE: If the `cortex-m` feature is selected, this function takes a short critical
/// section while committing.
pub fn commit(mut self, used: usize) {
self.commit_inner(used);
forget(self);
Expand All @@ -770,7 +747,6 @@ impl<'a, const N: usize> GrantW<'a, N> {
/// # }
/// #
/// # fn main() {
/// # #[cfg(not(feature = "cortex-m"))]
/// # bbqtest();
/// # }
/// ```
Expand Down Expand Up @@ -814,7 +790,7 @@ impl<'a, const N: usize> GrantW<'a, N> {
let used = min(len, used);

let write = inner.write.load(Acquire);
atomic::fetch_sub(&inner.reserve, len - used, AcqRel);
inner.reserve.fetch_sub(len - used, AcqRel);

let max = N;
let last = inner.last.load(Acquire);
Expand Down Expand Up @@ -860,9 +836,6 @@ impl<'a, const N: usize> GrantR<'a, N> {
///
/// If `used` is larger than the given grant, the full grant will
/// be released.
///
/// NOTE: If the `cortex-m` feature is selected, this function takes a short critical
/// section while releasing.
pub fn release(mut self, used: usize) {
// Saturate the grant release
let used = min(self.buf.len(), used);
Expand Down Expand Up @@ -903,7 +876,6 @@ impl<'a, const N: usize> GrantR<'a, N> {
/// # }
/// #
/// # fn main() {
/// # #[cfg(not(feature = "cortex-m"))]
/// # bbqtest();
/// # }
/// ```
Expand Down Expand Up @@ -951,7 +923,7 @@ impl<'a, const N: usize> GrantR<'a, N> {
debug_assert!(used <= self.buf.len());

// This should be fine, purely incrementing
let _ = atomic::fetch_add(&inner.read, used, Release);
let _ = inner.read.fetch_add(used, Release);

inner.read_in_progress.store(false, Release);
}
Expand All @@ -968,9 +940,6 @@ impl<'a, const N: usize> SplitGrantR<'a, N> {
///
/// If `used` is larger than the given grant, the full grant will
/// be released.
///
/// NOTE: If the `cortex-m` feature is selected, this function takes a short critical
/// section while releasing.
pub fn release(mut self, used: usize) {
// Saturate the grant release
let used = min(self.combined_len(), used);
Expand Down Expand Up @@ -1004,7 +973,6 @@ impl<'a, const N: usize> SplitGrantR<'a, N> {
/// # }
/// #
/// # fn main() {
/// # #[cfg(not(feature = "cortex-m"))]
/// # bbqtest();
/// # }
/// ```
Expand Down Expand Up @@ -1036,7 +1004,7 @@ impl<'a, const N: usize> SplitGrantR<'a, N> {

if used <= self.buf1.len() {
// This should be fine, purely incrementing
let _ = atomic::fetch_add(&inner.read, used, Release);
let _ = inner.read.fetch_add(used, Release);
} else {
// Also release parts of the second buffer
inner.read.store(used - self.buf1.len(), Release);
Expand Down Expand Up @@ -1101,59 +1069,3 @@ impl<'a, const N: usize> DerefMut for GrantR<'a, N> {
self.buf
}
}

#[cfg(feature = "cortex-m")]
mod atomic {
use core::sync::atomic::{
AtomicBool, AtomicUsize,
Ordering::{self, Acquire, Release},
};
use cortex_m::interrupt::free;

#[inline(always)]
pub fn fetch_add(atomic: &AtomicUsize, val: usize, _order: Ordering) -> usize {
free(|_| {
let prev = atomic.load(Acquire);
atomic.store(prev.wrapping_add(val), Release);
prev
})
}

#[inline(always)]
pub fn fetch_sub(atomic: &AtomicUsize, val: usize, _order: Ordering) -> usize {
free(|_| {
let prev = atomic.load(Acquire);
atomic.store(prev.wrapping_sub(val), Release);
prev
})
}

#[inline(always)]
pub fn swap(atomic: &AtomicBool, val: bool, _order: Ordering) -> bool {
free(|_| {
let prev = atomic.load(Acquire);
atomic.store(val, Release);
prev
})
}
}

#[cfg(not(feature = "cortex-m"))]
mod atomic {
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};

#[inline(always)]
pub fn fetch_add(atomic: &AtomicUsize, val: usize, order: Ordering) -> usize {
atomic.fetch_add(val, order)
}

#[inline(always)]
pub fn fetch_sub(atomic: &AtomicUsize, val: usize, order: Ordering) -> usize {
atomic.fetch_sub(val, order)
}

#[inline(always)]
pub fn swap(atomic: &AtomicBool, val: bool, order: Ordering) -> bool {
atomic.swap(val, order)
}
}
1 change: 0 additions & 1 deletion core/src/framed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
//! # }
//! #
//! # fn main() {
//! # #[cfg(not(feature = "cortex-m"))]
//! # bbqtest();
//! # }
//! ```
Expand Down
11 changes: 0 additions & 11 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,6 @@
//! assert!(BB.try_split().is_err());
//! }
//! ```
//!
//! ## Features
//!
//! By default BBQueue uses atomic operations which are available on most platforms. However on some
//! (mostly embedded) platforms atomic support is limited and with the default features you will get
//! a compiler error about missing atomic methods.
//!
//! This crate contains special support for Cortex-M0(+) targets with the `cortex-m` feature. By
//! enabling the feature, unsupported atomic operations will be replaced with critical sections
//! implemented by disabling interrupts. The critical sections are very short, a few instructions at
//! most, so they should make no difference to most applications.

#![cfg_attr(not(feature = "std"), no_std)]
#![deny(missing_docs)]
Expand Down

0 comments on commit 6b5ac37

Please sign in to comment.