Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify atomic availability detection. #400

Merged
merged 1 commit into from
Oct 30, 2023
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
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 @@ -34,6 +34,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 @@ -91,31 +95,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 @@ -295,8 +299,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 @@ -315,7 +319,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 @@ -360,7 +364,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 @@ -548,7 +552,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