Crates.io | critical-section |
lib.rs | critical-section |
version | |
source | src |
created_at | 2021-05-09 22:19:59.246258 |
updated_at | 2024-10-16 13:28:54.13049 |
description | Cross-platform critical section |
homepage | |
repository | https://github.com/rust-embedded/critical-section |
max_upload_size | |
id | 395364 |
Cargo.toml error: | TOML parse error at line 17, column 1 | 17 | autolib = false | ^^^^^^^ unknown field `autolib`, expected one of `name`, `version`, `edition`, `authors`, `description`, `readme`, `license`, `repository`, `homepage`, `documentation`, `build`, `resolver`, `links`, `default-run`, `default_dash_run`, `rust-version`, `rust_dash_version`, `rust_version`, `license-file`, `license_dash_file`, `license_file`, `licenseFile`, `license_capital_file`, `forced-target`, `forced_dash_target`, `autobins`, `autotests`, `autoexamples`, `autobenches`, `publish`, `metadata`, `keywords`, `categories`, `exclude`, `include` |
size | 0 |
This project is developed and maintained by the HAL team.
A critical section that works everywhere!
When writing software for embedded systems, it's common to use a "critical section" as a basic primitive to control concurrency. A critical section is essentially a mutex global to the whole process, that can be acquired by only one thread at a time. This can be used to protect data behind mutexes, to emulate atomics in targets that don't support them, etc.
There's a wide range of possible implementations depending on the execution environment:
std
targets, acquiring a global std::sync::Mutex
.Libraries often need to use critical sections, but there's no universal API for this in core
. This leads
library authors to hard-code them for their target, or at best add some cfg
s to support a few targets.
This doesn't scale since there are many targets out there, and in the general case it's impossible to know
which critical section implementation is needed from the Rust target alone. For example, the thumbv7em-none-eabi
target
could be cases 1-4 from the above list.
This crate solves the problem by providing this missing universal API.
acquire
, release
and with
that libraries can directly use.cortex-m
, riscv
), RTOS bindings, or HALs for multicore chips to supply the correct implementation so that all the crates in the dependency tree automatically use it.no-std
binaries.First, add a dependency on a crate providing a critical section implementation. Enable the critical-section-*
Cargo feature if required by the crate.
Implementations are typically provided by either architecture-support crates, HAL crates, and OS/RTOS bindings, including:
cortex-m
crate provides an implementation for all single-core Cortex-M microcontrollers via its critical-section-single-core
featureriscv
crate provides an implementation for all single-hart RISC-V microcontrollers via its critical-section-single-hart
featuremsp430
crate provides an implementation for all MSP430 microcontrollers via its critical-section-single-core
featurerp2040-hal
crate provides a multi-core-safe critical section for the RP2040 microcontroller via its critical-section-impl
featureavr-device
crate provides an implementation for all AVR microcontrollers via its critical-section-impl
featureesp-hal-common
crate provides an implementation for ESP32 microcontrollers which is used by the ESP HALsembassy-rp
crate provides a multi-core-safe critical section for the RP2040 microcontroller via its critical-section-impl
featurenrf-softdevice
crate provides a critical section that's compatible with the nRF soft-device firmware via its critical-section-impl
featureFor example, for single-core Cortex-M targets, you can use:
[dependencies]
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"]}
Then you can use critical_section::with()
.
use core::cell::Cell;
use critical_section::Mutex;
static MY_VALUE: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
critical_section::with(|cs| {
// This code runs within a critical section.
// `cs` is a token that you can use to "prove" that to some API,
// for example to a `Mutex`:
MY_VALUE.borrow(cs).set(42);
});
# #[cfg(not(feature = "std"))] // needed for `cargo test --features std`
# mod no_std {
# struct MyCriticalSection;
# critical_section::set_impl!(MyCriticalSection);
# unsafe impl critical_section::Impl for MyCriticalSection {
# unsafe fn acquire() -> () {}
# unsafe fn release(token: ()) {}
# }
# }
std
binaries.Add the critical-section
dependency to Cargo.toml
enabling the std
feature. This makes the critical-section
crate itself
provide an implementation based on std::sync::Mutex
, so you don't have to add any other dependency.
[dependencies]
critical-section = { version = "1.1", features = ["std"]}
If you're writing a library intended to be portable across many targets, simply add a dependency on critical-section
and use critical_section::free
and/or Mutex
as usual.
Do not add any dependency supplying a critical section implementation. Do not enable any critical-section-*
Cargo feature.
This has to be done by the end user, enabling the correct implementation for their target.
Do not enable any Cargo feature in critical-section
.
std
tests for no-std
libraries.If you want to run std
-using tests in otherwise no-std
libraries, enable the std
feature in dev-dependencies
only.
This way the main target will use the no-std
implementation chosen by the end-user's binary, and only the test targets
will use the std
implementation.
[dependencies]
critical-section = "1.1"
[dev-dependencies]
critical-section = { version = "1.1", features = ["std"]}
Crates adding support for a particular architecture, chip or operating system should provide a critical section implementation. It is strongly recommended to gate the implementation behind a feature, so the user can still use another implementation if needed (having two implementations in the same binary will cause linking to fail).
Add the dependency, and a critical-section-*
feature to your Cargo.toml
:
[features]
# Enable critical section implementation that does "foo"
critical-section-foo = ["critical-section/restore-state-bool"]
[dependencies]
critical-section = { version = "1.0", optional = true }
Then, provide the critical implementation like this:
# #[cfg(not(feature = "std"))] // needed for `cargo test --features std`
# mod no_std {
// This is a type alias for the enabled `restore-state-*` feature.
// For example, it is `bool` if you enable `restore-state-bool`.
use critical_section::RawRestoreState;
struct MyCriticalSection;
critical_section::set_impl!(MyCriticalSection);
unsafe impl critical_section::Impl for MyCriticalSection {
unsafe fn acquire() -> RawRestoreState {
// TODO
}
unsafe fn release(token: RawRestoreState) {
// TODO
}
}
# }
If you get an error like these:
undefined reference to `_critical_section_1_0_acquire'
undefined reference to `_critical_section_1_0_release'
it is because you (or a library) are using critical_section::with
without providing a critical section implementation.
Make sure you're depending on a crate providing the implementation, and have enabled the critical-section-*
feature in it if required. See the Usage
section above.
The error can also be caused by having the dependency but never use
ing it. This can be fixed by adding a dummy use
:
use the_cs_impl_crate as _;
If you get errors like these:
error: symbol `_critical_section_1_0_acquire` is already defined
it is because you have two crates trying to provide a critical section implementation. You can only have one implementation in a program.
You can use cargo tree --format '{p} {f}'
to view all dependencies and their enabled features. Make sure
that in the whole dependency tree, exactly one implementation is provided.
Check for multiple versions of the same crate as well. For example, check the critical-section-single-core
feature is not enabled for both cortex-m
0.7 and 0.8.
An alternative solution would be to use a CriticalSection
trait, and make all
code that needs acquiring the critical section generic over it. This has a few problems:
Mutex
es in static
variables, and static
s can't
be generic.This crate is guaranteed to compile on the following Rust versions:
std
feature is not enabled: stable Rust 1.54 and up.std
feature is enabled: stable Rust 1.63 and up.It might compile with older versions but that may change in any new patch release.
See here for details on how the MSRV may be upgraded.
This work is licensed under either of
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Contribution to this crate is organized under the terms of the Rust Code of Conduct, the maintainer of this crate, the HAL team, promises to intervene to uphold that code of conduct.