Skip to content

Commit

Permalink
Merge pull request #400 from rust-embedded/simplify-atomics
Browse files Browse the repository at this point in the history
Simplify atomic availability detection.
  • Loading branch information
newAM authored Oct 30, 2023
2 parents ccd3801 + f58e509 commit 73029ac
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 165 deletions.
22 changes: 7 additions & 15 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,6 @@ jobs:
- thumbv7m-none-eabi
- thumbv8m.base-none-eabi
- thumbv8m.main-none-eabi
toolchain:
- stable
- nightly
features:
- ""
- "cas,portable-atomic/critical-section"
- "serde"
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -113,14 +106,17 @@ jobs:
${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }}
${{ runner.OS }}-build-
- name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }})
- name: Install Rust with target (${{ matrix.target }})
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.toolchain }}
toolchain: stable
targets: ${{ matrix.target }}

- name: cargo check
run: cargo check --target=${{ matrix.target }} --no-default-features --features=${{ matrix.features }}
run: |
cargo check --target=${{ matrix.target }}
cargo check --target=${{ matrix.target }} --features="portable-atomic-critical-section"
cargo check --target=${{ matrix.target }} --features="ufmt serde defmt-03 mpmc_large"
doc:
name: doc
Expand All @@ -130,10 +126,6 @@ jobs:
target:
- x86_64-unknown-linux-gnu
- thumbv7m-none-eabi
features:
- ""
- "cas"
- "serde"
steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down Expand Up @@ -166,7 +158,7 @@ jobs:
targets: ${{ matrix.target }}

- name: cargo doc
run: cargo doc --target=${{ matrix.target }} --no-default-features --features=${{ matrix.features }}
run: cargo doc --target=${{ matrix.target }} --features="ufmt serde defmt-03 mpmc_large portable-atomic-critical-section"

# Run cpass tests
testcpass:
Expand Down
40 changes: 22 additions & 18 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- [breaking-change] this crate now uses `portable-atomic` v1.0 instead of `atomic-polyfill` for emulating
CAS instructions on targets where they're not natively available.
- [breaking-change] `From<&str>` for `String` was replaced with `TryFrom<&str>` because the `From` trait must not fail.
- [breaking-change] Renamed Cargo features
- `defmt-impl` is now `defmt-03`
- `ufmt-impl` is now `ufmt`
- `cas` is removed, atomic polyfilling is now opt-in via the `portable-atomic` feature.

### Fixed

Expand Down Expand Up @@ -92,31 +96,31 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Added

* Added support for AVR architecture.
* Add `entry` API to `IndexMap`
* Implement `IntoIterator` trait for `Indexmap`
* Implement `FromIterator` for `String`
* Add `first` and `last` methods to `IndexMap` and `IndexSet`
* Add `pop_{front_back}_unchecked` methods to `Deque`
- Added support for AVR architecture.
- Add `entry` API to `IndexMap`
- Implement `IntoIterator` trait for `Indexmap`
- Implement `FromIterator` for `String`
- Add `first` and `last` methods to `IndexMap` and `IndexSet`
- Add `pop_{front_back}_unchecked` methods to `Deque`

### Changed

* Optimize the codegen of `Vec::clone`
* `riscv32i` and `riscv32imc` targets unconditionally (e.g. `build --no-default-features`) depends on `atomic-polyfill`
- Optimize the codegen of `Vec::clone`
- `riscv32i` and `riscv32imc` targets unconditionally (e.g. `build --no-default-features`) depends on `atomic-polyfill`

### Fixed

* Inserting an item that replaces an already present item will no longer
fail with an error
- Inserting an item that replaces an already present item will no longer
fail with an error

## [v0.7.11] - 2022-05-09

### Fixed

* Fixed `pool` example in docstring.
* Fixed undefined behavior in `Vec::truncate()`, `Vec::swap_remove_unchecked()`,
- Fixed `pool` example in docstring.
- Fixed undefined behavior in `Vec::truncate()`, `Vec::swap_remove_unchecked()`,
and `Hole::move_to()` (internal to the binary heap implementation).
* Fixed `BinaryHeap` elements are being dropped twice
- Fixed `BinaryHeap` elements are being dropped twice

## [v0.7.10] - 2022-01-21

Expand Down Expand Up @@ -296,8 +300,8 @@ fail with an error
### Added

- opt-out `cas` feature to disable parts of the API that use CAS instructions.
Useful if using a custom (i.e. not built-in) rustc target that does not have CAS
instructions.
Useful if using a custom (i.e. not built-in) rustc target that does not have CAS
instructions.

- singleton `Pool` support on ARMv7-A devices

Expand All @@ -316,7 +320,7 @@ instructions.
- `Pool` now implements the `Sync` trait when targeting ARMv7-R.

- Most data structures can now be constructed in "const context" (e.g. `static
[mut]` variables) using a newtype in `heapless::i`.
[mut]` variables) using a newtype in `heapless::i`.

- `Pool` has gained a `grow_exact` method to more efficiently use statically
allocated memory.
Expand Down Expand Up @@ -361,7 +365,7 @@ instructions.
### Added

- Added a memory pool that's lock-free and interrupt-safe on the ARMv7-M
architecture.
architecture.

- `IndexMap` have gained `Eq` and `PartialEq` implementations.

Expand Down Expand Up @@ -549,7 +553,7 @@ architecture.
- [breaking-change] The error type of all operations that may fail has changed from `()` to
`BufferFullError`.

- Both `RingBuffer` and `Vec` now support arrays of *any* size for their backup storage.
- Both `RingBuffer` and `Vec` now support arrays of _any_ size for their backup storage.

## [v0.1.0] - 2017-04-27

Expand Down
39 changes: 18 additions & 21 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,32 @@ repository = "https://github.com/rust-embedded/heapless"
version = "0.8.0"

[features]
default = ["cas"]
cas = ["portable-atomic"]
ufmt-impl = ["ufmt-write"]
# only for tests
__trybuild = []
# Enable larger MPMC sizes.
mpmc_large = []
# This flag has no version guarantee, the `defmt` dependency can be updated in a patch release
defmt-impl = ["defmt"]
# Enable polyfilling of atomics via `portable-atomic`.
# `portable-atomic` polyfills some functionality by default, but to get full atomics you must
# enable one of its features to tell it how to do it. See `portable-atomic` documentation for details.
portable-atomic = ["dep:portable-atomic"]

[target.thumbv6m-none-eabi.dependencies]
portable-atomic = { version = "1.0", optional = true }
# Enable polyfilling of atomics via portable-atomic, using critical section for locking
portable-atomic-critical-section = ["dep:portable-atomic", "portable-atomic?/critical-section"]

[target.riscv32i-unknown-none-elf.dependencies]
portable-atomic = { version = "1.0" }
# Enable polyfilling of atomics via portable-atomic, using disabling interrupts for locking.
# WARNING: this is only sound for single-core bare-metal privileged-mode targets!
portable-atomic-unsafe-assume-single-core = ["dep:portable-atomic", "portable-atomic?/unsafe-assume-single-core"]

[target.riscv32imc-unknown-none-elf.dependencies]
portable-atomic = { version = "1.0" }
# implement serde traits.
serde = ["dep:serde"]

[target.msp430-none-elf.dependencies]
portable-atomic = { version = "1.0" }
# implement ufmt traits.
ufmt = ["dep:ufmt-write"]

[target.xtensa-esp32s2-none-elf.dependencies]
portable-atomic = { version = "1.0", optional = true }
# Implement defmt::Format from defmt v0.3
defmt-03 = ["dep:defmt"]

[target.'cfg(target_arch = "avr")'.dependencies]
portable-atomic = { version = "1.0", optional = true }
# Enable larger MPMC sizes.
mpmc_large = []

[dependencies]
portable-atomic = { version = "1.0", optional = true }
hash32 = "0.3.0"
serde = { version = "1", optional = true, default-features = false }
stable_deref_trait = { version = "1", default-features = false }
Expand Down
87 changes: 13 additions & 74 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,82 +11,21 @@ use std::{
fn main() -> Result<(), Box<dyn Error>> {
let target = env::var("TARGET")?;

if target.starts_with("thumbv6m-") {
println!("cargo:rustc-cfg=armv6m");
} else if target.starts_with("thumbv7m-") {
println!("cargo:rustc-cfg=armv7m");
} else if target.starts_with("thumbv7em-") {
println!("cargo:rustc-cfg=armv7m");
} else if target.starts_with("armv7r-") | target.starts_with("armebv7r-") {
println!("cargo:rustc-cfg=armv7r");
} else if target.starts_with("thumbv8m.base") {
println!("cargo:rustc-cfg=armv8m_base");
} else if target.starts_with("thumbv8m.main") {
println!("cargo:rustc-cfg=armv8m_main");
} else if target.starts_with("armv7-") | target.starts_with("armv7a-") {
println!("cargo:rustc-cfg=armv7a");
}

let is_avr = env::var("CARGO_CFG_TARGET_ARCH").as_deref() == Ok("avr");

// Set some cfg's depending on the target.
// - has_atomics: atomic load/store is available (either natively or through portable-atomic)
// - has_cas: atomic CAS is available (either natively or through portable-atomic)
// - use_portable_atomic: Use portable-atomic for all atomics (load/store and CAS).
// - use_portable_atomic_cas: Use portable-atomic for CAS atomic operations. Load/store can keep using core::sync:atomic.

// built-in targets with no atomic / CAS support as of nightly-2022-01-13
// AND not supported by the portable-atomic crate
// see the `no-atomics.sh` / `no-cas.sh` script sitting next to this file
if is_avr {
// lacks cas
} else {
match &target[..] {
"avr-unknown-gnu-atmega328"
| "bpfeb-unknown-none"
| "bpfel-unknown-none"
// | "msp430-none-elf" // supported by portable-atomic
// | "riscv32i-unknown-none-elf" // supported by portable-atomic
// | "riscv32imc-unknown-none-elf" // supported by portable-atomic
// | "thumbv4t-none-eabi" // supported by portable-atomic
// | "thumbv6m-none-eabi" // supported by portable-atomic
=> {}

_ => {
println!("cargo:rustc-cfg=has_cas");
}
}
// Manually list targets that have atomic load/store, but no CAS.
// Remove when `cfg(target_has_atomic_load_store)` is stable.
// last updated nightly-2023-10-28
match &target[..] {
"armv4t-none-eabi"
| "armv5te-none-eabi"
| "avr-unknown-gnu-atmega328"
| "bpfeb-unknown-none"
| "bpfel-unknown-none"
| "thumbv4t-none-eabi"
| "thumbv5te-none-eabi"
| "thumbv6m-none-eabi" => println!("cargo:rustc-cfg=has_atomic_load_store"),
_ => {}
};

if is_avr {
// lacks atomics
} else {
println!("cargo:rustc-cfg=has_atomics");
}

// Let the code know if it should use portable-atomic or not, for either
// only CAS, or for all atomics.
if is_avr {
println!("cargo:rustc-cfg=use_portable_atomic");
println!("cargo:rustc-cfg=use_portable_atomic_cas");
} else {
match &target[..] {
"riscv32i-unknown-none-elf"
| "riscv32imc-unknown-none-elf"
| "xtensa-esp32s2-none-elf"
| "thumbv4t-none-eabi"
| "msp430-none-elf" => {
println!("cargo:rustc-cfg=use_portable_atomic");
println!("cargo:rustc-cfg=use_portable_atomic_cas");
}

"thumbv6m-none-eabi" => {
println!("cargo:rustc-cfg=use_portable_atomic_cas");
}
_ => {}
}
}

// AArch64 instruction set contains `clrex` but not `ldrex` or `strex`; the
// probe will succeed when we already know to deny this target from LLSC.
if !target.starts_with("aarch64") {
Expand Down
14 changes: 0 additions & 14 deletions no-atomics.sh

This file was deleted.

14 changes: 0 additions & 14 deletions no-cas.sh

This file was deleted.

24 changes: 19 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
//!
//! The `heapless` crate provides the following optional Cargo features:
//!
//! - `ufmt-impl`: Implement [`ufmt_write::uWrite`] for `String<N>` and `Vec<u8, N>`
//! - `ufmt`: Implement [`ufmt_write::uWrite`] for `String<N>` and `Vec<u8, N>`
//!
//! [`ufmt_write::uWrite`]: https://docs.rs/ufmt-write/
//!
Expand Down Expand Up @@ -108,17 +108,31 @@ mod de;
mod ser;

pub mod binary_heap;
#[cfg(feature = "defmt-impl")]
#[cfg(feature = "defmt-03")]
mod defmt;
#[cfg(all(has_cas, feature = "cas"))]
#[cfg(any(
// assume we have all atomics available if we're using portable-atomic
feature = "portable-atomic",
// target has native atomic CAS (mpmc_large requires usize, otherwise just u8)
all(feature = "mpmc_large", target_has_atomic = "ptr"),
all(not(feature = "mpmc_large"), target_has_atomic = "8")
))]
pub mod mpmc;
#[cfg(any(arm_llsc, target_arch = "x86"))]
pub mod pool;
pub mod sorted_linked_list;
#[cfg(has_atomics)]
#[cfg(any(
// assume we have all atomics available if we're using portable-atomic
feature = "portable-atomic",
// target has native atomic CAS. Note this is too restrictive, spsc requires load/store only, not CAS.
// This should be `cfg(target_has_atomic_load_store)`, but that's not stable yet.
target_has_atomic = "ptr",
// or the current target is in a list in build.rs of targets known to have load/store but no CAS.
has_atomic_load_store
))]
pub mod spsc;

#[cfg(feature = "ufmt-impl")]
#[cfg(feature = "ufmt")]
mod ufmt;

mod sealed;
4 changes: 2 additions & 2 deletions src/mpmc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@
use core::{cell::UnsafeCell, mem::MaybeUninit};

#[cfg(not(use_portable_atomic_cas))]
#[cfg(not(feature = "portable-atomic"))]
use core::sync::atomic;
#[cfg(use_portable_atomic_cas)]
#[cfg(feature = "portable-atomic")]
use portable_atomic as atomic;

use atomic::Ordering;
Expand Down
4 changes: 2 additions & 2 deletions src/spsc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@
use core::{cell::UnsafeCell, fmt, hash, mem::MaybeUninit, ptr};

#[cfg(not(use_portable_atomic))]
#[cfg(not(feature = "portable-atomic"))]
use core::sync::atomic;
#[cfg(use_portable_atomic)]
#[cfg(feature = "portable-atomic")]
use portable_atomic as atomic;

use atomic::{AtomicUsize, Ordering};
Expand Down

0 comments on commit 73029ac

Please sign in to comment.