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

Improve docs. #18

Merged
merged 3 commits into from
Aug 11, 2022
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
1 change: 1 addition & 0 deletions .github/bors.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ required_approvals = 1
status = [
"clippy",
"rustfmt",
"test",
]
14 changes: 14 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: Test

on:
push:
branches: [ staging, trying, main ]
pull_request:

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Test
run: cargo test
106 changes: 100 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,78 @@ There's a wide range of possible implementations depending on the execution envi
Libraries often need to use critical sections, but there's no universal API for this in `core`. This leads
library authors to hardcode 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 impl is needed from the Rust target alone. For example, the `thumbv7em-none-eabi` target
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.

- It provides functions `acquire`, `release` and `with` that libraries can directly use.
- 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.
- 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 implementation so that all the crates in the dependency tree automatically use it.

## Usage

First, add a dependency on a crate providing a critical section implementation. Enable the `critical-section-*` Cargo feature if required by the crate.

```toml
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"]}
```

Then you can use `critical_section::with()`.

```rust
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);
});

# struct MyCriticalSection;
# critical_section::set_impl!(MyCriticalSection);
# unsafe impl critical_section::Impl for MyCriticalSection {
# unsafe fn acquire() -> () {}
# unsafe fn release(token: ()) {}
# }
```

## Usage in libraries

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`.

## Providing an implementation

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`:

```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:

```rust
// 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;
Expand All @@ -50,9 +111,42 @@ unsafe impl critical_section::Impl for MyCriticalSection {
}
```

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.
## Troubleshooting

### Undefined reference errors

If you get an error like these:

```not_rust
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`:

```rust,ignore
use the_cs_impl_crate as _;
```

### Duplicate symbol errors

If you get errors like these:

```not_rust
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.

## Why not generics?

Expand All @@ -63,7 +157,7 @@ code that needs acquiring the critical section generic over it. This has a few p
would be quite unergonomic.
- It's common to put `Mutex`es in `static` variables, and `static`s can't
be generic.
- The user can mix different critical section implementations in the same program,
- It would allow mixing different critical section implementations in the same program,
which would be unsound.

## License
Expand Down