Skip to content

Commit

Permalink
Remove all built-in implementations.
Browse files Browse the repository at this point in the history
  • Loading branch information
Dirbaio committed May 6, 2022
1 parent 88ba991 commit a3cdfd8
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 219 deletions.
4 changes: 2 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -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",
}
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
27 changes: 2 additions & 25 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
[package]
name = "critical-section"
version = "0.2.7"
authors = ["Dario Nieuwenhuis <[email protected]>"]
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"
Expand All @@ -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
Expand Down
24 changes: 5 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand All @@ -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 {
Expand All @@ -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.

Expand Down
27 changes: 0 additions & 27 deletions avr-specs/avr-atmega328p.json

This file was deleted.

12 changes: 0 additions & 12 deletions rust-toolchain.toml

This file was deleted.

169 changes: 36 additions & 133 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -58,141 +55,47 @@ pub fn with<R>(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<Mutex<()>> = None;
static mut GLOBAL_GUARD: Option<MutexGuard<'static, ()>> = None;

std::thread_local!(static IS_LOCKED: Cell<bool> = 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");
}
};
}

0 comments on commit a3cdfd8

Please sign in to comment.