From 21a856d4349481611967372e25528e0bbdeb9a16 Mon Sep 17 00:00:00 2001 From: Donny Zimmanck Date: Fri, 12 Nov 2021 10:14:12 -0800 Subject: [PATCH 01/13] Added a ordered() method to the History Buffer which returns an iterator on the data that starts from the oldest data and stops at the last data entered. --- src/histbuf.rs | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/src/histbuf.rs b/src/histbuf.rs index 7104e797..585b6c47 100644 --- a/src/histbuf.rs +++ b/src/histbuf.rs @@ -181,6 +181,31 @@ impl HistoryBuffer { pub fn as_slice(&self) -> &[T] { unsafe { slice::from_raw_parts(self.data.as_ptr() as *const _, self.len()) } } + + /// Returns an iterator for iterating over the buffer from oldest to newest + /// + /// # Examples + /// + /// ``` + /// use heapless::HistoryBuffer; + /// + /// let mut buffer: HistoryBuffer = HistoryBuffer::new(); + /// buffer.extend([0, 0, 0, 1, 2, 3, 4, 5, 6]); + /// let expected = [1, 2, 3, 4, 5, 6]; + /// for (x, y) in buffer.ordered().zip(expected.iter()) { + /// assert_eq!(x, y) + /// } + /// + /// ``` + pub fn ordered<'a>(&'a self) -> OrderedIter<'a, T, N> { + if !self.filled { + return OrderedIter { buf: self, cur: 0 }; + } + OrderedIter { + buf: self, + cur: self.write_at, + } + } } impl Extend for HistoryBuffer { @@ -247,6 +272,33 @@ impl Default for HistoryBuffer { } } +pub struct OrderedIter<'a, T, const N: usize> { + buf: &'a HistoryBuffer, + cur: usize, +} + +impl<'a, T, const N: usize> Iterator for OrderedIter<'a, T, N> { + type Item = &'a T; + + fn next(&mut self) -> Option<&'a T> { + let mut next = self.cur + 1; + + // roll-over + if next == self.buf.len() { + next = 0; + } + + // end of iteration + if next == self.buf.write_at { + return None; + } + + let item = &self.buf[self.cur]; + self.cur = next; + Some(item) + } +} + #[cfg(test)] mod tests { use crate::HistoryBuffer; @@ -314,4 +366,23 @@ mod tests { assert_eq!(x.as_slice(), [5, 2, 3, 4]); } + + #[test] + fn ordered() { + // test on a filled buffer + let mut buffer: HistoryBuffer = HistoryBuffer::new(); + buffer.extend([0, 0, 0, 1, 2, 3, 4, 5, 6]); + let expected = [1, 2, 3, 4, 5, 6]; + for (x, y) in buffer.ordered().zip(expected.iter()) { + assert_eq!(x, y); + } + + // test on a un-filled buffer + let mut buffer: HistoryBuffer = HistoryBuffer::new(); + buffer.extend([1, 2, 3]); + let expected = [1, 2, 3]; + for (x, y) in buffer.ordered().zip(expected.iter()) { + assert_eq!(x, y); + } + } } From 6a8fa3a4754818625d372de951b24bac172334b0 Mon Sep 17 00:00:00 2001 From: Donny Zimmanck Date: Fri, 12 Nov 2021 11:37:14 -0800 Subject: [PATCH 02/13] Expose the OrderedIterator for external use. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index fe976f10..cdc312f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,7 +76,7 @@ pub use binary_heap::BinaryHeap; pub use deque::Deque; -pub use histbuf::HistoryBuffer; +pub use histbuf::{HistoryBuffer, OrderedIter}; pub use indexmap::{Bucket, FnvIndexMap, IndexMap, Pos}; pub use indexset::{FnvIndexSet, IndexSet}; pub use linear_map::LinearMap; From 8b320e64e7e7403ec72fed6a19ed17c7791d1090 Mon Sep 17 00:00:00 2001 From: Donny Zimmanck Date: Fri, 12 Nov 2021 12:54:53 -0800 Subject: [PATCH 03/13] Derive the clone trait for the OrderedIter making it easy for users to use an iterator on a History Buffer more than once. --- src/histbuf.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/histbuf.rs b/src/histbuf.rs index 585b6c47..ab2c9a3a 100644 --- a/src/histbuf.rs +++ b/src/histbuf.rs @@ -272,6 +272,7 @@ impl Default for HistoryBuffer { } } +#[derive(Clone)] pub struct OrderedIter<'a, T, const N: usize> { buf: &'a HistoryBuffer, cur: usize, From ae103535d3d5a73ae69a3ea78a19248ac3eb2580 Mon Sep 17 00:00:00 2001 From: Donny Zimmanck Date: Fri, 12 Nov 2021 13:30:58 -0800 Subject: [PATCH 04/13] Added doc string to the OrderedIter struct --- src/histbuf.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/histbuf.rs b/src/histbuf.rs index ab2c9a3a..3423ef98 100644 --- a/src/histbuf.rs +++ b/src/histbuf.rs @@ -272,6 +272,7 @@ impl Default for HistoryBuffer { } } +/// An iterator on the underlying buffer ordered from oldest data to newest #[derive(Clone)] pub struct OrderedIter<'a, T, const N: usize> { buf: &'a HistoryBuffer, From 77adbab007f3082679eebca0083460a0fba5314a Mon Sep 17 00:00:00 2001 From: Finn Bear Date: Sat, 15 Jan 2022 15:48:05 -0800 Subject: [PATCH 05/13] Fix panic if buffer was empty. --- src/histbuf.rs | 41 +++++++++++++++++++++++++++-------------- src/lib.rs | 2 +- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/histbuf.rs b/src/histbuf.rs index 3423ef98..5b2f54e4 100644 --- a/src/histbuf.rs +++ b/src/histbuf.rs @@ -182,7 +182,7 @@ impl HistoryBuffer { unsafe { slice::from_raw_parts(self.data.as_ptr() as *const _, self.len()) } } - /// Returns an iterator for iterating over the buffer from oldest to newest + /// Returns an iterator for iterating over the buffer from oldest to newest. /// /// # Examples /// @@ -192,18 +192,26 @@ impl HistoryBuffer { /// let mut buffer: HistoryBuffer = HistoryBuffer::new(); /// buffer.extend([0, 0, 0, 1, 2, 3, 4, 5, 6]); /// let expected = [1, 2, 3, 4, 5, 6]; - /// for (x, y) in buffer.ordered().zip(expected.iter()) { + /// for (x, y) in buffer.oldest_ordered().zip(expected.iter()) { /// assert_eq!(x, y) /// } /// /// ``` - pub fn ordered<'a>(&'a self) -> OrderedIter<'a, T, N> { - if !self.filled { - return OrderedIter { buf: self, cur: 0 }; - } - OrderedIter { - buf: self, - cur: self.write_at, + pub fn oldest_ordered<'a>(&'a self) -> OldestOrdered<'a, T, N> { + if self.filled { + OldestOrdered { + buf: self, + cur: self.write_at, + } + } else if self.write_at == 0 { + // special case of empty buffer. first call to next + // will wrap cur to 0, equaling write_at, resulting in None. + OldestOrdered { + buf: self, + cur: usize::MAX, + } + } else { + OldestOrdered { buf: self, cur: 0 } } } } @@ -274,16 +282,16 @@ impl Default for HistoryBuffer { /// An iterator on the underlying buffer ordered from oldest data to newest #[derive(Clone)] -pub struct OrderedIter<'a, T, const N: usize> { +pub struct OldestOrdered<'a, T, const N: usize> { buf: &'a HistoryBuffer, cur: usize, } -impl<'a, T, const N: usize> Iterator for OrderedIter<'a, T, N> { +impl<'a, T, const N: usize> Iterator for OldestOrdered<'a, T, N> { type Item = &'a T; fn next(&mut self) -> Option<&'a T> { - let mut next = self.cur + 1; + let mut next = self.cur.wrapping_add(1); // roll-over if next == self.buf.len() { @@ -371,11 +379,16 @@ mod tests { #[test] fn ordered() { + // test on an empty buffer + let buffer: HistoryBuffer = HistoryBuffer::new(); + assert_eq!(buffer.oldest_ordered().next(), None); + assert_eq!(buffer.oldest_ordered().next(), None); + // test on a filled buffer let mut buffer: HistoryBuffer = HistoryBuffer::new(); buffer.extend([0, 0, 0, 1, 2, 3, 4, 5, 6]); let expected = [1, 2, 3, 4, 5, 6]; - for (x, y) in buffer.ordered().zip(expected.iter()) { + for (x, y) in buffer.oldest_ordered().zip(expected.iter()) { assert_eq!(x, y); } @@ -383,7 +396,7 @@ mod tests { let mut buffer: HistoryBuffer = HistoryBuffer::new(); buffer.extend([1, 2, 3]); let expected = [1, 2, 3]; - for (x, y) in buffer.ordered().zip(expected.iter()) { + for (x, y) in buffer.oldest_ordered().zip(expected.iter()) { assert_eq!(x, y); } } diff --git a/src/lib.rs b/src/lib.rs index cdc312f2..a8b52a3f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,7 +76,7 @@ pub use binary_heap::BinaryHeap; pub use deque::Deque; -pub use histbuf::{HistoryBuffer, OrderedIter}; +pub use histbuf::{HistoryBuffer, OldestOrdered}; pub use indexmap::{Bucket, FnvIndexMap, IndexMap, Pos}; pub use indexset::{FnvIndexSet, IndexSet}; pub use linear_map::LinearMap; From 4132b5115c8146a1fb67de6be762aa3d3a08e258 Mon Sep 17 00:00:00 2001 From: Finn Bear Date: Sat, 15 Jan 2022 16:41:33 -0800 Subject: [PATCH 06/13] Fix termination behavior and add comprehensive test cases. --- src/histbuf.rs | 87 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 25 deletions(-) diff --git a/src/histbuf.rs b/src/histbuf.rs index 5b2f54e4..eb808b48 100644 --- a/src/histbuf.rs +++ b/src/histbuf.rs @@ -202,16 +202,15 @@ impl HistoryBuffer { OldestOrdered { buf: self, cur: self.write_at, - } - } else if self.write_at == 0 { - // special case of empty buffer. first call to next - // will wrap cur to 0, equaling write_at, resulting in None. - OldestOrdered { - buf: self, - cur: usize::MAX, + wrapped: false, } } else { - OldestOrdered { buf: self, cur: 0 } + // special case: act like we wrapped already to handle empty buffer. + OldestOrdered { + buf: self, + cur: 0, + wrapped: true, + } } } } @@ -285,26 +284,25 @@ impl Default for HistoryBuffer { pub struct OldestOrdered<'a, T, const N: usize> { buf: &'a HistoryBuffer, cur: usize, + wrapped: bool, } impl<'a, T, const N: usize> Iterator for OldestOrdered<'a, T, N> { type Item = &'a T; fn next(&mut self) -> Option<&'a T> { - let mut next = self.cur.wrapping_add(1); - - // roll-over - if next == self.buf.len() { - next = 0; + if self.cur == self.buf.len() && self.buf.filled { + // roll-over + self.cur = 0; + self.wrapped = true; } - // end of iteration - if next == self.buf.write_at { + if self.cur == self.buf.write_at && self.wrapped { return None; } let item = &self.buf[self.cur]; - self.cur = next; + self.cur += 1; Some(item) } } @@ -312,6 +310,7 @@ impl<'a, T, const N: usize> Iterator for OldestOrdered<'a, T, N> { #[cfg(test)] mod tests { use crate::HistoryBuffer; + use core::fmt::Debug; #[test] fn new() { @@ -381,23 +380,61 @@ mod tests { fn ordered() { // test on an empty buffer let buffer: HistoryBuffer = HistoryBuffer::new(); - assert_eq!(buffer.oldest_ordered().next(), None); - assert_eq!(buffer.oldest_ordered().next(), None); + let mut iter = buffer.oldest_ordered(); + assert_eq!(iter.next(), None); + assert_eq!(iter.next(), None); + + let mut buffer: HistoryBuffer = HistoryBuffer::new(); + buffer.write(1); + let mut iter = buffer.oldest_ordered(); + assert_eq!(iter.next(), Some(&1)); + assert_eq!(iter.next(), None); // test on a filled buffer let mut buffer: HistoryBuffer = HistoryBuffer::new(); buffer.extend([0, 0, 0, 1, 2, 3, 4, 5, 6]); - let expected = [1, 2, 3, 4, 5, 6]; - for (x, y) in buffer.oldest_ordered().zip(expected.iter()) { - assert_eq!(x, y); - } + assert_eq!(buffer.len(), 6); + + assert_eq_iter(buffer.oldest_ordered(), &[1, 2, 3, 4, 5, 6]); // test on a un-filled buffer let mut buffer: HistoryBuffer = HistoryBuffer::new(); buffer.extend([1, 2, 3]); - let expected = [1, 2, 3]; - for (x, y) in buffer.oldest_ordered().zip(expected.iter()) { - assert_eq!(x, y); + assert_eq!(buffer.len(), 3); + assert_eq_iter(buffer.oldest_ordered(), &[1, 2, 3]); + + // comprehensive test + for n in 0..50 { + const N: usize = 7; + let mut buffer: HistoryBuffer = HistoryBuffer::new(); + buffer.extend(0..n); + assert_eq_iter( + buffer.oldest_ordered().copied(), + n.saturating_sub(N as u8)..n, + ); + } + } + + /// Compares two iterators item by item, making sure they stop at the same time. + fn assert_eq_iter( + a: impl IntoIterator, + b: impl IntoIterator, + ) { + let mut a = a.into_iter(); + let mut b = b.into_iter(); + + let mut i = 0; + loop { + let a_item = a.next(); + let b_item = b.next(); + + assert_eq!(a_item, b_item, "{}", i); + + i += 1; + + if b_item.is_none() { + break; + } } } } From 55a1ec9ea301e5c67419fafe1e188df58bc7ec93 Mon Sep 17 00:00:00 2001 From: Finn Bear Date: Sat, 15 Jan 2022 16:58:10 -0800 Subject: [PATCH 07/13] Cleanup tests. --- src/histbuf.rs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/histbuf.rs b/src/histbuf.rs index eb808b48..2bd52ec0 100644 --- a/src/histbuf.rs +++ b/src/histbuf.rs @@ -384,26 +384,19 @@ mod tests { assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); - let mut buffer: HistoryBuffer = HistoryBuffer::new(); - buffer.write(1); - let mut iter = buffer.oldest_ordered(); - assert_eq!(iter.next(), Some(&1)); - assert_eq!(iter.next(), None); - - // test on a filled buffer - let mut buffer: HistoryBuffer = HistoryBuffer::new(); - buffer.extend([0, 0, 0, 1, 2, 3, 4, 5, 6]); - assert_eq!(buffer.len(), 6); - - assert_eq_iter(buffer.oldest_ordered(), &[1, 2, 3, 4, 5, 6]); - // test on a un-filled buffer let mut buffer: HistoryBuffer = HistoryBuffer::new(); buffer.extend([1, 2, 3]); assert_eq!(buffer.len(), 3); assert_eq_iter(buffer.oldest_ordered(), &[1, 2, 3]); - // comprehensive test + // test on a filled buffer + let mut buffer: HistoryBuffer = HistoryBuffer::new(); + buffer.extend([0, 0, 0, 1, 2, 3, 4, 5, 6]); + assert_eq!(buffer.len(), 6); + assert_eq_iter(buffer.oldest_ordered(), &[1, 2, 3, 4, 5, 6]); + + // comprehensive test all cases for n in 0..50 { const N: usize = 7; let mut buffer: HistoryBuffer = HistoryBuffer::new(); From 60da0951f36d6dc9db2057907eca978f925bc039 Mon Sep 17 00:00:00 2001 From: Finn Bear Date: Sat, 15 Jan 2022 23:32:03 -0800 Subject: [PATCH 08/13] Changelog. --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2a63b0f..3306effa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added + +- Added `OldestOrdered` iterator for `HistoryBuffer` + ## [v0.7.7] - 2021-09-22 ### Fixed From 63c0370d690ba7a9121754c3ecd361db6088b694 Mon Sep 17 00:00:00 2001 From: Tyler Holmes Date: Sat, 15 Jan 2022 14:39:20 -0800 Subject: [PATCH 09/13] add riscv cas atomic support and cleanup the #cfg's for it --- Cargo.toml | 6 ++++++ build.rs | 27 ++++++++++++++++++++++++--- src/mpmc.rs | 12 ++++++------ src/pool/llsc.rs | 4 ++-- src/pool/singleton/arc.rs | 4 ++-- 5 files changed, 40 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bfedb936..64fe5062 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,12 @@ scoped_threadpool = "0.1.8" [target.thumbv6m-none-eabi.dependencies] atomic-polyfill = { version = "0.1.2", optional = true } +[target.riscv32i-unknown-none-elf.dependencies] +atomic-polyfill = { version = "0.1.4", optional = true } + +[target.riscv32imc-unknown-none-elf.dependencies] +atomic-polyfill = { version = "0.1.4", optional = true } + [dependencies] hash32 = "0.2.1" diff --git a/build.rs b/build.rs index 094b6cd2..f1c4bca6 100644 --- a/build.rs +++ b/build.rs @@ -21,10 +21,19 @@ fn main() -> Result<(), Box> { println!("cargo:rustc-cfg=armv7a"); } - // built-in targets with no atomic / CAS support as of nightly-2019-12-17 + // built-in targets with no atomic / CAS support as of nightly-2022-01-13 + // AND not supported by the atomic-polyfill crate // see the `no-atomics.sh` / `no-cas.sh` script sitting next to this file match &target[..] { - "msp430-none-elf" | "riscv32i-unknown-none-elf" | "riscv32imc-unknown-none-elf" => {} + "avr-unknown-gnu-atmega328" + | "bpfeb-unknown-none" + | "bpfel-unknown-none" + | "msp430-none-elf" + // | "riscv32i-unknown-none-elf" // supported by atomic-polyfill + // | "riscv32imc-unknown-none-elf" // supported by atomic-polyfill + | "thumbv4t-none-eabi" + // | "thumbv6m-none-eabi" // supported by atomic-polyfill + => {} _ => { println!("cargo:rustc-cfg=has_cas"); @@ -32,12 +41,24 @@ fn main() -> Result<(), Box> { }; match &target[..] { - "msp430-none-elf" | "riscv32i-unknown-none-elf" | "riscv32imc-unknown-none-elf" => {} + "avr-unknown-gnu-atmega328" + | "msp430-none-elf" + // | "riscv32i-unknown-none-elf" // supported by atomic-polyfill + // | "riscv32imc-unknown-none-elf" // supported by atomic-polyfill + => {} _ => { println!("cargo:rustc-cfg=has_atomics"); } }; + // Let the code know if it should use atomic-polyfill or not + match &target[..] { + "thumbv6m-none-eabi" | "riscv32i-unknown-none-elf" | "riscv32imc-unknown-none-elf" => { + println!("cargo:rustc-cfg=use_atomic_polyfill"); + } + _ => {} + } + Ok(()) } diff --git a/src/mpmc.rs b/src/mpmc.rs index 0fbf7ce3..ef345f91 100644 --- a/src/mpmc.rs +++ b/src/mpmc.rs @@ -84,18 +84,18 @@ use core::{cell::UnsafeCell, mem::MaybeUninit}; -#[cfg(all(feature = "mpmc_large", not(armv6m)))] +#[cfg(all(feature = "mpmc_large", not(use_atomic_polyfill)))] type AtomicTargetSize = core::sync::atomic::AtomicUsize; -#[cfg(all(feature = "mpmc_large", armv6m))] +#[cfg(all(feature = "mpmc_large", use_atomic_polyfill))] type AtomicTargetSize = atomic_polyfill::AtomicUsize; -#[cfg(all(not(feature = "mpmc_large"), not(armv6m)))] +#[cfg(all(not(feature = "mpmc_large"), not(use_atomic_polyfill)))] type AtomicTargetSize = core::sync::atomic::AtomicU8; -#[cfg(all(not(feature = "mpmc_large"), armv6m))] +#[cfg(all(not(feature = "mpmc_large"), use_atomic_polyfill))] type AtomicTargetSize = atomic_polyfill::AtomicU8; -#[cfg(not(armv6m))] +#[cfg(not(use_atomic_polyfill))] type Ordering = core::sync::atomic::Ordering; -#[cfg(armv6m)] +#[cfg(use_atomic_polyfill)] type Ordering = atomic_polyfill::Ordering; #[cfg(feature = "mpmc_large")] diff --git a/src/pool/llsc.rs b/src/pool/llsc.rs index 83081521..823a8a7c 100644 --- a/src/pool/llsc.rs +++ b/src/pool/llsc.rs @@ -3,10 +3,10 @@ pub use core::ptr::NonNull as Ptr; use core::{cell::UnsafeCell, ptr}; -#[cfg(armv6m)] +#[cfg(use_atomic_polyfill)] use atomic_polyfill::{AtomicPtr, Ordering}; -#[cfg(not(armv6m))] +#[cfg(not(use_atomic_polyfill))] use core::sync::atomic::{AtomicPtr, Ordering}; /// Unfortunate implementation detail required to use the diff --git a/src/pool/singleton/arc.rs b/src/pool/singleton/arc.rs index d110817e..b64b0f7b 100644 --- a/src/pool/singleton/arc.rs +++ b/src/pool/singleton/arc.rs @@ -81,10 +81,10 @@ use core::{ sync::atomic, }; -#[cfg(armv6m)] +#[cfg(use_atomic_polyfill)] use atomic_polyfill::{AtomicUsize, Ordering}; -#[cfg(not(armv6m))] +#[cfg(not(use_atomic_polyfill))] use core::sync::atomic::{AtomicUsize, Ordering}; use crate::pool::{self, stack::Ptr, Node}; From a4696454391acb501a53165b47d3dbbcfc945998 Mon Sep 17 00:00:00 2001 From: Tyler Holmes Date: Sat, 15 Jan 2022 14:39:36 -0800 Subject: [PATCH 10/13] fix documentation --- src/mpmc.rs | 9 ++++++--- src/pool/mod.rs | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/mpmc.rs b/src/mpmc.rs index ef345f91..a5c327f5 100644 --- a/src/mpmc.rs +++ b/src/mpmc.rs @@ -1,6 +1,7 @@ //! A fixed capacity Multiple-Producer Multiple-Consumer (MPMC) lock-free queue //! -//! NOTE: This module is not available on targets that do *not* support CAS operations, e.g. ARMv6-M +//! NOTE: This module is not available on targets that do *not* support CAS operations and are not +//! emulated by the [`atomic_polyfill`] crate (e.g., MSP430). //! //! # Example //! @@ -73,8 +74,10 @@ //! //! # Portability //! -//! This module is not exposed to architectures that lack the instructions to implement CAS loops. -//! Those architectures include ARMv6-M (`thumbv6m-none-eabi`) and MSP430 (`msp430-none-elf`). +//! This module requires CAS atomic instructions which are not available on all architectures +//! (e.g. ARMv6-M (`thumbv6m-none-eabi`) and MSP430 (`msp430-none-elf`)). These atomics can be emulated +//! however with [`atomic_polyfill`], which is enabled with the `cas` feature and is enabled by default +//! for `thumbv6m-none-eabi` and `riscv32` targets. MSP430 is currently not supported by [`atomic_polyfill`]. //! //! # References //! diff --git a/src/pool/mod.rs b/src/pool/mod.rs index 15ee5430..65a9c451 100644 --- a/src/pool/mod.rs +++ b/src/pool/mod.rs @@ -1,6 +1,7 @@ //! A heap-less, interrupt-safe, lock-free memory pool (\*) //! -//! NOTE: This module is not available on targets that do *not* support CAS operations, e.g. ARMv6-M +//! NOTE: This module is not available on targets that do *not* support CAS operations and are not +//! emulated by the [`atomic_polyfill`] crate (e.g., MSP430). //! //! (\*) Currently, the implementation is only lock-free *and* `Sync` on ARMv6, ARMv7-{A,R,M} & ARMv8-M //! devices @@ -59,8 +60,10 @@ //! on the target architecture (see section on ['Soundness'](#soundness) for more information). For //! this reason, `Pool` only implements `Sync` when compiling for some ARM cores. //! -//! Also note that ARMv6-M architecture lacks the primitives for CAS loops so this module does *not* -//! exist for `thumbv6m-none-eabi`. +//! This module requires CAS atomic instructions which are not available on all architectures +//! (e.g. ARMv6-M (`thumbv6m-none-eabi`) and MSP430 (`msp430-none-elf`)). These atomics can be emulated +//! however with [`atomic_polyfill`], which is enabled with the `cas` feature and is enabled by default +//! for `thumbv6m-none-eabi` and `riscv32` targets. MSP430 is currently not supported by [`atomic_polyfill`]. //! //! # Soundness //! From 58e020a2871946c081703996e8e35941723e497b Mon Sep 17 00:00:00 2001 From: Tyler Holmes Date: Sat, 15 Jan 2022 14:39:54 -0800 Subject: [PATCH 11/13] have docs.rs document the optional features --- Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 64fe5062..626365ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,3 +64,6 @@ version = "0.1" [dependencies.defmt] version = ">=0.2.0,<0.4" optional = true + +[package.metadata.docs.rs] +all-features = true From a68ce63fbed4e3f2da76687a8ed0b064167448b9 Mon Sep 17 00:00:00 2001 From: Tyler Holmes Date: Sat, 15 Jan 2022 14:40:01 -0800 Subject: [PATCH 12/13] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e604881..cb0939e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `OldestOrdered` iterator for `HistoryBuffer` +### Changed + +- `atomic-polyfill` is now enabled and used for `cas` atomic emulation on `riscv` targets + ## [v0.7.9] - 2021-12-16 ### Fixed From bc9e208089a415f0400f88ba4a8bfd20d955e317 Mon Sep 17 00:00:00 2001 From: Tyler Holmes Date: Sun, 16 Jan 2022 11:51:23 -0800 Subject: [PATCH 13/13] differentiate full vs CAS polyfill --- build.rs | 12 +++++++++--- src/mpmc.rs | 12 ++++++------ src/pool/llsc.rs | 4 ++-- src/pool/singleton/arc.rs | 4 ++-- src/spsc.rs | 17 ++++++++--------- 5 files changed, 27 insertions(+), 22 deletions(-) diff --git a/build.rs b/build.rs index f1c4bca6..0840d6fb 100644 --- a/build.rs +++ b/build.rs @@ -52,10 +52,16 @@ fn main() -> Result<(), Box> { } }; - // Let the code know if it should use atomic-polyfill or not + // Let the code know if it should use atomic-polyfill or not, and what aspects + // of polyfill it requires match &target[..] { - "thumbv6m-none-eabi" | "riscv32i-unknown-none-elf" | "riscv32imc-unknown-none-elf" => { - println!("cargo:rustc-cfg=use_atomic_polyfill"); + "riscv32i-unknown-none-elf" | "riscv32imc-unknown-none-elf" => { + println!("cargo:rustc-cfg=full_atomic_polyfill"); + println!("cargo:rustc-cfg=cas_atomic_polyfill"); + } + + "thumbv6m-none-eabi" => { + println!("cargo:rustc-cfg=cas_atomic_polyfill"); } _ => {} } diff --git a/src/mpmc.rs b/src/mpmc.rs index a5c327f5..91b33311 100644 --- a/src/mpmc.rs +++ b/src/mpmc.rs @@ -87,18 +87,18 @@ use core::{cell::UnsafeCell, mem::MaybeUninit}; -#[cfg(all(feature = "mpmc_large", not(use_atomic_polyfill)))] +#[cfg(all(feature = "mpmc_large", not(cas_atomic_polyfill)))] type AtomicTargetSize = core::sync::atomic::AtomicUsize; -#[cfg(all(feature = "mpmc_large", use_atomic_polyfill))] +#[cfg(all(feature = "mpmc_large", cas_atomic_polyfill))] type AtomicTargetSize = atomic_polyfill::AtomicUsize; -#[cfg(all(not(feature = "mpmc_large"), not(use_atomic_polyfill)))] +#[cfg(all(not(feature = "mpmc_large"), not(cas_atomic_polyfill)))] type AtomicTargetSize = core::sync::atomic::AtomicU8; -#[cfg(all(not(feature = "mpmc_large"), use_atomic_polyfill))] +#[cfg(all(not(feature = "mpmc_large"), cas_atomic_polyfill))] type AtomicTargetSize = atomic_polyfill::AtomicU8; -#[cfg(not(use_atomic_polyfill))] +#[cfg(not(cas_atomic_polyfill))] type Ordering = core::sync::atomic::Ordering; -#[cfg(use_atomic_polyfill)] +#[cfg(cas_atomic_polyfill)] type Ordering = atomic_polyfill::Ordering; #[cfg(feature = "mpmc_large")] diff --git a/src/pool/llsc.rs b/src/pool/llsc.rs index 823a8a7c..33f65557 100644 --- a/src/pool/llsc.rs +++ b/src/pool/llsc.rs @@ -3,10 +3,10 @@ pub use core::ptr::NonNull as Ptr; use core::{cell::UnsafeCell, ptr}; -#[cfg(use_atomic_polyfill)] +#[cfg(cas_atomic_polyfill)] use atomic_polyfill::{AtomicPtr, Ordering}; -#[cfg(not(use_atomic_polyfill))] +#[cfg(not(cas_atomic_polyfill))] use core::sync::atomic::{AtomicPtr, Ordering}; /// Unfortunate implementation detail required to use the diff --git a/src/pool/singleton/arc.rs b/src/pool/singleton/arc.rs index b64b0f7b..a83519d8 100644 --- a/src/pool/singleton/arc.rs +++ b/src/pool/singleton/arc.rs @@ -81,10 +81,10 @@ use core::{ sync::atomic, }; -#[cfg(use_atomic_polyfill)] +#[cfg(cas_atomic_polyfill)] use atomic_polyfill::{AtomicUsize, Ordering}; -#[cfg(not(use_atomic_polyfill))] +#[cfg(not(cas_atomic_polyfill))] use core::sync::atomic::{AtomicUsize, Ordering}; use crate::pool::{self, stack::Ptr, Node}; diff --git a/src/spsc.rs b/src/spsc.rs index 2c0a6b44..38990d56 100644 --- a/src/spsc.rs +++ b/src/spsc.rs @@ -2,8 +2,8 @@ //! //! Implementation based on //! -//! NOTE: This module is not available on targets that do *not* support atomic loads, e.g. RISC-V -//! cores w/o the A (Atomic) extension +//! NOTE: This module is not available on targets that do *not* support atomic loads and are not +//! supported by [`atomic_polyfill`]. (e.g., MSP430). //! //! # Examples //! @@ -84,13 +84,12 @@ //! - The numbers reported correspond to the successful path (i.e. `Some` is returned by `dequeue` //! and `Ok` is returned by `enqueue`). -use core::{ - cell::UnsafeCell, - fmt, hash, - mem::MaybeUninit, - ptr, - sync::atomic::{AtomicUsize, Ordering}, -}; +use core::{cell::UnsafeCell, fmt, hash, mem::MaybeUninit, ptr}; + +#[cfg(full_atomic_polyfill)] +use atomic_polyfill::{AtomicUsize, Ordering}; +#[cfg(not(full_atomic_polyfill))] +use core::sync::atomic::{AtomicUsize, Ordering}; /// A statically allocated single producer single consumer queue with a capacity of `N - 1` elements ///