diff --git a/.vscode/settings.json b/.vscode/settings.json index a3bcce0..b16083e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@ { "editor.formatOnSave": true, "rust-analyzer.procMacro.enable": true, - "rust-analyzer.cargo.loadOutDirsFromCheck": true, - "rust-analyzer.assist.importMergeBehavior": "last" + "rust-analyzer.cargo.runBuildScripts": true, + "rust-analyzer.assist.importGranularity": "module", } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ff1fc1..d82a923 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -No unreleased changes yet +- Breaking change: removed all builtin impls. These are going to be provided by platform-support now. ## 0.2.7 - 2022-04-08 diff --git a/Cargo.toml b/Cargo.toml index 95dd932..a494994 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,8 @@ [package] name = "critical-section" -version = "0.2.7" -authors = ["Dario Nieuwenhuis "] +version = "0.3.0" edition = "2018" -description = "Critical section abstraction" +description = "Cross-platform critical section" repository = "https://github.com/embassy-rs/critical-section" readme = "README.md" license = "MIT OR Apache-2.0" @@ -13,30 +12,8 @@ categories = [ "concurrency", ] -[features] -custom-impl = [] - [dependencies] bare-metal = "1.0.0" -cfg-if = "1.0.0" - -[target.thumbv6m-none-eabi.dependencies] -cortex-m = "0.7.2" -[target.thumbv7em-none-eabi.dependencies] -cortex-m = "0.7.2" -[target.thumbv7em-none-eabihf.dependencies] -cortex-m = "0.7.2" -[target.thumbv7m-none-eabi.dependencies] -cortex-m = "0.7.2" -[target."thumbv8m.base-none-eabi".dependencies] -cortex-m = "0.7.2" -[target."thumbv8m.main-none-eabi".dependencies] -cortex-m = "0.7.2" -[target."thumbv8m.main-none-eabihf".dependencies] -cortex-m = "0.7.2" - -[target.'cfg(target_arch = "riscv32")'.dependencies] -riscv = {version = "0.7.0"} [package.metadata.docs.rs] all-features = true diff --git a/README.md b/README.md index 1d7c0be..4fbaa4c 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ targets that don't support them, etc. There's a wide range of possible implementations depending on the execution environment: - For bare-metal single core, disabling interrupts globally. -- For bare-metal multicore, acquiring a hardware spinlocks and disabling interrupts globally. +- For bare-metal multicore, acquiring a hardware spinlock and disabling interrupts globally. - For bare-metal using a RTOS, it usually provides library functions for acquiring a critical section, often named "scheduler lock" or "kernel lock". - For bare-metal running in non-privileged mode, usually some system call is needed. - For `std` targets, acquiring a global `std::sync::Mutex`. @@ -26,27 +26,13 @@ could be cases 1-4 from the above list. This crate solves the problem by providing this missing universal API. - It provides functions `acquire`, `release` and `free` that libraries can directly use. -- It provides some built-in impls for well-known targets, so in many cases it Just Works. -- It provides a way for any crate to supply a "custom impl" that overrides the built-in one. This allows environment-support crates such as RTOS bindings or HALs for multicore chips to supply the correct impl so that all the crates in the dependency tree automatically use it. +- It provides a way for any crate to supply an implementation. This allows "target support" crates such as architecture crates (`cortex-m`, `riscv`), RTOS bindings, or HALs for multicore chips to supply the correct impl so that all the crates in the dependency tree automatically use it. -## Built-in impls - - -| Target | Mechanism | Notes | -|--------------------|---------------------------|-------------------| -| thumbv[6-8] | `cpsid` / `cpsie`. | Only sound in single-core privileged mode. | -| riscv32* | set/clear `mstatus.mie` | Only sound in single-core privileged mode. | -| avr* | `cli` / `sei` | Only sound in single-core (does multicore AVR even exist?) | -| std targets | Global `std::sync::Mutex` | | - -## Providing a custom impl - -- Enable the Cargo feature `custom-impl` in the `critical-section` crate. -- Define it like the following: +## Providing an implementation ```rust struct CriticalSection; -critical_section::custom_impl!(CriticalSection); +critical_section::set_impl!(CriticalSection); unsafe impl critical_section::Impl for CriticalSection { unsafe fn acquire() -> u8 { @@ -60,7 +46,7 @@ unsafe impl critical_section::Impl for CriticalSection { } ``` -If you're writing a library crate that provides a custom impl, it is strongly recommended that +If you're writing a library crate that provides an impl, it is strongly recommended that you only provide it if explicitly enabled by the user via a Cargo feature `critical-section-impl`. This allows the user to opt out from your impl to supply their own. diff --git a/avr-specs/avr-atmega328p.json b/avr-specs/avr-atmega328p.json deleted file mode 100644 index e236b08..0000000 --- a/avr-specs/avr-atmega328p.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "arch": "avr", - "atomic-cas": false, - "cpu": "atmega328p", - "data-layout": "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8", - "eh-frame-header": false, - "exe-suffix": ".elf", - "executables": true, - "late-link-args": { - "gcc": [ - "-lgcc" - ] - }, - "linker": "avr-gcc", - "linker-is-gnu": true, - "llvm-target": "avr-unknown-unknown", - "max-atomic-width": 8, - "no-default-libraries": false, - "pre-link-args": { - "gcc": [ - "-mmcu=atmega328p", - "-Wl,--as-needed" - ] - }, - "target-c-int-width": "16", - "target-pointer-width": "16" -} diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index a407ee4..0000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,12 +0,0 @@ -# Before upgrading check that everything is available on all tier1 targets here: -# https://rust-lang.github.io/rustup-components-history -[toolchain] -channel = "nightly-2021-08-18" -components = [ "rust-src", "rustfmt" ] -targets = [ - "thumbv6m-none-eabi", - "thumbv7em-none-eabi", - "riscv32imc-unknown-none-elf", - "riscv32imac-unknown-none-elf", - "avr-specs/avr-atmpeg328p.json", -] diff --git a/src/lib.rs b/src/lib.rs index 747fb92..b3073f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,4 @@ -#![cfg_attr(docsrs, feature(doc_cfg))] #![no_std] -#![cfg_attr(target_arch = "avr", feature(llvm_asm))] -#![cfg_attr(target_arch = "avr", feature(extended_key_value_attributes))] #![doc = include_str!("../README.md")] pub use bare_metal::CriticalSection; @@ -58,141 +55,47 @@ pub fn with(f: impl FnOnce(CriticalSection) -> R) -> R { } } -cfg_if::cfg_if! { - if #[cfg(feature = "custom-impl")] { - /// Methods required for a custom critical section implementation. - /// - /// This trait is not intended to be used except when implementing a custom critical section. - /// - /// Implementations must uphold the contract specified in [`crate::acquire`] and [`crate::release`]. - #[cfg_attr(docsrs, doc(cfg(feature = "custom-impl")))] - pub unsafe trait Impl { - /// Acquire the critical section. - unsafe fn acquire() -> u8; - /// Release the critical section. - unsafe fn release(token: u8); - } - - /// Set the custom critical section implementation. - /// - /// # Example - /// - /// ``` - /// struct MyCriticalSection; - /// critical_section::custom_impl!(MyCriticalSection); - /// - /// unsafe impl critical_section::Impl for MyCriticalSection { - /// unsafe fn acquire() -> u8 { - /// // ... - /// # return 0 - /// } - /// - /// unsafe fn release(token: u8) { - /// // ... - /// } - /// } - /// - #[cfg_attr(docsrs, doc(cfg(feature = "custom-impl")))] - #[macro_export] - macro_rules! custom_impl { - ($t: ty) => { - #[no_mangle] - unsafe fn _critical_section_acquire() -> u8 { - <$t as $crate::Impl>::acquire() - } - #[no_mangle] - unsafe fn _critical_section_release(token: u8) { - <$t as $crate::Impl>::release(token) - } - }; - } - } else if #[cfg(cortex_m)] { - #[no_mangle] - unsafe fn _critical_section_acquire() -> u8 { - let primask = cortex_m::register::primask::read(); - cortex_m::interrupt::disable(); - primask.is_active() as _ - } - - #[no_mangle] - unsafe fn _critical_section_release(token: u8) { - if token != 0 { - cortex_m::interrupt::enable() - } - } - } else if #[cfg(target_arch = "avr")] { - #[no_mangle] - unsafe fn _critical_section_acquire() -> u8 { - let mut sreg: u8; - llvm_asm!( - "in $0, 0x3F - cli" - : "=r"(sreg) - ::: "volatile" - ); - sreg - } - - #[no_mangle] - unsafe fn _critical_section_release(token: u8) { - if token & 0x80 == 0x80 { - llvm_asm!("sei" :::: "volatile"); - } - } - } else if #[cfg(target_arch = "riscv32")] { - #[no_mangle] - unsafe fn _critical_section_acquire() -> u8 { - let interrupts_active = riscv::register::mstatus::read().mie(); - riscv::interrupt::disable(); - interrupts_active as _ - } - - #[no_mangle] - unsafe fn _critical_section_release(token: u8) { - if token != 0 { - riscv::interrupt::enable(); - } - } - } else if #[cfg(any(unix, windows, wasm, target_arch = "wasm32"))] { - extern crate std; - use std::sync::{Once, Mutex, MutexGuard}; - use core::cell::Cell; - - static INIT: Once = Once::new(); - static mut GLOBAL_LOCK: Option> = None; - static mut GLOBAL_GUARD: Option> = None; - - std::thread_local!(static IS_LOCKED: Cell = Cell::new(false)); +/// Methods required for a custom critical section implementation. +/// +/// This trait is not intended to be used except when implementing a custom critical section. +/// +/// Implementations must uphold the contract specified in [`crate::acquire`] and [`crate::release`]. +pub unsafe trait Impl { + /// Acquire the critical section. + unsafe fn acquire() -> u8; + /// Release the critical section. + unsafe fn release(token: u8); +} +/// Set the custom critical section implementation. +/// +/// # Example +/// +/// ``` +/// struct MyCriticalSection; +/// critical_section::custom_impl!(MyCriticalSection); +/// +/// unsafe impl critical_section::Impl for MyCriticalSection { +/// unsafe fn acquire() -> u8 { +/// // ... +/// # return 0 +/// } +/// +/// unsafe fn release(token: u8) { +/// // ... +/// } +/// } +/// +#[macro_export] +macro_rules! set_impl { + ($t: ty) => { #[no_mangle] unsafe fn _critical_section_acquire() -> u8 { - INIT.call_once(|| unsafe { - GLOBAL_LOCK.replace(Mutex::new(())); - }); - - // Allow reentrancy by checking thread local state - IS_LOCKED.with(|l| { - if !l.get() { - let guard = GLOBAL_LOCK.as_ref().unwrap().lock().unwrap(); - GLOBAL_GUARD.replace(guard); - l.set(true); - 1 - } else { - 0 - } - }) + <$t as $crate::Impl>::acquire() } - #[no_mangle] unsafe fn _critical_section_release(token: u8) { - if token == 1 { - GLOBAL_GUARD.take(); - IS_LOCKED.with(|l| { - l.set(false); - }); - } + <$t as $crate::Impl>::release(token) } - } else { - compile_error!("Critical section is not implemented for this target. Make sure you've specified the correct --target. You may need to supply a custom critical section implementation with the `custom-impl` feature"); - } + }; }