diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2022-02-12 00:30:47 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-02-12 00:30:47 +0000 |
| commit | e728a32672d9cb108d73ba017a6a153dbc6c2d15 (patch) | |
| tree | 25cdf290d138929ebf0ff292fc6934e0a266400a | |
| parent | d708be7fe5e8fec8f2feea269fcb6964b6c73dcb (diff) | |
| parent | 1904906b363d2bbe32e95546f53201a3179dcb60 (diff) | |
Merge #613
613: Rust stable support r=Dirbaio a=Dirbaio
This PR adds (limited) stable Rust support!
The drawbacks are:
- No `#[embassy::task]`, `#[embassy::main]`. (requires `type_alias_impl_trait`). You have to manually allocate the tasks somewhere they'll live forever. See [example](https://github.com/embassy-rs/embassy/blob/master/examples/nrf/src/bin/raw_spawn.rs)
- No async trait impls (requires GATs). Note that the full API surface of HALs is still available through inherent methods: #552 #581
- Some stuff is not constructible in const (requires `const_fn_trait_bound`), although there's an (ugly) workaround for the generic `Mutex`.
So it's not that bad in the end, it's fully usable for shipping production-ready firmwares. We'll still recommend nightly as the default, until GATs and `type_alias_impl_trait` are stable.
Co-authored-by: Dario Nieuwenhuis <[email protected]>
46 files changed, 985 insertions, 845 deletions
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 767e42243..f1256320d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml | |||
| @@ -12,11 +12,11 @@ env: | |||
| 12 | jobs: | 12 | jobs: |
| 13 | all: | 13 | all: |
| 14 | runs-on: ubuntu-20.04 | 14 | runs-on: ubuntu-20.04 |
| 15 | needs: [build, test] | 15 | needs: [build-nightly, build-stable, test] |
| 16 | steps: | 16 | steps: |
| 17 | - name: Done | 17 | - name: Done |
| 18 | run: exit 0 | 18 | run: exit 0 |
| 19 | build: | 19 | build-nightly: |
| 20 | runs-on: ubuntu-latest | 20 | runs-on: ubuntu-latest |
| 21 | permissions: | 21 | permissions: |
| 22 | id-token: write | 22 | id-token: write |
| @@ -41,6 +41,28 @@ jobs: | |||
| 41 | chmod +x /usr/local/bin/cargo-batch | 41 | chmod +x /usr/local/bin/cargo-batch |
| 42 | ./ci.sh | 42 | ./ci.sh |
| 43 | rm -rf target_ci/*{,/release}/{build,deps,.fingerprint}/{lib,}{embassy,stm32}* | 43 | rm -rf target_ci/*{,/release}/{build,deps,.fingerprint}/{lib,}{embassy,stm32}* |
| 44 | build-stable: | ||
| 45 | runs-on: ubuntu-latest | ||
| 46 | steps: | ||
| 47 | - uses: actions/checkout@v2 | ||
| 48 | with: | ||
| 49 | submodules: true | ||
| 50 | - name: Cache multiple paths | ||
| 51 | uses: actions/cache@v2 | ||
| 52 | with: | ||
| 53 | path: | | ||
| 54 | ~/.cargo/bin/ | ||
| 55 | ~/.cargo/registry/index/ | ||
| 56 | ~/.cargo/registry/cache/ | ||
| 57 | ~/.cargo/git/db/ | ||
| 58 | target_ci_stable | ||
| 59 | key: rust-stable-${{ runner.os }}-${{ hashFiles('rust-toolchain.toml') }} | ||
| 60 | - name: build | ||
| 61 | run: | | ||
| 62 | curl -L -o /usr/local/bin/cargo-batch https://github.com/embassy-rs/cargo-batch/releases/download/batch-0.1.0/cargo-batch | ||
| 63 | chmod +x /usr/local/bin/cargo-batch | ||
| 64 | ./ci_stable.sh | ||
| 65 | rm -rf target_ci_stable/*{,/release}/{build,deps,.fingerprint}/{lib,}{embassy,stm32}* | ||
| 44 | 66 | ||
| 45 | test: | 67 | test: |
| 46 | runs-on: ubuntu-latest | 68 | runs-on: ubuntu-latest |
diff --git a/.gitignore b/.gitignore index 92f9a32b2..144dd703f 100644 --- a/.gitignore +++ b/.gitignore | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | target | 1 | target |
| 2 | target_ci | 2 | target_ci |
| 3 | target_ci_stable | ||
| 3 | Cargo.lock | 4 | Cargo.lock |
| 4 | third_party | 5 | third_party |
| 5 | /Cargo.toml | 6 | /Cargo.toml |
diff --git a/.vscode/settings.json b/.vscode/settings.json index 3defc9077..79433a7c9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json | |||
| @@ -4,16 +4,19 @@ | |||
| 4 | "rust-analyzer.assist.importGranularity": "module", | 4 | "rust-analyzer.assist.importGranularity": "module", |
| 5 | "rust-analyzer.checkOnSave.allFeatures": false, | 5 | "rust-analyzer.checkOnSave.allFeatures": false, |
| 6 | "rust-analyzer.checkOnSave.allTargets": false, | 6 | "rust-analyzer.checkOnSave.allTargets": false, |
| 7 | "rust-analyzer.checkOnSave.command": "clippy", | 7 | "rust-analyzer.checkOnSave.noDefaultFeatures": true, |
| 8 | "rust-analyzer.cargo.allFeatures": false, | ||
| 8 | "rust-analyzer.cargo.noDefaultFeatures": true, | 9 | "rust-analyzer.cargo.noDefaultFeatures": true, |
| 9 | "rust-analyzer.experimental.procAttrMacros": false, | 10 | "rust-analyzer.experimental.procAttrMacros": false, |
| 10 | "rust-analyzer.checkOnSave.noDefaultFeatures": true, | 11 | "rust-analyzer.procMacro.enable": true, |
| 12 | "rust-analyzer.cargo.runBuildScripts": true, | ||
| 11 | "rust-analyzer.cargo.target": "thumbv7em-none-eabi", | 13 | "rust-analyzer.cargo.target": "thumbv7em-none-eabi", |
| 12 | "rust-analyzer.cargo.features": [ | 14 | "rust-analyzer.cargo.features": [ |
| 13 | // These are needed to prevent embassy-net from failing to build | 15 | // These are needed to prevent embassy-net from failing to build |
| 14 | //"embassy-net/medium-ethernet", | 16 | //"embassy-net/medium-ethernet", |
| 15 | //"embassy-net/tcp", | 17 | //"embassy-net/tcp", |
| 16 | //"embassy-net/pool-16", | 18 | //"embassy-net/pool-16", |
| 19 | "nightly", | ||
| 17 | ], | 20 | ], |
| 18 | "rust-analyzer.linkedProjects": [ | 21 | "rust-analyzer.linkedProjects": [ |
| 19 | // Declare for the target you wish to develop | 22 | // Declare for the target you wish to develop |
| @@ -35,12 +38,4 @@ | |||
| 35 | // "examples/stm32wl55/Cargo.toml", | 38 | // "examples/stm32wl55/Cargo.toml", |
| 36 | // "examples/wasm/Cargo.toml", | 39 | // "examples/wasm/Cargo.toml", |
| 37 | ], | 40 | ], |
| 38 | "rust-analyzer.procMacro.enable": true, | ||
| 39 | "rust-analyzer.cargo.runBuildScripts": true, | ||
| 40 | "files.watcherExclude": { | ||
| 41 | "**/.git/objects/**": true, | ||
| 42 | "**/.git/subtree-cache/**": true, | ||
| 43 | "**/target/**": true | ||
| 44 | }, | ||
| 45 | "git.ignoreLimitWarning": true | ||
| 46 | } \ No newline at end of file | 41 | } \ No newline at end of file |
| @@ -24,24 +24,24 @@ rm -rf stm32-metapac | |||
| 24 | mv stm32-metapac-gen/out stm32-metapac | 24 | mv stm32-metapac-gen/out stm32-metapac |
| 25 | 25 | ||
| 26 | cargo batch \ | 26 | cargo batch \ |
| 27 | --- build --release --manifest-path embassy/Cargo.toml --target thumbv7em-none-eabi \ | 27 | --- build --release --manifest-path embassy/Cargo.toml --target thumbv7em-none-eabi --features nightly \ |
| 28 | --- build --release --manifest-path embassy/Cargo.toml --target thumbv7em-none-eabi --features log,executor-agnostic \ | 28 | --- build --release --manifest-path embassy/Cargo.toml --target thumbv7em-none-eabi --features nightly,log,executor-agnostic \ |
| 29 | --- build --release --manifest-path embassy/Cargo.toml --target thumbv7em-none-eabi --features defmt \ | 29 | --- build --release --manifest-path embassy/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \ |
| 30 | --- build --release --manifest-path embassy/Cargo.toml --target thumbv6m-none-eabi --features defmt \ | 30 | --- build --release --manifest-path embassy/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ |
| 31 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \ | 31 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \ |
| 32 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \ | 32 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \ |
| 33 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \ | 33 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \ |
| 34 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52820,gpiote,time-driver-rtc1 \ | 34 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52820,gpiote,time-driver-rtc1 \ |
| 35 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52832,gpiote,time-driver-rtc1 \ | 35 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52832,gpiote,time-driver-rtc1 \ |
| 36 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52833,gpiote,time-driver-rtc1,unstable-traits \ | 36 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52833,gpiote,time-driver-rtc1,unstable-traits \ |
| 37 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160-s,gpiote,time-driver-rtc1 \ | 37 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,nrf9160-s,gpiote,time-driver-rtc1 \ |
| 38 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160-ns,gpiote,time-driver-rtc1,unstable-traits \ | 38 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,nrf9160-ns,gpiote,time-driver-rtc1,unstable-traits \ |
| 39 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-s,gpiote,time-driver-rtc1,unstable-traits \ | 39 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,nrf5340-app-s,gpiote,time-driver-rtc1,unstable-traits \ |
| 40 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-ns,gpiote,time-driver-rtc1 \ | 40 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,nrf5340-app-ns,gpiote,time-driver-rtc1 \ |
| 41 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-net,gpiote,time-driver-rtc1,unstable-traits \ | 41 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,nrf5340-net,gpiote,time-driver-rtc1,unstable-traits \ |
| 42 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time-driver-rtc1 \ | 42 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52840,gpiote,time-driver-rtc1 \ |
| 43 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,log,gpiote,time-driver-rtc1 \ | 43 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52840,log,gpiote,time-driver-rtc1 \ |
| 44 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,defmt,gpiote,time-driver-rtc1,unstable-traits \ | 44 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52840,defmt,gpiote,time-driver-rtc1,unstable-traits \ |
| 45 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any \ | 45 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any \ |
| 46 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,unstable-traits \ | 46 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,unstable-traits \ |
| 47 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any \ | 47 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any \ |
diff --git a/ci_stable.sh b/ci_stable.sh new file mode 100755 index 000000000..c4ec30824 --- /dev/null +++ b/ci_stable.sh | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | |||
| 3 | set -euo pipefail | ||
| 4 | |||
| 5 | export CARGO_TARGET_DIR=$PWD/target_ci_stable | ||
| 6 | export RUSTFLAGS=-Dwarnings | ||
| 7 | export DEFMT_LOG=trace | ||
| 8 | |||
| 9 | sed -i 's/channel.*/channel = "stable"/g' rust-toolchain.toml | ||
| 10 | |||
| 11 | cargo batch \ | ||
| 12 | --- build --release --manifest-path embassy/Cargo.toml --target thumbv7em-none-eabi \ | ||
| 13 | --- build --release --manifest-path embassy/Cargo.toml --target thumbv7em-none-eabi --features log,executor-agnostic \ | ||
| 14 | --- build --release --manifest-path embassy/Cargo.toml --target thumbv7em-none-eabi --features defmt \ | ||
| 15 | --- build --release --manifest-path embassy/Cargo.toml --target thumbv6m-none-eabi --features defmt \ | ||
| 16 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \ | ||
| 17 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \ | ||
| 18 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \ | ||
| 19 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52820,gpiote,time-driver-rtc1 \ | ||
| 20 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52832,gpiote,time-driver-rtc1 \ | ||
| 21 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52833,gpiote,time-driver-rtc1,unstable-traits \ | ||
| 22 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160-s,gpiote,time-driver-rtc1 \ | ||
| 23 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160-ns,gpiote,time-driver-rtc1,unstable-traits \ | ||
| 24 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-s,gpiote,time-driver-rtc1,unstable-traits \ | ||
| 25 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-ns,gpiote,time-driver-rtc1 \ | ||
| 26 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-net,gpiote,time-driver-rtc1,unstable-traits \ | ||
| 27 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time-driver-rtc1 \ | ||
| 28 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,log,gpiote,time-driver-rtc1 \ | ||
| 29 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,defmt,gpiote,time-driver-rtc1,unstable-traits \ | ||
| 30 | --- build --release --manifest-path examples/nrf/Cargo.toml --target thumbv7em-none-eabi --no-default-features --out-dir out/examples/nrf --bin raw_spawn \ | ||
diff --git a/docs/modules/ROOT/examples/basic/Cargo.toml b/docs/modules/ROOT/examples/basic/Cargo.toml index 0f1c30da3..ed1c3cb1c 100644 --- a/docs/modules/ROOT/examples/basic/Cargo.toml +++ b/docs/modules/ROOT/examples/basic/Cargo.toml | |||
| @@ -5,8 +5,8 @@ name = "embassy-basic-example" | |||
| 5 | version = "0.1.0" | 5 | version = "0.1.0" |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy = { version = "0.1.0", path = "../../../../../embassy", features = ["defmt"] } | 8 | embassy = { version = "0.1.0", path = "../../../../../embassy", features = ["defmt", "nightly"] } |
| 9 | embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } | 9 | embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] } |
| 10 | 10 | ||
| 11 | defmt = "0.3" | 11 | defmt = "0.3" |
| 12 | defmt-rtt = "0.3" | 12 | defmt-rtt = "0.3" |
diff --git a/embassy-macros/src/chip/nrf.rs b/embassy-macros/src/chip/nrf.rs deleted file mode 100644 index 3ff6a74cf..000000000 --- a/embassy-macros/src/chip/nrf.rs +++ /dev/null | |||
| @@ -1,11 +0,0 @@ | |||
| 1 | use crate::path::ModulePrefix; | ||
| 2 | use proc_macro2::TokenStream; | ||
| 3 | use quote::quote; | ||
| 4 | |||
| 5 | pub fn generate(embassy_prefix: &ModulePrefix, config: syn::Expr) -> TokenStream { | ||
| 6 | let embassy_nrf_path = embassy_prefix.append("embassy_nrf").path(); | ||
| 7 | |||
| 8 | quote!( | ||
| 9 | let p = #embassy_nrf_path::init(#config); | ||
| 10 | ) | ||
| 11 | } | ||
diff --git a/embassy-macros/src/chip/rp.rs b/embassy-macros/src/chip/rp.rs deleted file mode 100644 index ba0a97ada..000000000 --- a/embassy-macros/src/chip/rp.rs +++ /dev/null | |||
| @@ -1,10 +0,0 @@ | |||
| 1 | use crate::path::ModulePrefix; | ||
| 2 | use proc_macro2::TokenStream; | ||
| 3 | use quote::quote; | ||
| 4 | |||
| 5 | pub fn generate(embassy_prefix: &ModulePrefix, config: syn::Expr) -> TokenStream { | ||
| 6 | let embassy_rp_path = embassy_prefix.append("embassy_rp").path(); | ||
| 7 | quote!( | ||
| 8 | let p = #embassy_rp_path::init(#config); | ||
| 9 | ) | ||
| 10 | } | ||
diff --git a/embassy-macros/src/chip/stm32.rs b/embassy-macros/src/chip/stm32.rs deleted file mode 100644 index c6938836c..000000000 --- a/embassy-macros/src/chip/stm32.rs +++ /dev/null | |||
| @@ -1,11 +0,0 @@ | |||
| 1 | use crate::path::ModulePrefix; | ||
| 2 | use proc_macro2::TokenStream; | ||
| 3 | use quote::quote; | ||
| 4 | |||
| 5 | pub fn generate(embassy_prefix: &ModulePrefix, config: syn::Expr) -> TokenStream { | ||
| 6 | let embassy_stm32_path = embassy_prefix.append("embassy_stm32").path(); | ||
| 7 | |||
| 8 | quote!( | ||
| 9 | let p = #embassy_stm32_path::init(#config); | ||
| 10 | ) | ||
| 11 | } | ||
diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs index 44a8d3b93..085f7889d 100644 --- a/embassy-macros/src/lib.rs +++ b/embassy-macros/src/lib.rs | |||
| @@ -1,228 +1,39 @@ | |||
| 1 | #![feature(proc_macro_diagnostic)] | ||
| 2 | |||
| 3 | extern crate proc_macro; | 1 | extern crate proc_macro; |
| 4 | 2 | ||
| 5 | use darling::FromMeta; | ||
| 6 | use proc_macro::TokenStream; | 3 | use proc_macro::TokenStream; |
| 7 | use proc_macro2::Span; | ||
| 8 | use quote::{format_ident, quote}; | ||
| 9 | use std::iter; | ||
| 10 | use syn::spanned::Spanned; | ||
| 11 | use syn::{parse, Type, Visibility}; | ||
| 12 | use syn::{ItemFn, ReturnType}; | ||
| 13 | |||
| 14 | mod path; | ||
| 15 | |||
| 16 | use path::ModulePrefix; | ||
| 17 | 4 | ||
| 18 | #[derive(Debug, FromMeta)] | 5 | mod macros; |
| 19 | struct TaskArgs { | 6 | mod util; |
| 20 | #[darling(default)] | 7 | use macros::*; |
| 21 | pool_size: Option<usize>, | ||
| 22 | #[darling(default)] | ||
| 23 | send: bool, | ||
| 24 | #[darling(default)] | ||
| 25 | embassy_prefix: ModulePrefix, | ||
| 26 | } | ||
| 27 | 8 | ||
| 28 | #[proc_macro_attribute] | 9 | #[proc_macro_attribute] |
| 29 | pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { | 10 | pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { |
| 30 | let macro_args = syn::parse_macro_input!(args as syn::AttributeArgs); | 11 | let args = syn::parse_macro_input!(args as syn::AttributeArgs); |
| 31 | let mut task_fn = syn::parse_macro_input!(item as syn::ItemFn); | 12 | let f = syn::parse_macro_input!(item as syn::ItemFn); |
| 32 | |||
| 33 | let macro_args = match TaskArgs::from_list(¯o_args) { | ||
| 34 | Ok(v) => v, | ||
| 35 | Err(e) => { | ||
| 36 | return TokenStream::from(e.write_errors()); | ||
| 37 | } | ||
| 38 | }; | ||
| 39 | |||
| 40 | let embassy_prefix = macro_args.embassy_prefix.append("embassy"); | ||
| 41 | let embassy_path = embassy_prefix.path(); | ||
| 42 | |||
| 43 | let pool_size: usize = macro_args.pool_size.unwrap_or(1); | ||
| 44 | |||
| 45 | let mut fail = false; | ||
| 46 | if task_fn.sig.asyncness.is_none() { | ||
| 47 | task_fn | ||
| 48 | .sig | ||
| 49 | .span() | ||
| 50 | .unwrap() | ||
| 51 | .error("task functions must be async") | ||
| 52 | .emit(); | ||
| 53 | fail = true; | ||
| 54 | } | ||
| 55 | if !task_fn.sig.generics.params.is_empty() { | ||
| 56 | task_fn | ||
| 57 | .sig | ||
| 58 | .span() | ||
| 59 | .unwrap() | ||
| 60 | .error("task functions must not be generic") | ||
| 61 | .emit(); | ||
| 62 | fail = true; | ||
| 63 | } | ||
| 64 | if pool_size < 1 { | ||
| 65 | return parse::Error::new(Span::call_site(), "pool_size must be 1 or greater") | ||
| 66 | .to_compile_error() | ||
| 67 | .into(); | ||
| 68 | } | ||
| 69 | |||
| 70 | let mut arg_names: syn::punctuated::Punctuated<syn::Ident, syn::Token![,]> = | ||
| 71 | syn::punctuated::Punctuated::new(); | ||
| 72 | let mut args = task_fn.sig.inputs.clone(); | ||
| 73 | |||
| 74 | for arg in args.iter_mut() { | ||
| 75 | match arg { | ||
| 76 | syn::FnArg::Receiver(_) => { | ||
| 77 | arg.span() | ||
| 78 | .unwrap() | ||
| 79 | .error("task functions must not have receiver arguments") | ||
| 80 | .emit(); | ||
| 81 | fail = true; | ||
| 82 | } | ||
| 83 | syn::FnArg::Typed(t) => match t.pat.as_mut() { | ||
| 84 | syn::Pat::Ident(i) => { | ||
| 85 | arg_names.push(i.ident.clone()); | ||
| 86 | i.mutability = None; | ||
| 87 | } | ||
| 88 | _ => { | ||
| 89 | arg.span() | ||
| 90 | .unwrap() | ||
| 91 | .error("pattern matching in task arguments is not yet supporteds") | ||
| 92 | .emit(); | ||
| 93 | fail = true; | ||
| 94 | } | ||
| 95 | }, | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | if fail { | ||
| 100 | return TokenStream::new(); | ||
| 101 | } | ||
| 102 | 13 | ||
| 103 | let name = task_fn.sig.ident.clone(); | 14 | task::run(args, f).unwrap_or_else(|x| x).into() |
| 104 | |||
| 105 | let visibility = &task_fn.vis; | ||
| 106 | task_fn.sig.ident = format_ident!("task"); | ||
| 107 | let impl_ty = if macro_args.send { | ||
| 108 | quote!(impl ::core::future::Future + Send + 'static) | ||
| 109 | } else { | ||
| 110 | quote!(impl ::core::future::Future + 'static) | ||
| 111 | }; | ||
| 112 | |||
| 113 | let attrs = &task_fn.attrs; | ||
| 114 | |||
| 115 | let result = quote! { | ||
| 116 | #(#attrs)* | ||
| 117 | #visibility fn #name(#args) -> #embassy_path::executor::SpawnToken<#impl_ty> { | ||
| 118 | use #embassy_path::executor::raw::TaskStorage; | ||
| 119 | #task_fn | ||
| 120 | type F = #impl_ty; | ||
| 121 | #[allow(clippy::declare_interior_mutable_const)] | ||
| 122 | const NEW_TASK: TaskStorage<F> = TaskStorage::new(); | ||
| 123 | static POOL: [TaskStorage<F>; #pool_size] = [NEW_TASK; #pool_size]; | ||
| 124 | unsafe { TaskStorage::spawn_pool(&POOL, move || task(#arg_names)) } | ||
| 125 | } | ||
| 126 | }; | ||
| 127 | result.into() | ||
| 128 | } | 15 | } |
| 129 | 16 | ||
| 130 | #[proc_macro_attribute] | 17 | #[proc_macro_attribute] |
| 131 | pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { | 18 | pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { |
| 132 | let mut f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function"); | 19 | let args = syn::parse_macro_input!(args as syn::AttributeArgs); |
| 133 | 20 | let f = syn::parse_macro_input!(item as syn::ItemFn); | |
| 134 | if !args.is_empty() { | 21 | main::run(args, f).unwrap_or_else(|x| x).into() |
| 135 | return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") | 22 | } |
| 136 | .to_compile_error() | ||
| 137 | .into(); | ||
| 138 | } | ||
| 139 | |||
| 140 | let fspan = f.span(); | ||
| 141 | let ident = f.sig.ident.clone(); | ||
| 142 | let ident_s = ident.to_string(); | ||
| 143 | |||
| 144 | // XXX should we blacklist other attributes? | ||
| 145 | |||
| 146 | let valid_signature = f.sig.constness.is_none() | ||
| 147 | && f.vis == Visibility::Inherited | ||
| 148 | && f.sig.abi.is_none() | ||
| 149 | && f.sig.inputs.is_empty() | ||
| 150 | && f.sig.generics.params.is_empty() | ||
| 151 | && f.sig.generics.where_clause.is_none() | ||
| 152 | && f.sig.variadic.is_none() | ||
| 153 | && match f.sig.output { | ||
| 154 | ReturnType::Default => true, | ||
| 155 | ReturnType::Type(_, ref ty) => match **ty { | ||
| 156 | Type::Tuple(ref tuple) => tuple.elems.is_empty(), | ||
| 157 | Type::Never(..) => true, | ||
| 158 | _ => false, | ||
| 159 | }, | ||
| 160 | }; | ||
| 161 | |||
| 162 | if !valid_signature { | ||
| 163 | return parse::Error::new( | ||
| 164 | fspan, | ||
| 165 | "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`", | ||
| 166 | ) | ||
| 167 | .to_compile_error() | ||
| 168 | .into(); | ||
| 169 | } | ||
| 170 | |||
| 171 | f.block.stmts = iter::once( | ||
| 172 | syn::parse2(quote! {{ | ||
| 173 | // Check that this interrupt actually exists | ||
| 174 | let __irq_exists_check: interrupt::#ident; | ||
| 175 | }}) | ||
| 176 | .unwrap(), | ||
| 177 | ) | ||
| 178 | .chain(f.block.stmts) | ||
| 179 | .collect(); | ||
| 180 | 23 | ||
| 181 | quote!( | 24 | #[proc_macro_attribute] |
| 182 | #[doc(hidden)] | 25 | pub fn interrupt(args: TokenStream, item: TokenStream) -> TokenStream { |
| 183 | #[export_name = #ident_s] | 26 | let args = syn::parse_macro_input!(args as syn::AttributeArgs); |
| 184 | #[allow(non_snake_case)] | 27 | let f = syn::parse_macro_input!(item as syn::ItemFn); |
| 185 | #f | 28 | interrupt::run(args, f).unwrap_or_else(|x| x).into() |
| 186 | ) | ||
| 187 | .into() | ||
| 188 | } | 29 | } |
| 189 | 30 | ||
| 190 | #[proc_macro] | 31 | #[proc_macro] |
| 191 | pub fn interrupt_declare(item: TokenStream) -> TokenStream { | 32 | pub fn interrupt_declare(item: TokenStream) -> TokenStream { |
| 192 | let name = syn::parse_macro_input!(item as syn::Ident); | 33 | let name = syn::parse_macro_input!(item as syn::Ident); |
| 193 | let name = format_ident!("{}", name); | 34 | interrupt_declare::run(name).unwrap_or_else(|x| x).into() |
| 194 | let name_interrupt = format_ident!("{}", name); | ||
| 195 | let name_handler = format!("__EMBASSY_{}_HANDLER", name); | ||
| 196 | |||
| 197 | let result = quote! { | ||
| 198 | #[allow(non_camel_case_types)] | ||
| 199 | pub struct #name_interrupt(()); | ||
| 200 | unsafe impl ::embassy::interrupt::Interrupt for #name_interrupt { | ||
| 201 | type Priority = crate::interrupt::Priority; | ||
| 202 | fn number(&self) -> u16 { | ||
| 203 | use cortex_m::interrupt::InterruptNumber; | ||
| 204 | let irq = InterruptEnum::#name; | ||
| 205 | irq.number() as u16 | ||
| 206 | } | ||
| 207 | unsafe fn steal() -> Self { | ||
| 208 | Self(()) | ||
| 209 | } | ||
| 210 | unsafe fn __handler(&self) -> &'static ::embassy::interrupt::Handler { | ||
| 211 | #[export_name = #name_handler] | ||
| 212 | static HANDLER: ::embassy::interrupt::Handler = ::embassy::interrupt::Handler::new(); | ||
| 213 | &HANDLER | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | unsafe impl ::embassy::util::Unborrow for #name_interrupt { | ||
| 218 | type Target = #name_interrupt; | ||
| 219 | unsafe fn unborrow(self) -> #name_interrupt { | ||
| 220 | self | ||
| 221 | } | ||
| 222 | } | ||
| 223 | }; | ||
| 224 | result.into() | ||
| 225 | } | 35 | } |
| 36 | |||
| 226 | /// # interrupt_take procedural macro | 37 | /// # interrupt_take procedural macro |
| 227 | /// | 38 | /// |
| 228 | /// core::panic! is used as a default way to panic in this macro as there is no sensible way of enabling/disabling defmt for macro generation. | 39 | /// core::panic! is used as a default way to panic in this macro as there is no sensible way of enabling/disabling defmt for macro generation. |
| @@ -231,312 +42,5 @@ pub fn interrupt_declare(item: TokenStream) -> TokenStream { | |||
| 231 | #[proc_macro] | 42 | #[proc_macro] |
| 232 | pub fn interrupt_take(item: TokenStream) -> TokenStream { | 43 | pub fn interrupt_take(item: TokenStream) -> TokenStream { |
| 233 | let name = syn::parse_macro_input!(item as syn::Ident); | 44 | let name = syn::parse_macro_input!(item as syn::Ident); |
| 234 | let name = format!("{}", name); | 45 | interrupt_take::run(name).unwrap_or_else(|x| x).into() |
| 235 | let name_interrupt = format_ident!("{}", name); | ||
| 236 | let name_handler = format!("__EMBASSY_{}_HANDLER", name); | ||
| 237 | |||
| 238 | let result = quote! { | ||
| 239 | { | ||
| 240 | #[allow(non_snake_case)] | ||
| 241 | #[export_name = #name] | ||
| 242 | pub unsafe extern "C" fn trampoline() { | ||
| 243 | extern "C" { | ||
| 244 | #[link_name = #name_handler] | ||
| 245 | static HANDLER: ::embassy::interrupt::Handler; | ||
| 246 | } | ||
| 247 | |||
| 248 | let func = HANDLER.func.load(::embassy::export::atomic::Ordering::Relaxed); | ||
| 249 | let ctx = HANDLER.ctx.load(::embassy::export::atomic::Ordering::Relaxed); | ||
| 250 | let func: fn(*mut ()) = ::core::mem::transmute(func); | ||
| 251 | func(ctx) | ||
| 252 | } | ||
| 253 | |||
| 254 | static TAKEN: ::embassy::export::atomic::AtomicBool = ::embassy::export::atomic::AtomicBool::new(false); | ||
| 255 | |||
| 256 | if TAKEN.compare_exchange(false, true, ::embassy::export::atomic::Ordering::AcqRel, ::embassy::export::atomic::Ordering::Acquire).is_err() { | ||
| 257 | core::panic!("IRQ Already taken"); | ||
| 258 | } | ||
| 259 | |||
| 260 | let irq: interrupt::#name_interrupt = unsafe { ::core::mem::transmute(()) }; | ||
| 261 | irq | ||
| 262 | } | ||
| 263 | }; | ||
| 264 | result.into() | ||
| 265 | } | ||
| 266 | |||
| 267 | #[cfg(feature = "stm32")] | ||
| 268 | #[path = "chip/stm32.rs"] | ||
| 269 | mod chip; | ||
| 270 | |||
| 271 | #[cfg(feature = "nrf")] | ||
| 272 | #[path = "chip/nrf.rs"] | ||
| 273 | mod chip; | ||
| 274 | |||
| 275 | #[cfg(feature = "rp")] | ||
| 276 | #[path = "chip/rp.rs"] | ||
| 277 | mod chip; | ||
| 278 | |||
| 279 | #[cfg(any( | ||
| 280 | feature = "nrf", | ||
| 281 | feature = "rp", | ||
| 282 | feature = "stm32", | ||
| 283 | feature = "wasm", | ||
| 284 | feature = "std" | ||
| 285 | ))] | ||
| 286 | #[derive(Debug, FromMeta)] | ||
| 287 | struct MainArgs { | ||
| 288 | #[darling(default)] | ||
| 289 | embassy_prefix: ModulePrefix, | ||
| 290 | |||
| 291 | #[allow(unused)] | ||
| 292 | #[darling(default)] | ||
| 293 | config: Option<syn::LitStr>, | ||
| 294 | } | ||
| 295 | |||
| 296 | #[cfg(any(feature = "nrf", feature = "rp", feature = "stm32"))] | ||
| 297 | #[proc_macro_attribute] | ||
| 298 | pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { | ||
| 299 | let macro_args = syn::parse_macro_input!(args as syn::AttributeArgs); | ||
| 300 | let task_fn = syn::parse_macro_input!(item as syn::ItemFn); | ||
| 301 | |||
| 302 | let macro_args = match MainArgs::from_list(¯o_args) { | ||
| 303 | Ok(v) => v, | ||
| 304 | Err(e) => { | ||
| 305 | return TokenStream::from(e.write_errors()); | ||
| 306 | } | ||
| 307 | }; | ||
| 308 | |||
| 309 | let mut fail = false; | ||
| 310 | if task_fn.sig.asyncness.is_none() { | ||
| 311 | task_fn | ||
| 312 | .sig | ||
| 313 | .span() | ||
| 314 | .unwrap() | ||
| 315 | .error("task functions must be async") | ||
| 316 | .emit(); | ||
| 317 | fail = true; | ||
| 318 | } | ||
| 319 | if !task_fn.sig.generics.params.is_empty() { | ||
| 320 | task_fn | ||
| 321 | .sig | ||
| 322 | .span() | ||
| 323 | .unwrap() | ||
| 324 | .error("main function must not be generic") | ||
| 325 | .emit(); | ||
| 326 | fail = true; | ||
| 327 | } | ||
| 328 | |||
| 329 | let args = task_fn.sig.inputs.clone(); | ||
| 330 | |||
| 331 | if args.len() != 2 { | ||
| 332 | task_fn | ||
| 333 | .sig | ||
| 334 | .span() | ||
| 335 | .unwrap() | ||
| 336 | .error("main function must have 2 arguments") | ||
| 337 | .emit(); | ||
| 338 | fail = true; | ||
| 339 | } | ||
| 340 | |||
| 341 | if fail { | ||
| 342 | return TokenStream::new(); | ||
| 343 | } | ||
| 344 | |||
| 345 | let embassy_prefix = macro_args.embassy_prefix; | ||
| 346 | let embassy_prefix_lit = embassy_prefix.literal(); | ||
| 347 | let embassy_path = embassy_prefix.append("embassy").path(); | ||
| 348 | let task_fn_body = task_fn.block; | ||
| 349 | |||
| 350 | let config = macro_args | ||
| 351 | .config | ||
| 352 | .map(|s| s.parse::<syn::Expr>().unwrap()) | ||
| 353 | .unwrap_or_else(|| { | ||
| 354 | syn::Expr::Verbatim(quote! { | ||
| 355 | Default::default() | ||
| 356 | }) | ||
| 357 | }); | ||
| 358 | |||
| 359 | let chip_setup = chip::generate(&embassy_prefix, config); | ||
| 360 | |||
| 361 | let result = quote! { | ||
| 362 | #[#embassy_path::task(embassy_prefix = #embassy_prefix_lit)] | ||
| 363 | async fn __embassy_main(#args) { | ||
| 364 | #task_fn_body | ||
| 365 | } | ||
| 366 | |||
| 367 | #[cortex_m_rt::entry] | ||
| 368 | fn main() -> ! { | ||
| 369 | unsafe fn make_static<T>(t: &mut T) -> &'static mut T { | ||
| 370 | ::core::mem::transmute(t) | ||
| 371 | } | ||
| 372 | |||
| 373 | #chip_setup | ||
| 374 | |||
| 375 | let mut executor = #embassy_path::executor::Executor::new(); | ||
| 376 | let executor = unsafe { make_static(&mut executor) }; | ||
| 377 | |||
| 378 | executor.run(|spawner| { | ||
| 379 | spawner.must_spawn(__embassy_main(spawner, p)); | ||
| 380 | }) | ||
| 381 | } | ||
| 382 | }; | ||
| 383 | result.into() | ||
| 384 | } | ||
| 385 | |||
| 386 | #[cfg(feature = "std")] | ||
| 387 | #[proc_macro_attribute] | ||
| 388 | pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { | ||
| 389 | let macro_args = syn::parse_macro_input!(args as syn::AttributeArgs); | ||
| 390 | let task_fn = syn::parse_macro_input!(item as syn::ItemFn); | ||
| 391 | |||
| 392 | let macro_args = match MainArgs::from_list(¯o_args) { | ||
| 393 | Ok(v) => v, | ||
| 394 | Err(e) => { | ||
| 395 | return TokenStream::from(e.write_errors()); | ||
| 396 | } | ||
| 397 | }; | ||
| 398 | |||
| 399 | let embassy_path = macro_args.embassy_prefix.append("embassy"); | ||
| 400 | |||
| 401 | let mut fail = false; | ||
| 402 | if task_fn.sig.asyncness.is_none() { | ||
| 403 | task_fn | ||
| 404 | .sig | ||
| 405 | .span() | ||
| 406 | .unwrap() | ||
| 407 | .error("task functions must be async") | ||
| 408 | .emit(); | ||
| 409 | fail = true; | ||
| 410 | } | ||
| 411 | if !task_fn.sig.generics.params.is_empty() { | ||
| 412 | task_fn | ||
| 413 | .sig | ||
| 414 | .span() | ||
| 415 | .unwrap() | ||
| 416 | .error("main function must not be generic") | ||
| 417 | .emit(); | ||
| 418 | fail = true; | ||
| 419 | } | ||
| 420 | |||
| 421 | let args = task_fn.sig.inputs.clone(); | ||
| 422 | |||
| 423 | if args.len() != 1 { | ||
| 424 | task_fn | ||
| 425 | .sig | ||
| 426 | .span() | ||
| 427 | .unwrap() | ||
| 428 | .error("main function must have one argument") | ||
| 429 | .emit(); | ||
| 430 | fail = true; | ||
| 431 | } | ||
| 432 | |||
| 433 | if fail { | ||
| 434 | return TokenStream::new(); | ||
| 435 | } | ||
| 436 | |||
| 437 | let task_fn_body = task_fn.block.clone(); | ||
| 438 | |||
| 439 | let embassy_path = embassy_path.path(); | ||
| 440 | let embassy_prefix_lit = macro_args.embassy_prefix.literal(); | ||
| 441 | |||
| 442 | let result = quote! { | ||
| 443 | #[#embassy_path::task(embassy_prefix = #embassy_prefix_lit)] | ||
| 444 | async fn __embassy_main(#args) { | ||
| 445 | #task_fn_body | ||
| 446 | } | ||
| 447 | |||
| 448 | fn main() -> ! { | ||
| 449 | unsafe fn make_static<T>(t: &mut T) -> &'static mut T { | ||
| 450 | ::core::mem::transmute(t) | ||
| 451 | } | ||
| 452 | |||
| 453 | let mut executor = #embassy_path::executor::Executor::new(); | ||
| 454 | let executor = unsafe { make_static(&mut executor) }; | ||
| 455 | |||
| 456 | executor.run(|spawner| { | ||
| 457 | spawner.spawn(__embassy_main(spawner)).unwrap(); | ||
| 458 | }) | ||
| 459 | |||
| 460 | } | ||
| 461 | }; | ||
| 462 | result.into() | ||
| 463 | } | ||
| 464 | |||
| 465 | #[cfg(feature = "wasm")] | ||
| 466 | #[proc_macro_attribute] | ||
| 467 | pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { | ||
| 468 | let macro_args = syn::parse_macro_input!(args as syn::AttributeArgs); | ||
| 469 | let task_fn = syn::parse_macro_input!(item as syn::ItemFn); | ||
| 470 | |||
| 471 | let macro_args = match MainArgs::from_list(¯o_args) { | ||
| 472 | Ok(v) => v, | ||
| 473 | Err(e) => { | ||
| 474 | return TokenStream::from(e.write_errors()); | ||
| 475 | } | ||
| 476 | }; | ||
| 477 | |||
| 478 | let embassy_path = macro_args.embassy_prefix.append("embassy"); | ||
| 479 | |||
| 480 | let mut fail = false; | ||
| 481 | if task_fn.sig.asyncness.is_none() { | ||
| 482 | task_fn | ||
| 483 | .sig | ||
| 484 | .span() | ||
| 485 | .unwrap() | ||
| 486 | .error("task functions must be async") | ||
| 487 | .emit(); | ||
| 488 | fail = true; | ||
| 489 | } | ||
| 490 | if !task_fn.sig.generics.params.is_empty() { | ||
| 491 | task_fn | ||
| 492 | .sig | ||
| 493 | .span() | ||
| 494 | .unwrap() | ||
| 495 | .error("main function must not be generic") | ||
| 496 | .emit(); | ||
| 497 | fail = true; | ||
| 498 | } | ||
| 499 | |||
| 500 | let args = task_fn.sig.inputs.clone(); | ||
| 501 | |||
| 502 | if args.len() != 1 { | ||
| 503 | task_fn | ||
| 504 | .sig | ||
| 505 | .span() | ||
| 506 | .unwrap() | ||
| 507 | .error("main function must have one argument") | ||
| 508 | .emit(); | ||
| 509 | fail = true; | ||
| 510 | } | ||
| 511 | |||
| 512 | if fail { | ||
| 513 | return TokenStream::new(); | ||
| 514 | } | ||
| 515 | |||
| 516 | let task_fn_body = task_fn.block.clone(); | ||
| 517 | |||
| 518 | let embassy_path = embassy_path.path(); | ||
| 519 | let embassy_prefix_lit = macro_args.embassy_prefix.literal(); | ||
| 520 | |||
| 521 | let result = quote! { | ||
| 522 | #[#embassy_path::task(embassy_prefix = #embassy_prefix_lit)] | ||
| 523 | async fn __embassy_main(#args) { | ||
| 524 | #task_fn_body | ||
| 525 | } | ||
| 526 | |||
| 527 | use wasm_bindgen::prelude::*; | ||
| 528 | |||
| 529 | #[wasm_bindgen(start)] | ||
| 530 | pub fn main() -> Result<(), JsValue> { | ||
| 531 | static EXECUTOR: #embassy_path::util::Forever<#embassy_path::executor::Executor> = #embassy_path::util::Forever::new(); | ||
| 532 | let executor = EXECUTOR.put(#embassy_path::executor::Executor::new()); | ||
| 533 | |||
| 534 | executor.start(|spawner| { | ||
| 535 | spawner.spawn(__embassy_main(spawner)).unwrap(); | ||
| 536 | }); | ||
| 537 | |||
| 538 | Ok(()) | ||
| 539 | } | ||
| 540 | }; | ||
| 541 | result.into() | ||
| 542 | } | 46 | } |
diff --git a/embassy-macros/src/macros/interrupt.rs b/embassy-macros/src/macros/interrupt.rs new file mode 100644 index 000000000..32cc0e010 --- /dev/null +++ b/embassy-macros/src/macros/interrupt.rs | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | use darling::FromMeta; | ||
| 2 | use proc_macro2::TokenStream; | ||
| 3 | use quote::quote; | ||
| 4 | use std::iter; | ||
| 5 | use syn::ReturnType; | ||
| 6 | use syn::{Type, Visibility}; | ||
| 7 | |||
| 8 | use crate::util::ctxt::Ctxt; | ||
| 9 | |||
| 10 | #[derive(Debug, FromMeta)] | ||
| 11 | struct Args {} | ||
| 12 | |||
| 13 | pub fn run(args: syn::AttributeArgs, mut f: syn::ItemFn) -> Result<TokenStream, TokenStream> { | ||
| 14 | let _args = Args::from_list(&args).map_err(|e| e.write_errors())?; | ||
| 15 | |||
| 16 | let ident = f.sig.ident.clone(); | ||
| 17 | let ident_s = ident.to_string(); | ||
| 18 | |||
| 19 | // XXX should we blacklist other attributes? | ||
| 20 | |||
| 21 | let valid_signature = f.sig.constness.is_none() | ||
| 22 | && f.vis == Visibility::Inherited | ||
| 23 | && f.sig.abi.is_none() | ||
| 24 | && f.sig.inputs.is_empty() | ||
| 25 | && f.sig.generics.params.is_empty() | ||
| 26 | && f.sig.generics.where_clause.is_none() | ||
| 27 | && f.sig.variadic.is_none() | ||
| 28 | && match f.sig.output { | ||
| 29 | ReturnType::Default => true, | ||
| 30 | ReturnType::Type(_, ref ty) => match **ty { | ||
| 31 | Type::Tuple(ref tuple) => tuple.elems.is_empty(), | ||
| 32 | Type::Never(..) => true, | ||
| 33 | _ => false, | ||
| 34 | }, | ||
| 35 | }; | ||
| 36 | |||
| 37 | let ctxt = Ctxt::new(); | ||
| 38 | |||
| 39 | if !valid_signature { | ||
| 40 | ctxt.error_spanned_by( | ||
| 41 | &f.sig, | ||
| 42 | "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`", | ||
| 43 | ); | ||
| 44 | } | ||
| 45 | |||
| 46 | ctxt.check()?; | ||
| 47 | |||
| 48 | f.block.stmts = iter::once( | ||
| 49 | syn::parse2(quote! {{ | ||
| 50 | // Check that this interrupt actually exists | ||
| 51 | let __irq_exists_check: interrupt::#ident; | ||
| 52 | }}) | ||
| 53 | .unwrap(), | ||
| 54 | ) | ||
| 55 | .chain(f.block.stmts) | ||
| 56 | .collect(); | ||
| 57 | |||
| 58 | let result = quote!( | ||
| 59 | #[doc(hidden)] | ||
| 60 | #[export_name = #ident_s] | ||
| 61 | #[allow(non_snake_case)] | ||
| 62 | #f | ||
| 63 | ); | ||
| 64 | |||
| 65 | Ok(result) | ||
| 66 | } | ||
diff --git a/embassy-macros/src/macros/interrupt_declare.rs b/embassy-macros/src/macros/interrupt_declare.rs new file mode 100644 index 000000000..0059936d9 --- /dev/null +++ b/embassy-macros/src/macros/interrupt_declare.rs | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | use proc_macro2::TokenStream; | ||
| 2 | use quote::{format_ident, quote}; | ||
| 3 | |||
| 4 | pub fn run(name: syn::Ident) -> Result<TokenStream, TokenStream> { | ||
| 5 | let name = format_ident!("{}", name); | ||
| 6 | let name_interrupt = format_ident!("{}", name); | ||
| 7 | let name_handler = format!("__EMBASSY_{}_HANDLER", name); | ||
| 8 | |||
| 9 | let result = quote! { | ||
| 10 | #[allow(non_camel_case_types)] | ||
| 11 | pub struct #name_interrupt(()); | ||
| 12 | unsafe impl ::embassy::interrupt::Interrupt for #name_interrupt { | ||
| 13 | type Priority = crate::interrupt::Priority; | ||
| 14 | fn number(&self) -> u16 { | ||
| 15 | use cortex_m::interrupt::InterruptNumber; | ||
| 16 | let irq = InterruptEnum::#name; | ||
| 17 | irq.number() as u16 | ||
| 18 | } | ||
| 19 | unsafe fn steal() -> Self { | ||
| 20 | Self(()) | ||
| 21 | } | ||
| 22 | unsafe fn __handler(&self) -> &'static ::embassy::interrupt::Handler { | ||
| 23 | #[export_name = #name_handler] | ||
| 24 | static HANDLER: ::embassy::interrupt::Handler = ::embassy::interrupt::Handler::new(); | ||
| 25 | &HANDLER | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | unsafe impl ::embassy::util::Unborrow for #name_interrupt { | ||
| 30 | type Target = #name_interrupt; | ||
| 31 | unsafe fn unborrow(self) -> #name_interrupt { | ||
| 32 | self | ||
| 33 | } | ||
| 34 | } | ||
| 35 | }; | ||
| 36 | Ok(result) | ||
| 37 | } | ||
diff --git a/embassy-macros/src/macros/interrupt_take.rs b/embassy-macros/src/macros/interrupt_take.rs new file mode 100644 index 000000000..230b9c741 --- /dev/null +++ b/embassy-macros/src/macros/interrupt_take.rs | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | use proc_macro2::TokenStream; | ||
| 2 | use quote::{format_ident, quote}; | ||
| 3 | |||
| 4 | pub fn run(name: syn::Ident) -> Result<TokenStream, TokenStream> { | ||
| 5 | let name = format!("{}", name); | ||
| 6 | let name_interrupt = format_ident!("{}", name); | ||
| 7 | let name_handler = format!("__EMBASSY_{}_HANDLER", name); | ||
| 8 | |||
| 9 | let result = quote! { | ||
| 10 | { | ||
| 11 | #[allow(non_snake_case)] | ||
| 12 | #[export_name = #name] | ||
| 13 | pub unsafe extern "C" fn trampoline() { | ||
| 14 | extern "C" { | ||
| 15 | #[link_name = #name_handler] | ||
| 16 | static HANDLER: ::embassy::interrupt::Handler; | ||
| 17 | } | ||
| 18 | |||
| 19 | let func = HANDLER.func.load(::embassy::export::atomic::Ordering::Relaxed); | ||
| 20 | let ctx = HANDLER.ctx.load(::embassy::export::atomic::Ordering::Relaxed); | ||
| 21 | let func: fn(*mut ()) = ::core::mem::transmute(func); | ||
| 22 | func(ctx) | ||
| 23 | } | ||
| 24 | |||
| 25 | static TAKEN: ::embassy::export::atomic::AtomicBool = ::embassy::export::atomic::AtomicBool::new(false); | ||
| 26 | |||
| 27 | if TAKEN.compare_exchange(false, true, ::embassy::export::atomic::Ordering::AcqRel, ::embassy::export::atomic::Ordering::Acquire).is_err() { | ||
| 28 | core::panic!("IRQ Already taken"); | ||
| 29 | } | ||
| 30 | |||
| 31 | let irq: interrupt::#name_interrupt = unsafe { ::core::mem::transmute(()) }; | ||
| 32 | irq | ||
| 33 | } | ||
| 34 | }; | ||
| 35 | Ok(result) | ||
| 36 | } | ||
diff --git a/embassy-macros/src/macros/main.rs b/embassy-macros/src/macros/main.rs new file mode 100644 index 000000000..01e302921 --- /dev/null +++ b/embassy-macros/src/macros/main.rs | |||
| @@ -0,0 +1,135 @@ | |||
| 1 | use darling::FromMeta; | ||
| 2 | use proc_macro2::TokenStream; | ||
| 3 | use quote::quote; | ||
| 4 | |||
| 5 | use crate::util::ctxt::Ctxt; | ||
| 6 | use crate::util::path::ModulePrefix; | ||
| 7 | |||
| 8 | #[cfg(feature = "stm32")] | ||
| 9 | const HAL: Option<&str> = Some("embassy_stm32"); | ||
| 10 | #[cfg(feature = "nrf")] | ||
| 11 | const HAL: Option<&str> = Some("embassy_nrf"); | ||
| 12 | #[cfg(feature = "rp")] | ||
| 13 | const HAL: Option<&str> = Some("embassy_rp"); | ||
| 14 | #[cfg(not(any(feature = "stm32", feature = "nrf", feature = "rp")))] | ||
| 15 | const HAL: Option<&str> = None; | ||
| 16 | |||
| 17 | #[derive(Debug, FromMeta)] | ||
| 18 | struct Args { | ||
| 19 | #[darling(default)] | ||
| 20 | embassy_prefix: ModulePrefix, | ||
| 21 | |||
| 22 | #[allow(unused)] | ||
| 23 | #[darling(default)] | ||
| 24 | config: Option<syn::LitStr>, | ||
| 25 | } | ||
| 26 | |||
| 27 | pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result<TokenStream, TokenStream> { | ||
| 28 | let args = Args::from_list(&args).map_err(|e| e.write_errors())?; | ||
| 29 | |||
| 30 | let fargs = f.sig.inputs.clone(); | ||
| 31 | |||
| 32 | let ctxt = Ctxt::new(); | ||
| 33 | |||
| 34 | if f.sig.asyncness.is_none() { | ||
| 35 | ctxt.error_spanned_by(&f.sig, "task functions must be async"); | ||
| 36 | } | ||
| 37 | if !f.sig.generics.params.is_empty() { | ||
| 38 | ctxt.error_spanned_by(&f.sig, "task functions must not be generic"); | ||
| 39 | } | ||
| 40 | |||
| 41 | if HAL.is_some() && fargs.len() != 2 { | ||
| 42 | ctxt.error_spanned_by(&f.sig, "main function must have 2 arguments"); | ||
| 43 | } | ||
| 44 | if HAL.is_none() && fargs.len() != 1 { | ||
| 45 | ctxt.error_spanned_by(&f.sig, "main function must have 2 arguments"); | ||
| 46 | } | ||
| 47 | |||
| 48 | ctxt.check()?; | ||
| 49 | |||
| 50 | let embassy_prefix = args.embassy_prefix; | ||
| 51 | let embassy_prefix_lit = embassy_prefix.literal(); | ||
| 52 | let embassy_path = embassy_prefix.append("embassy").path(); | ||
| 53 | let f_body = f.block; | ||
| 54 | |||
| 55 | #[cfg(feature = "wasm")] | ||
| 56 | let main = quote! { | ||
| 57 | #[wasm_bindgen::prelude::wasm_bindgen(start)] | ||
| 58 | pub fn main() -> Result<(), wasm_bindgen::JsValue> { | ||
| 59 | static EXECUTOR: #embassy_path::util::Forever<#embassy_path::executor::Executor> = #embassy_path::util::Forever::new(); | ||
| 60 | let executor = EXECUTOR.put(#embassy_path::executor::Executor::new()); | ||
| 61 | |||
| 62 | executor.start(|spawner| { | ||
| 63 | spawner.spawn(__embassy_main(spawner)).unwrap(); | ||
| 64 | }); | ||
| 65 | |||
| 66 | Ok(()) | ||
| 67 | } | ||
| 68 | }; | ||
| 69 | |||
| 70 | #[cfg(all(feature = "std", not(feature = "wasm")))] | ||
| 71 | let main = quote! { | ||
| 72 | fn main() -> ! { | ||
| 73 | let mut executor = #embassy_path::executor::Executor::new(); | ||
| 74 | let executor = unsafe { __make_static(&mut executor) }; | ||
| 75 | |||
| 76 | executor.run(|spawner| { | ||
| 77 | spawner.must_spawn(__embassy_main(spawner)); | ||
| 78 | }) | ||
| 79 | } | ||
| 80 | }; | ||
| 81 | |||
| 82 | #[cfg(all(not(feature = "std"), not(feature = "wasm")))] | ||
| 83 | let main = { | ||
| 84 | let config = args | ||
| 85 | .config | ||
| 86 | .map(|s| s.parse::<syn::Expr>().unwrap()) | ||
| 87 | .unwrap_or_else(|| { | ||
| 88 | syn::Expr::Verbatim(quote! { | ||
| 89 | Default::default() | ||
| 90 | }) | ||
| 91 | }); | ||
| 92 | |||
| 93 | let (hal_setup, peris_arg) = match HAL { | ||
| 94 | Some(hal) => { | ||
| 95 | let embassy_hal_path = embassy_prefix.append(hal).path(); | ||
| 96 | ( | ||
| 97 | quote!( | ||
| 98 | let p = #embassy_hal_path::init(#config); | ||
| 99 | ), | ||
| 100 | quote!(p), | ||
| 101 | ) | ||
| 102 | } | ||
| 103 | None => (quote!(), quote!()), | ||
| 104 | }; | ||
| 105 | |||
| 106 | quote! { | ||
| 107 | #[cortex_m_rt::entry] | ||
| 108 | fn main() -> ! { | ||
| 109 | #hal_setup | ||
| 110 | |||
| 111 | let mut executor = #embassy_path::executor::Executor::new(); | ||
| 112 | let executor = unsafe { __make_static(&mut executor) }; | ||
| 113 | |||
| 114 | executor.run(|spawner| { | ||
| 115 | spawner.must_spawn(__embassy_main(spawner, #peris_arg)); | ||
| 116 | }) | ||
| 117 | } | ||
| 118 | } | ||
| 119 | }; | ||
| 120 | |||
| 121 | let result = quote! { | ||
| 122 | #[#embassy_path::task(embassy_prefix = #embassy_prefix_lit)] | ||
| 123 | async fn __embassy_main(#fargs) { | ||
| 124 | #f_body | ||
| 125 | } | ||
| 126 | |||
| 127 | unsafe fn __make_static<T>(t: &mut T) -> &'static mut T { | ||
| 128 | ::core::mem::transmute(t) | ||
| 129 | } | ||
| 130 | |||
| 131 | #main | ||
| 132 | }; | ||
| 133 | |||
| 134 | Ok(result) | ||
| 135 | } | ||
diff --git a/embassy-macros/src/macros/mod.rs b/embassy-macros/src/macros/mod.rs new file mode 100644 index 000000000..4350f229f --- /dev/null +++ b/embassy-macros/src/macros/mod.rs | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | pub mod interrupt; | ||
| 2 | pub mod interrupt_declare; | ||
| 3 | pub mod interrupt_take; | ||
| 4 | pub mod main; | ||
| 5 | pub mod task; | ||
diff --git a/embassy-macros/src/macros/task.rs b/embassy-macros/src/macros/task.rs new file mode 100644 index 000000000..f0c78c596 --- /dev/null +++ b/embassy-macros/src/macros/task.rs | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | use darling::FromMeta; | ||
| 2 | use proc_macro2::TokenStream; | ||
| 3 | use quote::{format_ident, quote}; | ||
| 4 | |||
| 5 | use crate::util::ctxt::Ctxt; | ||
| 6 | use crate::util::path::ModulePrefix; | ||
| 7 | |||
| 8 | #[derive(Debug, FromMeta)] | ||
| 9 | struct Args { | ||
| 10 | #[darling(default)] | ||
| 11 | pool_size: Option<usize>, | ||
| 12 | #[darling(default)] | ||
| 13 | send: bool, | ||
| 14 | #[darling(default)] | ||
| 15 | embassy_prefix: ModulePrefix, | ||
| 16 | } | ||
| 17 | |||
| 18 | pub fn run(args: syn::AttributeArgs, mut f: syn::ItemFn) -> Result<TokenStream, TokenStream> { | ||
| 19 | let args = Args::from_list(&args).map_err(|e| e.write_errors())?; | ||
| 20 | |||
| 21 | let embassy_prefix = args.embassy_prefix.append("embassy"); | ||
| 22 | let embassy_path = embassy_prefix.path(); | ||
| 23 | |||
| 24 | let pool_size: usize = args.pool_size.unwrap_or(1); | ||
| 25 | |||
| 26 | let ctxt = Ctxt::new(); | ||
| 27 | |||
| 28 | if f.sig.asyncness.is_none() { | ||
| 29 | ctxt.error_spanned_by(&f.sig, "task functions must be async"); | ||
| 30 | } | ||
| 31 | if !f.sig.generics.params.is_empty() { | ||
| 32 | ctxt.error_spanned_by(&f.sig, "task functions must not be generic"); | ||
| 33 | } | ||
| 34 | if pool_size < 1 { | ||
| 35 | ctxt.error_spanned_by(&f.sig, "pool_size must be 1 or greater"); | ||
| 36 | } | ||
| 37 | |||
| 38 | let mut arg_names: syn::punctuated::Punctuated<syn::Ident, syn::Token![,]> = | ||
| 39 | syn::punctuated::Punctuated::new(); | ||
| 40 | let mut fargs = f.sig.inputs.clone(); | ||
| 41 | |||
| 42 | for arg in fargs.iter_mut() { | ||
| 43 | match arg { | ||
| 44 | syn::FnArg::Receiver(_) => { | ||
| 45 | ctxt.error_spanned_by(arg, "task functions must not have receiver arguments"); | ||
| 46 | } | ||
| 47 | syn::FnArg::Typed(t) => match t.pat.as_mut() { | ||
| 48 | syn::Pat::Ident(i) => { | ||
| 49 | arg_names.push(i.ident.clone()); | ||
| 50 | i.mutability = None; | ||
| 51 | } | ||
| 52 | _ => { | ||
| 53 | ctxt.error_spanned_by( | ||
| 54 | arg, | ||
| 55 | "pattern matching in task arguments is not yet supporteds", | ||
| 56 | ); | ||
| 57 | } | ||
| 58 | }, | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | ctxt.check()?; | ||
| 63 | |||
| 64 | let name = f.sig.ident.clone(); | ||
| 65 | |||
| 66 | let visibility = &f.vis; | ||
| 67 | f.sig.ident = format_ident!("task"); | ||
| 68 | let impl_ty = if args.send { | ||
| 69 | quote!(impl ::core::future::Future + Send + 'static) | ||
| 70 | } else { | ||
| 71 | quote!(impl ::core::future::Future + 'static) | ||
| 72 | }; | ||
| 73 | |||
| 74 | let attrs = &f.attrs; | ||
| 75 | |||
| 76 | let result = quote! { | ||
| 77 | #(#attrs)* | ||
| 78 | #visibility fn #name(#fargs) -> #embassy_path::executor::SpawnToken<#impl_ty> { | ||
| 79 | use #embassy_path::executor::raw::TaskStorage; | ||
| 80 | #f | ||
| 81 | type F = #impl_ty; | ||
| 82 | #[allow(clippy::declare_interior_mutable_const)] | ||
| 83 | const NEW_TASK: TaskStorage<F> = TaskStorage::new(); | ||
| 84 | static POOL: [TaskStorage<F>; #pool_size] = [NEW_TASK; #pool_size]; | ||
| 85 | unsafe { TaskStorage::spawn_pool(&POOL, move || task(#arg_names)) } | ||
| 86 | } | ||
| 87 | }; | ||
| 88 | |||
| 89 | Ok(result) | ||
| 90 | } | ||
diff --git a/embassy-macros/src/util/ctxt.rs b/embassy-macros/src/util/ctxt.rs new file mode 100644 index 000000000..d668ae780 --- /dev/null +++ b/embassy-macros/src/util/ctxt.rs | |||
| @@ -0,0 +1,72 @@ | |||
| 1 | // nifty utility borrowed from serde :) | ||
| 2 | // https://github.com/serde-rs/serde/blob/master/serde_derive/src/internals/ctxt.rs | ||
| 3 | |||
| 4 | use proc_macro2::TokenStream; | ||
| 5 | use quote::{quote, ToTokens}; | ||
| 6 | use std::cell::RefCell; | ||
| 7 | use std::fmt::Display; | ||
| 8 | use std::thread; | ||
| 9 | use syn; | ||
| 10 | |||
| 11 | /// A type to collect errors together and format them. | ||
| 12 | /// | ||
| 13 | /// Dropping this object will cause a panic. It must be consumed using `check`. | ||
| 14 | /// | ||
| 15 | /// References can be shared since this type uses run-time exclusive mut checking. | ||
| 16 | #[derive(Default)] | ||
| 17 | pub struct Ctxt { | ||
| 18 | // The contents will be set to `None` during checking. This is so that checking can be | ||
| 19 | // enforced. | ||
| 20 | errors: RefCell<Option<Vec<syn::Error>>>, | ||
| 21 | } | ||
| 22 | |||
| 23 | impl Ctxt { | ||
| 24 | /// Create a new context object. | ||
| 25 | /// | ||
| 26 | /// This object contains no errors, but will still trigger a panic if it is not `check`ed. | ||
| 27 | pub fn new() -> Self { | ||
| 28 | Ctxt { | ||
| 29 | errors: RefCell::new(Some(Vec::new())), | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | /// Add an error to the context object with a tokenenizable object. | ||
| 34 | /// | ||
| 35 | /// The object is used for spanning in error messages. | ||
| 36 | pub fn error_spanned_by<A: ToTokens, T: Display>(&self, obj: A, msg: T) { | ||
| 37 | self.errors | ||
| 38 | .borrow_mut() | ||
| 39 | .as_mut() | ||
| 40 | .unwrap() | ||
| 41 | // Curb monomorphization from generating too many identical methods. | ||
| 42 | .push(syn::Error::new_spanned(obj.into_token_stream(), msg)); | ||
| 43 | } | ||
| 44 | |||
| 45 | /// Add one of Syn's parse errors. | ||
| 46 | #[allow(unused)] | ||
| 47 | pub fn syn_error(&self, err: syn::Error) { | ||
| 48 | self.errors.borrow_mut().as_mut().unwrap().push(err); | ||
| 49 | } | ||
| 50 | |||
| 51 | /// Consume this object, producing a formatted error string if there are errors. | ||
| 52 | pub fn check(self) -> Result<(), TokenStream> { | ||
| 53 | let errors = self.errors.borrow_mut().take().unwrap(); | ||
| 54 | match errors.len() { | ||
| 55 | 0 => Ok(()), | ||
| 56 | _ => Err(to_compile_errors(errors)), | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream { | ||
| 62 | let compile_errors = errors.iter().map(syn::Error::to_compile_error); | ||
| 63 | quote!(#(#compile_errors)*) | ||
| 64 | } | ||
| 65 | |||
| 66 | impl Drop for Ctxt { | ||
| 67 | fn drop(&mut self) { | ||
| 68 | if !thread::panicking() && self.errors.borrow().is_some() { | ||
| 69 | panic!("forgot to check for errors"); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | } | ||
diff --git a/embassy-macros/src/util/mod.rs b/embassy-macros/src/util/mod.rs new file mode 100644 index 000000000..c2f2dfd65 --- /dev/null +++ b/embassy-macros/src/util/mod.rs | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | pub mod ctxt; | ||
| 2 | pub mod path; | ||
diff --git a/embassy-macros/src/path.rs b/embassy-macros/src/util/path.rs index 00fca7bdb..00fca7bdb 100644 --- a/embassy-macros/src/path.rs +++ b/embassy-macros/src/util/path.rs | |||
diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 4ed922ba9..5ced9b1b2 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml | |||
| @@ -6,14 +6,18 @@ edition = "2018" | |||
| 6 | 6 | ||
| 7 | [features] | 7 | [features] |
| 8 | 8 | ||
| 9 | # Enable nightly-only features | ||
| 10 | nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async"] | ||
| 11 | |||
| 9 | # Reexport the PAC for the currently enabled chip at `embassy_nrf::pac`. | 12 | # Reexport the PAC for the currently enabled chip at `embassy_nrf::pac`. |
| 10 | # This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version. | 13 | # This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version. |
| 11 | # If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC. | 14 | # If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC. |
| 12 | # There are no plans to make this stable. | 15 | # There are no plans to make this stable. |
| 13 | unstable-pac = [] | 16 | unstable-pac = [] |
| 14 | 17 | ||
| 15 | # Implement embedded-hal 1.0 alpha and embedded-hal-async traits. | 18 | # Implement embedded-hal 1.0 alpha traits. |
| 16 | unstable-traits = ["embedded-hal-1", "embedded-hal-async"] | 19 | # Implement embedded-hal-async traits if `nightly` is set as well. |
| 20 | unstable-traits = ["embedded-hal-1"] | ||
| 17 | 21 | ||
| 18 | nrf52805 = ["nrf52805-pac", "_ppi"] | 22 | nrf52805 = ["nrf52805-pac", "_ppi"] |
| 19 | nrf52810 = ["nrf52810-pac", "_ppi"] | 23 | nrf52810 = ["nrf52810-pac", "_ppi"] |
diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index a4c24058c..b856c2dfe 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs | |||
| @@ -463,7 +463,6 @@ mod eh02 { | |||
| 463 | #[cfg(feature = "unstable-traits")] | 463 | #[cfg(feature = "unstable-traits")] |
| 464 | mod eh1 { | 464 | mod eh1 { |
| 465 | use super::*; | 465 | use super::*; |
| 466 | use futures::FutureExt; | ||
| 467 | 466 | ||
| 468 | impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::ErrorType for InputChannel<'d, C, T> { | 467 | impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::ErrorType for InputChannel<'d, C, T> { |
| 469 | type Error = Infallible; | 468 | type Error = Infallible; |
| @@ -480,6 +479,12 @@ mod eh1 { | |||
| 480 | self.pin.is_low() | 479 | self.pin.is_low() |
| 481 | } | 480 | } |
| 482 | } | 481 | } |
| 482 | } | ||
| 483 | |||
| 484 | #[cfg(all(feature = "unstable-traits", feature = "nightly"))] | ||
| 485 | mod eh1a { | ||
| 486 | use super::*; | ||
| 487 | use futures::FutureExt; | ||
| 483 | 488 | ||
| 484 | impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Input<'d, T> { | 489 | impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Input<'d, T> { |
| 485 | type WaitForHighFuture<'a> | 490 | type WaitForHighFuture<'a> |
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 8e05d9b6a..b448f6ab6 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![feature(generic_associated_types)] | 2 | #![cfg_attr( |
| 3 | #![feature(type_alias_impl_trait)] | 3 | feature = "nightly", |
| 4 | feature(generic_associated_types, type_alias_impl_trait) | ||
| 5 | )] | ||
| 4 | 6 | ||
| 5 | #[cfg(not(any( | 7 | #[cfg(not(any( |
| 6 | feature = "nrf51", | 8 | feature = "nrf51", |
diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 976a546ce..a45b9390d 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs | |||
| @@ -374,7 +374,6 @@ mod eh02 { | |||
| 374 | #[cfg(feature = "unstable-traits")] | 374 | #[cfg(feature = "unstable-traits")] |
| 375 | mod eh1 { | 375 | mod eh1 { |
| 376 | use super::*; | 376 | use super::*; |
| 377 | use core::future::Future; | ||
| 378 | 377 | ||
| 379 | impl embedded_hal_1::spi::Error for Error { | 378 | impl embedded_hal_1::spi::Error for Error { |
| 380 | fn kind(&self) -> embedded_hal_1::spi::ErrorKind { | 379 | fn kind(&self) -> embedded_hal_1::spi::ErrorKind { |
| @@ -437,7 +436,7 @@ mod eh1 { | |||
| 437 | 436 | ||
| 438 | fn transaction<'a>( | 437 | fn transaction<'a>( |
| 439 | &mut self, | 438 | &mut self, |
| 440 | operations: &mut [embedded_hal_async::spi::Operation<'a, u8>], | 439 | operations: &mut [embedded_hal_1::spi::blocking::Operation<'a, u8>], |
| 441 | ) -> Result<(), Self::Error> { | 440 | ) -> Result<(), Self::Error> { |
| 442 | use embedded_hal_1::spi::blocking::Operation; | 441 | use embedded_hal_1::spi::blocking::Operation; |
| 443 | for o in operations { | 442 | for o in operations { |
| @@ -451,6 +450,12 @@ mod eh1 { | |||
| 451 | Ok(()) | 450 | Ok(()) |
| 452 | } | 451 | } |
| 453 | } | 452 | } |
| 453 | } | ||
| 454 | |||
| 455 | #[cfg(all(feature = "unstable-traits", feature = "nightly"))] | ||
| 456 | mod eh1a { | ||
| 457 | use super::*; | ||
| 458 | use core::future::Future; | ||
| 454 | 459 | ||
| 455 | impl<'d, T: Instance> embedded_hal_async::spi::Read<u8> for Spim<'d, T> { | 460 | impl<'d, T: Instance> embedded_hal_async::spi::Read<u8> for Spim<'d, T> { |
| 456 | type ReadFuture<'a> | 461 | type ReadFuture<'a> |
diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index 19356c2d2..4240b9ac4 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs | |||
| @@ -2,6 +2,7 @@ use core::cell::Cell; | |||
| 2 | use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering}; | 2 | use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering}; |
| 3 | use core::{mem, ptr}; | 3 | use core::{mem, ptr}; |
| 4 | use critical_section::CriticalSection; | 4 | use critical_section::CriticalSection; |
| 5 | use embassy::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 5 | use embassy::blocking_mutex::CriticalSectionMutex as Mutex; | 6 | use embassy::blocking_mutex::CriticalSectionMutex as Mutex; |
| 6 | use embassy::interrupt::{Interrupt, InterruptExt}; | 7 | use embassy::interrupt::{Interrupt, InterruptExt}; |
| 7 | use embassy::time::driver::{AlarmHandle, Driver}; | 8 | use embassy::time::driver::{AlarmHandle, Driver}; |
| @@ -94,7 +95,7 @@ const ALARM_STATE_NEW: AlarmState = AlarmState::new(); | |||
| 94 | embassy::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { | 95 | embassy::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { |
| 95 | period: AtomicU32::new(0), | 96 | period: AtomicU32::new(0), |
| 96 | alarm_count: AtomicU8::new(0), | 97 | alarm_count: AtomicU8::new(0), |
| 97 | alarms: Mutex::new([ALARM_STATE_NEW; ALARM_COUNT]), | 98 | alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [ALARM_STATE_NEW; ALARM_COUNT]), |
| 98 | }); | 99 | }); |
| 99 | 100 | ||
| 100 | impl RtcDriver { | 101 | impl RtcDriver { |
diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 4cd47c897..ed2844f79 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs | |||
| @@ -679,7 +679,7 @@ mod eh1 { | |||
| 679 | fn transaction<'a>( | 679 | fn transaction<'a>( |
| 680 | &mut self, | 680 | &mut self, |
| 681 | _address: u8, | 681 | _address: u8, |
| 682 | _operations: &mut [embedded_hal_async::i2c::Operation<'a>], | 682 | _operations: &mut [embedded_hal_1::i2c::blocking::Operation<'a>], |
| 683 | ) -> Result<(), Self::Error> { | 683 | ) -> Result<(), Self::Error> { |
| 684 | todo!(); | 684 | todo!(); |
| 685 | } | 685 | } |
| @@ -690,58 +690,59 @@ mod eh1 { | |||
| 690 | _operations: O, | 690 | _operations: O, |
| 691 | ) -> Result<(), Self::Error> | 691 | ) -> Result<(), Self::Error> |
| 692 | where | 692 | where |
| 693 | O: IntoIterator<Item = embedded_hal_async::i2c::Operation<'a>>, | 693 | O: IntoIterator<Item = embedded_hal_1::i2c::blocking::Operation<'a>>, |
| 694 | { | 694 | { |
| 695 | todo!(); | 695 | todo!(); |
| 696 | } | 696 | } |
| 697 | } | 697 | } |
| 698 | } | ||
| 698 | 699 | ||
| 699 | impl<'d, T: Instance> embedded_hal_async::i2c::I2c for Twim<'d, T> { | 700 | #[cfg(all(feature = "unstable-traits", feature = "nightly"))] |
| 700 | type ReadFuture<'a> | 701 | impl<'d, T: Instance> embedded_hal_async::i2c::I2c for Twim<'d, T> { |
| 701 | where | 702 | type ReadFuture<'a> |
| 702 | Self: 'a, | 703 | where |
| 703 | = impl Future<Output = Result<(), Self::Error>> + 'a; | 704 | Self: 'a, |
| 705 | = impl Future<Output = Result<(), Self::Error>> + 'a; | ||
| 704 | 706 | ||
| 705 | fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { | 707 | fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { |
| 706 | self.read(address, buffer) | 708 | self.read(address, buffer) |
| 707 | } | 709 | } |
| 708 | 710 | ||
| 709 | type WriteFuture<'a> | 711 | type WriteFuture<'a> |
| 710 | where | 712 | where |
| 711 | Self: 'a, | 713 | Self: 'a, |
| 712 | = impl Future<Output = Result<(), Self::Error>> + 'a; | 714 | = impl Future<Output = Result<(), Self::Error>> + 'a; |
| 713 | 715 | ||
| 714 | fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> { | 716 | fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> { |
| 715 | self.write(address, bytes) | 717 | self.write(address, bytes) |
| 716 | } | 718 | } |
| 717 | 719 | ||
| 718 | type WriteReadFuture<'a> | 720 | type WriteReadFuture<'a> |
| 719 | where | 721 | where |
| 720 | Self: 'a, | 722 | Self: 'a, |
| 721 | = impl Future<Output = Result<(), Self::Error>> + 'a; | 723 | = impl Future<Output = Result<(), Self::Error>> + 'a; |
| 722 | 724 | ||
| 723 | fn write_read<'a>( | 725 | fn write_read<'a>( |
| 724 | &'a mut self, | 726 | &'a mut self, |
| 725 | address: u8, | 727 | address: u8, |
| 726 | wr_buffer: &'a [u8], | 728 | wr_buffer: &'a [u8], |
| 727 | rd_buffer: &'a mut [u8], | 729 | rd_buffer: &'a mut [u8], |
| 728 | ) -> Self::WriteReadFuture<'a> { | 730 | ) -> Self::WriteReadFuture<'a> { |
| 729 | self.write_read(address, wr_buffer, rd_buffer) | 731 | self.write_read(address, wr_buffer, rd_buffer) |
| 730 | } | 732 | } |
| 731 | 733 | ||
| 732 | type TransactionFuture<'a> | 734 | type TransactionFuture<'a> |
| 733 | where | 735 | where |
| 734 | Self: 'a, | 736 | Self: 'a, |
| 735 | = impl Future<Output = Result<(), Self::Error>> + 'a; | 737 | = impl Future<Output = Result<(), Self::Error>> + 'a; |
| 736 | 738 | ||
| 737 | fn transaction<'a>( | 739 | fn transaction<'a>( |
| 738 | &'a mut self, | 740 | &'a mut self, |
| 739 | address: u8, | 741 | address: u8, |
| 740 | operations: &mut [embedded_hal_async::i2c::Operation<'a>], | 742 | operations: &mut [embedded_hal_async::i2c::Operation<'a>], |
| 741 | ) -> Self::TransactionFuture<'a> { | 743 | ) -> Self::TransactionFuture<'a> { |
| 742 | let _ = address; | 744 | let _ = address; |
| 743 | let _ = operations; | 745 | let _ = operations; |
| 744 | async move { todo!() } | 746 | async move { todo!() } |
| 745 | } | ||
| 746 | } | 747 | } |
| 747 | } | 748 | } |
diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index a1c47cff5..119a17981 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs | |||
| @@ -844,7 +844,6 @@ mod eh02 { | |||
| 844 | #[cfg(feature = "unstable-traits")] | 844 | #[cfg(feature = "unstable-traits")] |
| 845 | mod eh1 { | 845 | mod eh1 { |
| 846 | use super::*; | 846 | use super::*; |
| 847 | use core::future::Future; | ||
| 848 | 847 | ||
| 849 | impl embedded_hal_1::serial::Error for Error { | 848 | impl embedded_hal_1::serial::Error for Error { |
| 850 | fn kind(&self) -> embedded_hal_1::serial::ErrorKind { | 849 | fn kind(&self) -> embedded_hal_1::serial::ErrorKind { |
| @@ -872,6 +871,36 @@ mod eh1 { | |||
| 872 | } | 871 | } |
| 873 | } | 872 | } |
| 874 | 873 | ||
| 874 | impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteTx<'d, T> { | ||
| 875 | type Error = Error; | ||
| 876 | } | ||
| 877 | |||
| 878 | impl<'d, T: Instance> embedded_hal_1::serial::blocking::Write for UarteTx<'d, T> { | ||
| 879 | fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { | ||
| 880 | self.blocking_write(buffer) | ||
| 881 | } | ||
| 882 | |||
| 883 | fn flush(&mut self) -> Result<(), Self::Error> { | ||
| 884 | Ok(()) | ||
| 885 | } | ||
| 886 | } | ||
| 887 | |||
| 888 | impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteRx<'d, T> { | ||
| 889 | type Error = Error; | ||
| 890 | } | ||
| 891 | |||
| 892 | impl<'d, U: Instance, T: TimerInstance> embedded_hal_1::serial::ErrorType | ||
| 893 | for UarteWithIdle<'d, U, T> | ||
| 894 | { | ||
| 895 | type Error = Error; | ||
| 896 | } | ||
| 897 | } | ||
| 898 | |||
| 899 | #[cfg(all(feature = "unstable-traits", feature = "nightly"))] | ||
| 900 | mod eh1a { | ||
| 901 | use super::*; | ||
| 902 | use core::future::Future; | ||
| 903 | |||
| 875 | impl<'d, T: Instance> embedded_hal_async::serial::Read for Uarte<'d, T> { | 904 | impl<'d, T: Instance> embedded_hal_async::serial::Read for Uarte<'d, T> { |
| 876 | type ReadFuture<'a> | 905 | type ReadFuture<'a> |
| 877 | where | 906 | where |
| @@ -903,22 +932,6 @@ mod eh1 { | |||
| 903 | } | 932 | } |
| 904 | } | 933 | } |
| 905 | 934 | ||
| 906 | // ===================== | ||
| 907 | |||
| 908 | impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteTx<'d, T> { | ||
| 909 | type Error = Error; | ||
| 910 | } | ||
| 911 | |||
| 912 | impl<'d, T: Instance> embedded_hal_1::serial::blocking::Write for UarteTx<'d, T> { | ||
| 913 | fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { | ||
| 914 | self.blocking_write(buffer) | ||
| 915 | } | ||
| 916 | |||
| 917 | fn flush(&mut self) -> Result<(), Self::Error> { | ||
| 918 | Ok(()) | ||
| 919 | } | ||
| 920 | } | ||
| 921 | |||
| 922 | impl<'d, T: Instance> embedded_hal_async::serial::Write for UarteTx<'d, T> { | 935 | impl<'d, T: Instance> embedded_hal_async::serial::Write for UarteTx<'d, T> { |
| 923 | type WriteFuture<'a> | 936 | type WriteFuture<'a> |
| 924 | where | 937 | where |
| @@ -939,12 +952,6 @@ mod eh1 { | |||
| 939 | } | 952 | } |
| 940 | } | 953 | } |
| 941 | 954 | ||
| 942 | // ===================== | ||
| 943 | |||
| 944 | impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteRx<'d, T> { | ||
| 945 | type Error = Error; | ||
| 946 | } | ||
| 947 | |||
| 948 | impl<'d, T: Instance> embedded_hal_async::serial::Read for UarteRx<'d, T> { | 955 | impl<'d, T: Instance> embedded_hal_async::serial::Read for UarteRx<'d, T> { |
| 949 | type ReadFuture<'a> | 956 | type ReadFuture<'a> |
| 950 | where | 957 | where |
| @@ -956,14 +963,6 @@ mod eh1 { | |||
| 956 | } | 963 | } |
| 957 | } | 964 | } |
| 958 | 965 | ||
| 959 | // ===================== | ||
| 960 | |||
| 961 | impl<'d, U: Instance, T: TimerInstance> embedded_hal_1::serial::ErrorType | ||
| 962 | for UarteWithIdle<'d, U, T> | ||
| 963 | { | ||
| 964 | type Error = Error; | ||
| 965 | } | ||
| 966 | |||
| 967 | impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Read | 966 | impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Read |
| 968 | for UarteWithIdle<'d, U, T> | 967 | for UarteWithIdle<'d, U, T> |
| 969 | { | 968 | { |
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index be41f95eb..cb6544028 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml | |||
| @@ -13,7 +13,7 @@ edition = "2018" | |||
| 13 | unstable-pac = [] | 13 | unstable-pac = [] |
| 14 | 14 | ||
| 15 | [dependencies] | 15 | [dependencies] |
| 16 | embassy = { version = "0.1.0", path = "../embassy", features = [ "time-tick-1mhz" ] } | 16 | embassy = { version = "0.1.0", path = "../embassy", features = [ "time-tick-1mhz", "nightly"] } |
| 17 | embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } | 17 | embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } |
| 18 | embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["rp"]} | 18 | embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["rp"]} |
| 19 | atomic-polyfill = "0.1.5" | 19 | atomic-polyfill = "0.1.5" |
diff --git a/embassy-rp/src/timer.rs b/embassy-rp/src/timer.rs index b3c047ca4..f449df000 100644 --- a/embassy-rp/src/timer.rs +++ b/embassy-rp/src/timer.rs | |||
| @@ -1,7 +1,8 @@ | |||
| 1 | use atomic_polyfill::{AtomicU8, Ordering}; | 1 | use atomic_polyfill::{AtomicU8, Ordering}; |
| 2 | use core::cell::Cell; | 2 | use core::cell::Cell; |
| 3 | use critical_section::CriticalSection; | 3 | use critical_section::CriticalSection; |
| 4 | use embassy::blocking_mutex::CriticalSectionMutex as Mutex; | 4 | use embassy::blocking_mutex::raw::CriticalSectionRawMutex; |
| 5 | use embassy::blocking_mutex::Mutex; | ||
| 5 | use embassy::interrupt::{Interrupt, InterruptExt}; | 6 | use embassy::interrupt::{Interrupt, InterruptExt}; |
| 6 | use embassy::time::driver::{AlarmHandle, Driver}; | 7 | use embassy::time::driver::{AlarmHandle, Driver}; |
| 7 | 8 | ||
| @@ -20,12 +21,12 @@ const DUMMY_ALARM: AlarmState = AlarmState { | |||
| 20 | }; | 21 | }; |
| 21 | 22 | ||
| 22 | struct TimerDriver { | 23 | struct TimerDriver { |
| 23 | alarms: Mutex<[AlarmState; ALARM_COUNT]>, | 24 | alarms: Mutex<CriticalSectionRawMutex, [AlarmState; ALARM_COUNT]>, |
| 24 | next_alarm: AtomicU8, | 25 | next_alarm: AtomicU8, |
| 25 | } | 26 | } |
| 26 | 27 | ||
| 27 | embassy::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{ | 28 | embassy::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{ |
| 28 | alarms: Mutex::new([DUMMY_ALARM; ALARM_COUNT]), | 29 | alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [DUMMY_ALARM; ALARM_COUNT]), |
| 29 | next_alarm: AtomicU8::new(0), | 30 | next_alarm: AtomicU8::new(0), |
| 30 | }); | 31 | }); |
| 31 | 32 | ||
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 386722d47..55a646d4a 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -6,7 +6,7 @@ edition = "2018" | |||
| 6 | resolver = "2" | 6 | resolver = "2" |
| 7 | 7 | ||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | embassy = { version = "0.1.0", path = "../embassy" } | 9 | embassy = { version = "0.1.0", path = "../embassy", features = ["nightly"]} |
| 10 | embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["stm32"] } | 10 | embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["stm32"] } |
| 11 | embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } | 11 | embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } |
| 12 | embassy-net = { version = "0.1.0", path = "../embassy-net", default-features = false, optional = true } | 12 | embassy-net = { version = "0.1.0", path = "../embassy-net", default-features = false, optional = true } |
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index d1596c5f8..7efe0d3a5 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs | |||
| @@ -3,13 +3,15 @@ use core::cell::Cell; | |||
| 3 | use core::convert::TryInto; | 3 | use core::convert::TryInto; |
| 4 | use core::sync::atomic::{compiler_fence, Ordering}; | 4 | use core::sync::atomic::{compiler_fence, Ordering}; |
| 5 | use core::{mem, ptr}; | 5 | use core::{mem, ptr}; |
| 6 | use embassy::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 7 | use embassy::blocking_mutex::Mutex; | ||
| 6 | use embassy::interrupt::InterruptExt; | 8 | use embassy::interrupt::InterruptExt; |
| 7 | use embassy::time::driver::{AlarmHandle, Driver}; | 9 | use embassy::time::driver::{AlarmHandle, Driver}; |
| 8 | use embassy::time::TICKS_PER_SECOND; | 10 | use embassy::time::TICKS_PER_SECOND; |
| 9 | use stm32_metapac::timer::regs; | 11 | use stm32_metapac::timer::regs; |
| 10 | 12 | ||
| 11 | use crate::interrupt; | 13 | use crate::interrupt; |
| 12 | use crate::interrupt::{CriticalSection, Interrupt, Mutex}; | 14 | use crate::interrupt::{CriticalSection, Interrupt}; |
| 13 | use crate::pac::timer::{vals, TimGp16}; | 15 | use crate::pac::timer::{vals, TimGp16}; |
| 14 | use crate::peripherals; | 16 | use crate::peripherals; |
| 15 | use crate::rcc::sealed::RccPeripheral; | 17 | use crate::rcc::sealed::RccPeripheral; |
| @@ -95,7 +97,7 @@ struct RtcDriver { | |||
| 95 | period: AtomicU32, | 97 | period: AtomicU32, |
| 96 | alarm_count: AtomicU8, | 98 | alarm_count: AtomicU8, |
| 97 | /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. | 99 | /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. |
| 98 | alarms: Mutex<[AlarmState; ALARM_COUNT]>, | 100 | alarms: Mutex<CriticalSectionRawMutex, [AlarmState; ALARM_COUNT]>, |
| 99 | } | 101 | } |
| 100 | 102 | ||
| 101 | const ALARM_STATE_NEW: AlarmState = AlarmState::new(); | 103 | const ALARM_STATE_NEW: AlarmState = AlarmState::new(); |
| @@ -103,7 +105,7 @@ const ALARM_STATE_NEW: AlarmState = AlarmState::new(); | |||
| 103 | embassy::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { | 105 | embassy::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { |
| 104 | period: AtomicU32::new(0), | 106 | period: AtomicU32::new(0), |
| 105 | alarm_count: AtomicU8::new(0), | 107 | alarm_count: AtomicU8::new(0), |
| 106 | alarms: Mutex::new([ALARM_STATE_NEW; ALARM_COUNT]), | 108 | alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [ALARM_STATE_NEW; ALARM_COUNT]), |
| 107 | }); | 109 | }); |
| 108 | 110 | ||
| 109 | impl RtcDriver { | 111 | impl RtcDriver { |
diff --git a/embassy/Cargo.toml b/embassy/Cargo.toml index ccb5574d7..d10a8874e 100644 --- a/embassy/Cargo.toml +++ b/embassy/Cargo.toml | |||
| @@ -10,8 +10,12 @@ default = [] | |||
| 10 | std = ["futures/std", "time", "time-tick-1mhz", "embassy-macros/std"] | 10 | std = ["futures/std", "time", "time-tick-1mhz", "embassy-macros/std"] |
| 11 | wasm = ["wasm-bindgen", "js-sys", "embassy-macros/wasm", "wasm-timer", "time", "time-tick-1mhz"] | 11 | wasm = ["wasm-bindgen", "js-sys", "embassy-macros/wasm", "wasm-timer", "time", "time-tick-1mhz"] |
| 12 | 12 | ||
| 13 | # Enable nightly-only features | ||
| 14 | nightly = ["embedded-hal-async"] | ||
| 15 | |||
| 13 | # Implement embedded-hal 1.0 alpha and embedded-hal-async traits. | 16 | # Implement embedded-hal 1.0 alpha and embedded-hal-async traits. |
| 14 | unstable-traits = ["embedded-hal-1", "embedded-hal-async"] | 17 | # Implement embedded-hal-async traits if `nightly` is set as well. |
| 18 | unstable-traits = ["embedded-hal-1"] | ||
| 15 | 19 | ||
| 16 | # Enable `embassy::time` module. | 20 | # Enable `embassy::time` module. |
| 17 | # NOTE: This feature is only intended to be enabled by crates providing the time driver implementation. | 21 | # NOTE: This feature is only intended to be enabled by crates providing the time driver implementation. |
diff --git a/embassy/src/blocking_mutex/kind.rs b/embassy/src/blocking_mutex/kind.rs deleted file mode 100644 index a4a45605f..000000000 --- a/embassy/src/blocking_mutex/kind.rs +++ /dev/null | |||
| @@ -1,20 +0,0 @@ | |||
| 1 | pub trait MutexKind { | ||
| 2 | type Mutex<T>: super::Mutex<Data = T>; | ||
| 3 | } | ||
| 4 | |||
| 5 | pub enum CriticalSection {} | ||
| 6 | impl MutexKind for CriticalSection { | ||
| 7 | type Mutex<T> = super::CriticalSectionMutex<T>; | ||
| 8 | } | ||
| 9 | |||
| 10 | #[cfg(any(cortex_m, feature = "std"))] | ||
| 11 | pub enum ThreadMode {} | ||
| 12 | #[cfg(any(cortex_m, feature = "std"))] | ||
| 13 | impl MutexKind for ThreadMode { | ||
| 14 | type Mutex<T> = super::ThreadModeMutex<T>; | ||
| 15 | } | ||
| 16 | |||
| 17 | pub enum Noop {} | ||
| 18 | impl MutexKind for Noop { | ||
| 19 | type Mutex<T> = super::NoopMutex<T>; | ||
| 20 | } | ||
diff --git a/embassy/src/blocking_mutex/mod.rs b/embassy/src/blocking_mutex/mod.rs index 949531392..859eca075 100644 --- a/embassy/src/blocking_mutex/mod.rs +++ b/embassy/src/blocking_mutex/mod.rs | |||
| @@ -1,67 +1,107 @@ | |||
| 1 | //! Blocking mutex (not async) | 1 | //! Blocking mutex (not async) |
| 2 | 2 | ||
| 3 | pub mod kind; | 3 | pub mod raw; |
| 4 | 4 | ||
| 5 | use self::raw::RawMutex; | ||
| 5 | use core::cell::UnsafeCell; | 6 | use core::cell::UnsafeCell; |
| 6 | use critical_section::CriticalSection; | ||
| 7 | 7 | ||
| 8 | /// Any object implementing this trait guarantees exclusive access to the data contained | 8 | /// Any object implementing this trait guarantees exclusive access to the data contained |
| 9 | /// within the mutex for the duration of the lock. | 9 | /// within the mutex for the duration of the lock. |
| 10 | /// Adapted from <https://github.com/rust-embedded/mutex-trait>. | 10 | /// Adapted from <https://github.com/rust-embedded/mutex-trait>. |
| 11 | pub trait Mutex { | 11 | pub struct Mutex<R, T: ?Sized> { |
| 12 | /// Data protected by the mutex. | 12 | // NOTE: `raw` must be FIRST, so when using ThreadModeMutex the "can't drop in non-thread-mode" gets |
| 13 | type Data; | 13 | // to run BEFORE dropping `data`. |
| 14 | raw: R, | ||
| 15 | data: UnsafeCell<T>, | ||
| 16 | } | ||
| 14 | 17 | ||
| 15 | fn new(data: Self::Data) -> Self; | 18 | unsafe impl<R: RawMutex + Send, T: ?Sized + Send> Send for Mutex<R, T> {} |
| 19 | unsafe impl<R: RawMutex + Sync, T: ?Sized + Send> Sync for Mutex<R, T> {} | ||
| 20 | |||
| 21 | impl<R: RawMutex, T> Mutex<R, T> { | ||
| 22 | /// Creates a new mutex in an unlocked state ready for use. | ||
| 23 | #[cfg(feature = "nightly")] | ||
| 24 | #[inline] | ||
| 25 | pub const fn new(val: T) -> Mutex<R, T> { | ||
| 26 | Mutex { | ||
| 27 | raw: R::INIT, | ||
| 28 | data: UnsafeCell::new(val), | ||
| 29 | } | ||
| 30 | } | ||
| 16 | 31 | ||
| 17 | /// Creates a critical section and grants temporary access to the protected data. | 32 | /// Creates a new mutex in an unlocked state ready for use. |
| 18 | fn lock<R>(&self, f: impl FnOnce(&Self::Data) -> R) -> R; | 33 | #[cfg(not(feature = "nightly"))] |
| 19 | } | 34 | #[inline] |
| 35 | pub fn new(val: T) -> Mutex<R, T> { | ||
| 36 | Mutex { | ||
| 37 | raw: R::INIT, | ||
| 38 | data: UnsafeCell::new(val), | ||
| 39 | } | ||
| 40 | } | ||
| 20 | 41 | ||
| 21 | /// A "mutex" based on critical sections | 42 | /// Creates a critical section and grants temporary access to the protected data. |
| 22 | /// | 43 | pub fn lock<U>(&self, f: impl FnOnce(&T) -> U) -> U { |
| 23 | /// # Safety | 44 | self.raw.lock(|| { |
| 24 | /// | 45 | let ptr = self.data.get() as *const T; |
| 25 | /// **This Mutex is only safe on single-core systems.** | 46 | let inner = unsafe { &*ptr }; |
| 26 | /// | 47 | f(inner) |
| 27 | /// On multi-core systems, a `CriticalSection` **is not sufficient** to ensure exclusive access. | 48 | }) |
| 28 | pub struct CriticalSectionMutex<T> { | 49 | } |
| 29 | inner: UnsafeCell<T>, | ||
| 30 | } | 50 | } |
| 31 | 51 | ||
| 32 | // NOTE: A `CriticalSectionMutex` can be used as a channel so the protected data must be `Send` | 52 | impl<R, T> Mutex<R, T> { |
| 33 | // to prevent sending non-Sendable stuff (e.g. access tokens) across different | 53 | /// Creates a new mutex based on a pre-existing raw mutex. |
| 34 | // execution contexts (e.g. interrupts) | 54 | /// |
| 35 | unsafe impl<T> Sync for CriticalSectionMutex<T> where T: Send {} | 55 | /// This allows creating a mutex in a constant context on stable Rust. |
| 36 | 56 | #[inline] | |
| 37 | impl<T> CriticalSectionMutex<T> { | 57 | pub const fn const_new(raw_mutex: R, val: T) -> Mutex<R, T> { |
| 38 | /// Creates a new mutex | 58 | Mutex { |
| 39 | pub const fn new(value: T) -> Self { | 59 | raw: raw_mutex, |
| 40 | CriticalSectionMutex { | 60 | data: UnsafeCell::new(val), |
| 41 | inner: UnsafeCell::new(value), | ||
| 42 | } | 61 | } |
| 43 | } | 62 | } |
| 44 | } | ||
| 45 | 63 | ||
| 46 | impl<T> CriticalSectionMutex<T> { | 64 | /// Consumes this mutex, returning the underlying data. |
| 47 | /// Borrows the data for the duration of the critical section | 65 | #[inline] |
| 48 | pub fn borrow<'cs>(&'cs self, _cs: CriticalSection<'cs>) -> &'cs T { | 66 | pub fn into_inner(self) -> T { |
| 49 | unsafe { &*self.inner.get() } | 67 | self.data.into_inner() |
| 68 | } | ||
| 69 | |||
| 70 | /// Returns a mutable reference to the underlying data. | ||
| 71 | /// | ||
| 72 | /// Since this call borrows the `Mutex` mutably, no actual locking needs to | ||
| 73 | /// take place---the mutable borrow statically guarantees no locks exist. | ||
| 74 | #[inline] | ||
| 75 | pub fn get_mut(&mut self) -> &mut T { | ||
| 76 | unsafe { &mut *self.data.get() } | ||
| 50 | } | 77 | } |
| 51 | } | 78 | } |
| 52 | 79 | ||
| 53 | impl<T> Mutex for CriticalSectionMutex<T> { | 80 | pub type CriticalSectionMutex<T> = Mutex<raw::CriticalSectionRawMutex, T>; |
| 54 | type Data = T; | 81 | pub type NoopMutex<T> = Mutex<raw::NoopRawMutex, T>; |
| 55 | 82 | ||
| 56 | fn new(data: T) -> Self { | 83 | impl<T> Mutex<raw::CriticalSectionRawMutex, T> { |
| 57 | Self::new(data) | 84 | /// Borrows the data for the duration of the critical section |
| 85 | pub fn borrow<'cs>(&'cs self, _cs: critical_section::CriticalSection<'cs>) -> &'cs T { | ||
| 86 | let ptr = self.data.get() as *const T; | ||
| 87 | unsafe { &*ptr } | ||
| 58 | } | 88 | } |
| 89 | } | ||
| 59 | 90 | ||
| 60 | fn lock<R>(&self, f: impl FnOnce(&Self::Data) -> R) -> R { | 91 | impl<T> Mutex<raw::NoopRawMutex, T> { |
| 61 | critical_section::with(|cs| f(self.borrow(cs))) | 92 | /// Borrows the data |
| 93 | pub fn borrow(&self) -> &T { | ||
| 94 | let ptr = self.data.get() as *const T; | ||
| 95 | unsafe { &*ptr } | ||
| 62 | } | 96 | } |
| 63 | } | 97 | } |
| 64 | 98 | ||
| 99 | // ThreadModeMutex does NOT use the generic mutex from above because it's special: | ||
| 100 | // it's Send+Sync even if T: !Send. There's no way to do that without specialization (I think?). | ||
| 101 | // | ||
| 102 | // There's still a ThreadModeRawMutex for use with the generic Mutex (handy with Channel, for example), | ||
| 103 | // but that will require T: Send even though it shouldn't be needed. | ||
| 104 | |||
| 65 | #[cfg(any(cortex_m, feature = "std"))] | 105 | #[cfg(any(cortex_m, feature = "std"))] |
| 66 | pub use thread_mode_mutex::*; | 106 | pub use thread_mode_mutex::*; |
| 67 | #[cfg(any(cortex_m, feature = "std"))] | 107 | #[cfg(any(cortex_m, feature = "std"))] |
| @@ -75,15 +115,15 @@ mod thread_mode_mutex { | |||
| 75 | /// **This Mutex is only safe on single-core systems.** | 115 | /// **This Mutex is only safe on single-core systems.** |
| 76 | /// | 116 | /// |
| 77 | /// On multi-core systems, a `ThreadModeMutex` **is not sufficient** to ensure exclusive access. | 117 | /// On multi-core systems, a `ThreadModeMutex` **is not sufficient** to ensure exclusive access. |
| 78 | pub struct ThreadModeMutex<T> { | 118 | pub struct ThreadModeMutex<T: ?Sized> { |
| 79 | inner: UnsafeCell<T>, | 119 | inner: UnsafeCell<T>, |
| 80 | } | 120 | } |
| 81 | 121 | ||
| 82 | // NOTE: ThreadModeMutex only allows borrowing from one execution context ever: thread mode. | 122 | // NOTE: ThreadModeMutex only allows borrowing from one execution context ever: thread mode. |
| 83 | // Therefore it cannot be used to send non-sendable stuff between execution contexts, so it can | 123 | // Therefore it cannot be used to send non-sendable stuff between execution contexts, so it can |
| 84 | // be Send+Sync even if T is not Send (unlike CriticalSectionMutex) | 124 | // be Send+Sync even if T is not Send (unlike CriticalSectionMutex) |
| 85 | unsafe impl<T> Sync for ThreadModeMutex<T> {} | 125 | unsafe impl<T: ?Sized> Sync for ThreadModeMutex<T> {} |
| 86 | unsafe impl<T> Send for ThreadModeMutex<T> {} | 126 | unsafe impl<T: ?Sized> Send for ThreadModeMutex<T> {} |
| 87 | 127 | ||
| 88 | impl<T> ThreadModeMutex<T> { | 128 | impl<T> ThreadModeMutex<T> { |
| 89 | /// Creates a new mutex | 129 | /// Creates a new mutex |
| @@ -92,79 +132,35 @@ mod thread_mode_mutex { | |||
| 92 | inner: UnsafeCell::new(value), | 132 | inner: UnsafeCell::new(value), |
| 93 | } | 133 | } |
| 94 | } | 134 | } |
| 135 | } | ||
| 136 | |||
| 137 | impl<T: ?Sized> ThreadModeMutex<T> { | ||
| 138 | pub fn lock<R>(&self, f: impl FnOnce(&T) -> R) -> R { | ||
| 139 | f(self.borrow()) | ||
| 140 | } | ||
| 95 | 141 | ||
| 96 | /// Borrows the data | 142 | /// Borrows the data |
| 97 | pub fn borrow(&self) -> &T { | 143 | pub fn borrow(&self) -> &T { |
| 98 | assert!( | 144 | assert!( |
| 99 | in_thread_mode(), | 145 | raw::in_thread_mode(), |
| 100 | "ThreadModeMutex can only be borrowed from thread mode." | 146 | "ThreadModeMutex can only be borrowed from thread mode." |
| 101 | ); | 147 | ); |
| 102 | unsafe { &*self.inner.get() } | 148 | unsafe { &*self.inner.get() } |
| 103 | } | 149 | } |
| 104 | } | 150 | } |
| 105 | 151 | ||
| 106 | impl<T> Mutex for ThreadModeMutex<T> { | 152 | impl<T: ?Sized> Drop for ThreadModeMutex<T> { |
| 107 | type Data = T; | ||
| 108 | |||
| 109 | fn new(data: T) -> Self { | ||
| 110 | Self::new(data) | ||
| 111 | } | ||
| 112 | |||
| 113 | fn lock<R>(&self, f: impl FnOnce(&Self::Data) -> R) -> R { | ||
| 114 | f(self.borrow()) | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | impl<T> Drop for ThreadModeMutex<T> { | ||
| 119 | fn drop(&mut self) { | 153 | fn drop(&mut self) { |
| 120 | // Only allow dropping from thread mode. Dropping calls drop on the inner `T`, so | 154 | // Only allow dropping from thread mode. Dropping calls drop on the inner `T`, so |
| 121 | // `drop` needs the same guarantees as `lock`. `ThreadModeMutex<T>` is Send even if | 155 | // `drop` needs the same guarantees as `lock`. `ThreadModeMutex<T>` is Send even if |
| 122 | // T isn't, so without this check a user could create a ThreadModeMutex in thread mode, | 156 | // T isn't, so without this check a user could create a ThreadModeMutex in thread mode, |
| 123 | // send it to interrupt context and drop it there, which would "send" a T even if T is not Send. | 157 | // send it to interrupt context and drop it there, which would "send" a T even if T is not Send. |
| 124 | assert!( | 158 | assert!( |
| 125 | in_thread_mode(), | 159 | raw::in_thread_mode(), |
| 126 | "ThreadModeMutex can only be dropped from thread mode." | 160 | "ThreadModeMutex can only be dropped from thread mode." |
| 127 | ); | 161 | ); |
| 128 | 162 | ||
| 129 | // Drop of the inner `T` happens after this. | 163 | // Drop of the inner `T` happens after this. |
| 130 | } | 164 | } |
| 131 | } | 165 | } |
| 132 | |||
| 133 | pub fn in_thread_mode() -> bool { | ||
| 134 | #[cfg(feature = "std")] | ||
| 135 | return Some("main") == std::thread::current().name(); | ||
| 136 | |||
| 137 | #[cfg(not(feature = "std"))] | ||
| 138 | return cortex_m::peripheral::SCB::vect_active() | ||
| 139 | == cortex_m::peripheral::scb::VectActive::ThreadMode; | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | /// A "mutex" that does nothing and cannot be shared between threads. | ||
| 144 | pub struct NoopMutex<T> { | ||
| 145 | inner: T, | ||
| 146 | } | ||
| 147 | |||
| 148 | impl<T> NoopMutex<T> { | ||
| 149 | pub const fn new(value: T) -> Self { | ||
| 150 | NoopMutex { inner: value } | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | impl<T> NoopMutex<T> { | ||
| 155 | pub fn borrow(&self) -> &T { | ||
| 156 | &self.inner | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | impl<T> Mutex for NoopMutex<T> { | ||
| 161 | type Data = T; | ||
| 162 | |||
| 163 | fn new(data: T) -> Self { | ||
| 164 | Self::new(data) | ||
| 165 | } | ||
| 166 | |||
| 167 | fn lock<R>(&self, f: impl FnOnce(&Self::Data) -> R) -> R { | ||
| 168 | f(self.borrow()) | ||
| 169 | } | ||
| 170 | } | 166 | } |
diff --git a/embassy/src/blocking_mutex/raw.rs b/embassy/src/blocking_mutex/raw.rs new file mode 100644 index 000000000..ebeb6dccf --- /dev/null +++ b/embassy/src/blocking_mutex/raw.rs | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | |||
| 3 | pub trait RawMutex { | ||
| 4 | const INIT: Self; | ||
| 5 | |||
| 6 | fn lock<R>(&self, f: impl FnOnce() -> R) -> R; | ||
| 7 | } | ||
| 8 | |||
| 9 | pub struct CriticalSectionRawMutex { | ||
| 10 | _phantom: PhantomData<()>, | ||
| 11 | } | ||
| 12 | unsafe impl Send for CriticalSectionRawMutex {} | ||
| 13 | unsafe impl Sync for CriticalSectionRawMutex {} | ||
| 14 | |||
| 15 | impl CriticalSectionRawMutex { | ||
| 16 | pub const fn new() -> Self { | ||
| 17 | Self { | ||
| 18 | _phantom: PhantomData, | ||
| 19 | } | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | impl RawMutex for CriticalSectionRawMutex { | ||
| 24 | const INIT: Self = Self::new(); | ||
| 25 | |||
| 26 | fn lock<R>(&self, f: impl FnOnce() -> R) -> R { | ||
| 27 | critical_section::with(|_| f()) | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | // ================ | ||
| 32 | |||
| 33 | pub struct NoopRawMutex { | ||
| 34 | _phantom: PhantomData<*mut ()>, | ||
| 35 | } | ||
| 36 | |||
| 37 | unsafe impl Send for NoopRawMutex {} | ||
| 38 | |||
| 39 | impl NoopRawMutex { | ||
| 40 | pub const fn new() -> Self { | ||
| 41 | Self { | ||
| 42 | _phantom: PhantomData, | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | impl RawMutex for NoopRawMutex { | ||
| 48 | const INIT: Self = Self::new(); | ||
| 49 | fn lock<R>(&self, f: impl FnOnce() -> R) -> R { | ||
| 50 | f() | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | // ================ | ||
| 55 | |||
| 56 | #[cfg(any(cortex_m, feature = "std"))] | ||
| 57 | mod thread_mode { | ||
| 58 | use super::*; | ||
| 59 | |||
| 60 | pub struct ThreadModeRawMutex { | ||
| 61 | _phantom: PhantomData<()>, | ||
| 62 | } | ||
| 63 | |||
| 64 | unsafe impl Send for ThreadModeRawMutex {} | ||
| 65 | unsafe impl Sync for ThreadModeRawMutex {} | ||
| 66 | |||
| 67 | impl ThreadModeRawMutex { | ||
| 68 | pub const fn new() -> Self { | ||
| 69 | Self { | ||
| 70 | _phantom: PhantomData, | ||
| 71 | } | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | impl RawMutex for ThreadModeRawMutex { | ||
| 76 | const INIT: Self = Self::new(); | ||
| 77 | fn lock<R>(&self, f: impl FnOnce() -> R) -> R { | ||
| 78 | assert!( | ||
| 79 | in_thread_mode(), | ||
| 80 | "ThreadModeMutex can only be locked from thread mode." | ||
| 81 | ); | ||
| 82 | |||
| 83 | f() | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | impl Drop for ThreadModeRawMutex { | ||
| 88 | fn drop(&mut self) { | ||
| 89 | // Only allow dropping from thread mode. Dropping calls drop on the inner `T`, so | ||
| 90 | // `drop` needs the same guarantees as `lock`. `ThreadModeMutex<T>` is Send even if | ||
| 91 | // T isn't, so without this check a user could create a ThreadModeMutex in thread mode, | ||
| 92 | // send it to interrupt context and drop it there, which would "send" a T even if T is not Send. | ||
| 93 | assert!( | ||
| 94 | in_thread_mode(), | ||
| 95 | "ThreadModeMutex can only be dropped from thread mode." | ||
| 96 | ); | ||
| 97 | |||
| 98 | // Drop of the inner `T` happens after this. | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | pub(crate) fn in_thread_mode() -> bool { | ||
| 103 | #[cfg(feature = "std")] | ||
| 104 | return Some("main") == std::thread::current().name(); | ||
| 105 | |||
| 106 | #[cfg(not(feature = "std"))] | ||
| 107 | return cortex_m::peripheral::SCB::vect_active() | ||
| 108 | == cortex_m::peripheral::scb::VectActive::ThreadMode; | ||
| 109 | } | ||
| 110 | } | ||
| 111 | #[cfg(any(cortex_m, feature = "std"))] | ||
| 112 | pub use thread_mode::*; | ||
diff --git a/embassy/src/channel/mpsc.rs b/embassy/src/channel/mpsc.rs index 04709cb90..32787d810 100644 --- a/embassy/src/channel/mpsc.rs +++ b/embassy/src/channel/mpsc.rs | |||
| @@ -47,7 +47,7 @@ use core::task::Waker; | |||
| 47 | use futures::Future; | 47 | use futures::Future; |
| 48 | use heapless::Deque; | 48 | use heapless::Deque; |
| 49 | 49 | ||
| 50 | use crate::blocking_mutex::kind::MutexKind; | 50 | use crate::blocking_mutex::raw::RawMutex; |
| 51 | use crate::blocking_mutex::Mutex; | 51 | use crate::blocking_mutex::Mutex; |
| 52 | use crate::waitqueue::WakerRegistration; | 52 | use crate::waitqueue::WakerRegistration; |
| 53 | 53 | ||
| @@ -56,7 +56,7 @@ use crate::waitqueue::WakerRegistration; | |||
| 56 | /// Instances are created by the [`split`](split) function. | 56 | /// Instances are created by the [`split`](split) function. |
| 57 | pub struct Sender<'ch, M, T, const N: usize> | 57 | pub struct Sender<'ch, M, T, const N: usize> |
| 58 | where | 58 | where |
| 59 | M: MutexKind, | 59 | M: RawMutex, |
| 60 | { | 60 | { |
| 61 | channel: &'ch Channel<M, T, N>, | 61 | channel: &'ch Channel<M, T, N>, |
| 62 | } | 62 | } |
| @@ -66,7 +66,7 @@ where | |||
| 66 | /// Instances are created by the [`split`](split) function. | 66 | /// Instances are created by the [`split`](split) function. |
| 67 | pub struct Receiver<'ch, M, T, const N: usize> | 67 | pub struct Receiver<'ch, M, T, const N: usize> |
| 68 | where | 68 | where |
| 69 | M: MutexKind, | 69 | M: RawMutex, |
| 70 | { | 70 | { |
| 71 | channel: &'ch Channel<M, T, N>, | 71 | channel: &'ch Channel<M, T, N>, |
| 72 | } | 72 | } |
| @@ -99,7 +99,7 @@ pub fn split<M, T, const N: usize>( | |||
| 99 | channel: &mut Channel<M, T, N>, | 99 | channel: &mut Channel<M, T, N>, |
| 100 | ) -> (Sender<M, T, N>, Receiver<M, T, N>) | 100 | ) -> (Sender<M, T, N>, Receiver<M, T, N>) |
| 101 | where | 101 | where |
| 102 | M: MutexKind, | 102 | M: RawMutex, |
| 103 | { | 103 | { |
| 104 | let sender = Sender { channel }; | 104 | let sender = Sender { channel }; |
| 105 | let receiver = Receiver { channel }; | 105 | let receiver = Receiver { channel }; |
| @@ -112,7 +112,7 @@ where | |||
| 112 | 112 | ||
| 113 | impl<'ch, M, T, const N: usize> Receiver<'ch, M, T, N> | 113 | impl<'ch, M, T, const N: usize> Receiver<'ch, M, T, N> |
| 114 | where | 114 | where |
| 115 | M: MutexKind, | 115 | M: RawMutex, |
| 116 | { | 116 | { |
| 117 | /// Receives the next value for this receiver. | 117 | /// Receives the next value for this receiver. |
| 118 | /// | 118 | /// |
| @@ -161,7 +161,7 @@ where | |||
| 161 | 161 | ||
| 162 | impl<'ch, M, T, const N: usize> Drop for Receiver<'ch, M, T, N> | 162 | impl<'ch, M, T, const N: usize> Drop for Receiver<'ch, M, T, N> |
| 163 | where | 163 | where |
| 164 | M: MutexKind, | 164 | M: RawMutex, |
| 165 | { | 165 | { |
| 166 | fn drop(&mut self) { | 166 | fn drop(&mut self) { |
| 167 | self.channel.lock(|c| c.deregister_receiver()) | 167 | self.channel.lock(|c| c.deregister_receiver()) |
| @@ -170,14 +170,14 @@ where | |||
| 170 | 170 | ||
| 171 | pub struct RecvFuture<'ch, M, T, const N: usize> | 171 | pub struct RecvFuture<'ch, M, T, const N: usize> |
| 172 | where | 172 | where |
| 173 | M: MutexKind, | 173 | M: RawMutex, |
| 174 | { | 174 | { |
| 175 | channel: &'ch Channel<M, T, N>, | 175 | channel: &'ch Channel<M, T, N>, |
| 176 | } | 176 | } |
| 177 | 177 | ||
| 178 | impl<'ch, M, T, const N: usize> Future for RecvFuture<'ch, M, T, N> | 178 | impl<'ch, M, T, const N: usize> Future for RecvFuture<'ch, M, T, N> |
| 179 | where | 179 | where |
| 180 | M: MutexKind, | 180 | M: RawMutex, |
| 181 | { | 181 | { |
| 182 | type Output = Option<T>; | 182 | type Output = Option<T>; |
| 183 | 183 | ||
| @@ -193,7 +193,7 @@ where | |||
| 193 | 193 | ||
| 194 | impl<'ch, M, T, const N: usize> Sender<'ch, M, T, N> | 194 | impl<'ch, M, T, const N: usize> Sender<'ch, M, T, N> |
| 195 | where | 195 | where |
| 196 | M: MutexKind, | 196 | M: RawMutex, |
| 197 | { | 197 | { |
| 198 | /// Sends a value, waiting until there is capacity. | 198 | /// Sends a value, waiting until there is capacity. |
| 199 | /// | 199 | /// |
| @@ -268,7 +268,7 @@ where | |||
| 268 | 268 | ||
| 269 | pub struct SendFuture<'ch, M, T, const N: usize> | 269 | pub struct SendFuture<'ch, M, T, const N: usize> |
| 270 | where | 270 | where |
| 271 | M: MutexKind, | 271 | M: RawMutex, |
| 272 | { | 272 | { |
| 273 | channel: &'ch Channel<M, T, N>, | 273 | channel: &'ch Channel<M, T, N>, |
| 274 | message: Option<T>, | 274 | message: Option<T>, |
| @@ -276,7 +276,7 @@ where | |||
| 276 | 276 | ||
| 277 | impl<'ch, M, T, const N: usize> Future for SendFuture<'ch, M, T, N> | 277 | impl<'ch, M, T, const N: usize> Future for SendFuture<'ch, M, T, N> |
| 278 | where | 278 | where |
| 279 | M: MutexKind, | 279 | M: RawMutex, |
| 280 | { | 280 | { |
| 281 | type Output = Result<(), SendError<T>>; | 281 | type Output = Result<(), SendError<T>>; |
| 282 | 282 | ||
| @@ -295,18 +295,18 @@ where | |||
| 295 | } | 295 | } |
| 296 | } | 296 | } |
| 297 | 297 | ||
| 298 | impl<'ch, M, T, const N: usize> Unpin for SendFuture<'ch, M, T, N> where M: MutexKind {} | 298 | impl<'ch, M, T, const N: usize> Unpin for SendFuture<'ch, M, T, N> where M: RawMutex {} |
| 299 | 299 | ||
| 300 | struct CloseFuture<'ch, M, T, const N: usize> | 300 | struct CloseFuture<'ch, M, T, const N: usize> |
| 301 | where | 301 | where |
| 302 | M: MutexKind, | 302 | M: RawMutex, |
| 303 | { | 303 | { |
| 304 | channel: &'ch Channel<M, T, N>, | 304 | channel: &'ch Channel<M, T, N>, |
| 305 | } | 305 | } |
| 306 | 306 | ||
| 307 | impl<'ch, M, T, const N: usize> Future for CloseFuture<'ch, M, T, N> | 307 | impl<'ch, M, T, const N: usize> Future for CloseFuture<'ch, M, T, N> |
| 308 | where | 308 | where |
| 309 | M: MutexKind, | 309 | M: RawMutex, |
| 310 | { | 310 | { |
| 311 | type Output = (); | 311 | type Output = (); |
| 312 | 312 | ||
| @@ -321,7 +321,7 @@ where | |||
| 321 | 321 | ||
| 322 | impl<'ch, M, T, const N: usize> Drop for Sender<'ch, M, T, N> | 322 | impl<'ch, M, T, const N: usize> Drop for Sender<'ch, M, T, N> |
| 323 | where | 323 | where |
| 324 | M: MutexKind, | 324 | M: RawMutex, |
| 325 | { | 325 | { |
| 326 | fn drop(&mut self) { | 326 | fn drop(&mut self) { |
| 327 | self.channel.lock(|c| c.deregister_sender()) | 327 | self.channel.lock(|c| c.deregister_sender()) |
| @@ -330,7 +330,7 @@ where | |||
| 330 | 330 | ||
| 331 | impl<'ch, M, T, const N: usize> Clone for Sender<'ch, M, T, N> | 331 | impl<'ch, M, T, const N: usize> Clone for Sender<'ch, M, T, N> |
| 332 | where | 332 | where |
| 333 | M: MutexKind, | 333 | M: RawMutex, |
| 334 | { | 334 | { |
| 335 | fn clone(&self) -> Self { | 335 | fn clone(&self) -> Self { |
| 336 | self.channel.lock(|c| c.register_sender()); | 336 | self.channel.lock(|c| c.register_sender()); |
| @@ -546,30 +546,50 @@ impl<T, const N: usize> ChannelState<T, N> { | |||
| 546 | /// All data sent will become available in the same order as it was sent. | 546 | /// All data sent will become available in the same order as it was sent. |
| 547 | pub struct Channel<M, T, const N: usize> | 547 | pub struct Channel<M, T, const N: usize> |
| 548 | where | 548 | where |
| 549 | M: MutexKind, | 549 | M: RawMutex, |
| 550 | { | 550 | { |
| 551 | inner: M::Mutex<RefCell<ChannelState<T, N>>>, | 551 | inner: Mutex<M, RefCell<ChannelState<T, N>>>, |
| 552 | } | 552 | } |
| 553 | 553 | ||
| 554 | impl<M, T, const N: usize> Channel<M, T, N> | 554 | impl<M, T, const N: usize> Channel<M, T, N> |
| 555 | where | 555 | where |
| 556 | M: MutexKind, | 556 | M: RawMutex, |
| 557 | { | 557 | { |
| 558 | /// Establish a new bounded channel. For example, to create one with a NoopMutex: | 558 | /// Establish a new bounded channel. For example, to create one with a NoopMutex: |
| 559 | /// | 559 | /// |
| 560 | /// ``` | 560 | /// ``` |
| 561 | /// use embassy::channel::mpsc; | 561 | /// use embassy::channel::mpsc; |
| 562 | /// use embassy::blocking_mutex::kind::Noop; | 562 | /// use embassy::blocking_mutex::raw::NoopRawMutex; |
| 563 | /// use embassy::channel::mpsc::Channel; | 563 | /// use embassy::channel::mpsc::Channel; |
| 564 | /// | 564 | /// |
| 565 | /// // Declare a bounded channel of 3 u32s. | 565 | /// // Declare a bounded channel of 3 u32s. |
| 566 | /// let mut channel = Channel::<Noop, u32, 3>::new(); | 566 | /// let mut channel = Channel::<NoopRawMutex, u32, 3>::new(); |
| 567 | /// // once we have a channel, obtain its sender and receiver | 567 | /// // once we have a channel, obtain its sender and receiver |
| 568 | /// let (sender, receiver) = mpsc::split(&mut channel); | 568 | /// let (sender, receiver) = mpsc::split(&mut channel); |
| 569 | /// ``` | 569 | /// ``` |
| 570 | #[cfg(feature = "nightly")] | ||
| 571 | pub const fn new() -> Self { | ||
| 572 | Self { | ||
| 573 | inner: Mutex::new(RefCell::new(ChannelState::new())), | ||
| 574 | } | ||
| 575 | } | ||
| 576 | |||
| 577 | /// Establish a new bounded channel. For example, to create one with a NoopMutex: | ||
| 578 | /// | ||
| 579 | /// ``` | ||
| 580 | /// use embassy::channel::mpsc; | ||
| 581 | /// use embassy::blocking_mutex::raw::NoopRawMutex; | ||
| 582 | /// use embassy::channel::mpsc::Channel; | ||
| 583 | /// | ||
| 584 | /// // Declare a bounded channel of 3 u32s. | ||
| 585 | /// let mut channel = Channel::<NoopRawMutex, u32, 3>::new(); | ||
| 586 | /// // once we have a channel, obtain its sender and receiver | ||
| 587 | /// let (sender, receiver) = mpsc::split(&mut channel); | ||
| 588 | /// ``` | ||
| 589 | #[cfg(not(feature = "nightly"))] | ||
| 570 | pub fn new() -> Self { | 590 | pub fn new() -> Self { |
| 571 | Self { | 591 | Self { |
| 572 | inner: M::Mutex::new(RefCell::new(ChannelState::new())), | 592 | inner: Mutex::new(RefCell::new(ChannelState::new())), |
| 573 | } | 593 | } |
| 574 | } | 594 | } |
| 575 | 595 | ||
| @@ -586,7 +606,7 @@ mod tests { | |||
| 586 | use futures_executor::ThreadPool; | 606 | use futures_executor::ThreadPool; |
| 587 | use futures_timer::Delay; | 607 | use futures_timer::Delay; |
| 588 | 608 | ||
| 589 | use crate::blocking_mutex::kind::{CriticalSection, Noop}; | 609 | use crate::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; |
| 590 | use crate::util::Forever; | 610 | use crate::util::Forever; |
| 591 | 611 | ||
| 592 | use super::*; | 612 | use super::*; |
| @@ -655,7 +675,7 @@ mod tests { | |||
| 655 | 675 | ||
| 656 | #[test] | 676 | #[test] |
| 657 | fn simple_send_and_receive() { | 677 | fn simple_send_and_receive() { |
| 658 | let mut c = Channel::<Noop, u32, 3>::new(); | 678 | let mut c = Channel::<NoopRawMutex, u32, 3>::new(); |
| 659 | let (s, r) = split(&mut c); | 679 | let (s, r) = split(&mut c); |
| 660 | assert!(s.clone().try_send(1).is_ok()); | 680 | assert!(s.clone().try_send(1).is_ok()); |
| 661 | assert_eq!(r.try_recv().unwrap(), 1); | 681 | assert_eq!(r.try_recv().unwrap(), 1); |
| @@ -663,7 +683,7 @@ mod tests { | |||
| 663 | 683 | ||
| 664 | #[test] | 684 | #[test] |
| 665 | fn should_close_without_sender() { | 685 | fn should_close_without_sender() { |
| 666 | let mut c = Channel::<Noop, u32, 3>::new(); | 686 | let mut c = Channel::<NoopRawMutex, u32, 3>::new(); |
| 667 | let (s, r) = split(&mut c); | 687 | let (s, r) = split(&mut c); |
| 668 | drop(s); | 688 | drop(s); |
| 669 | match r.try_recv() { | 689 | match r.try_recv() { |
| @@ -674,7 +694,7 @@ mod tests { | |||
| 674 | 694 | ||
| 675 | #[test] | 695 | #[test] |
| 676 | fn should_close_once_drained() { | 696 | fn should_close_once_drained() { |
| 677 | let mut c = Channel::<Noop, u32, 3>::new(); | 697 | let mut c = Channel::<NoopRawMutex, u32, 3>::new(); |
| 678 | let (s, r) = split(&mut c); | 698 | let (s, r) = split(&mut c); |
| 679 | assert!(s.try_send(1).is_ok()); | 699 | assert!(s.try_send(1).is_ok()); |
| 680 | drop(s); | 700 | drop(s); |
| @@ -687,7 +707,7 @@ mod tests { | |||
| 687 | 707 | ||
| 688 | #[test] | 708 | #[test] |
| 689 | fn should_reject_send_when_receiver_dropped() { | 709 | fn should_reject_send_when_receiver_dropped() { |
| 690 | let mut c = Channel::<Noop, u32, 3>::new(); | 710 | let mut c = Channel::<NoopRawMutex, u32, 3>::new(); |
| 691 | let (s, r) = split(&mut c); | 711 | let (s, r) = split(&mut c); |
| 692 | drop(r); | 712 | drop(r); |
| 693 | match s.try_send(1) { | 713 | match s.try_send(1) { |
| @@ -698,7 +718,7 @@ mod tests { | |||
| 698 | 718 | ||
| 699 | #[test] | 719 | #[test] |
| 700 | fn should_reject_send_when_channel_closed() { | 720 | fn should_reject_send_when_channel_closed() { |
| 701 | let mut c = Channel::<Noop, u32, 3>::new(); | 721 | let mut c = Channel::<NoopRawMutex, u32, 3>::new(); |
| 702 | let (s, mut r) = split(&mut c); | 722 | let (s, mut r) = split(&mut c); |
| 703 | assert!(s.try_send(1).is_ok()); | 723 | assert!(s.try_send(1).is_ok()); |
| 704 | r.close(); | 724 | r.close(); |
| @@ -714,7 +734,7 @@ mod tests { | |||
| 714 | async fn receiver_closes_when_sender_dropped_async() { | 734 | async fn receiver_closes_when_sender_dropped_async() { |
| 715 | let executor = ThreadPool::new().unwrap(); | 735 | let executor = ThreadPool::new().unwrap(); |
| 716 | 736 | ||
| 717 | static CHANNEL: Forever<Channel<CriticalSection, u32, 3>> = Forever::new(); | 737 | static CHANNEL: Forever<Channel<CriticalSectionRawMutex, u32, 3>> = Forever::new(); |
| 718 | let c = CHANNEL.put(Channel::new()); | 738 | let c = CHANNEL.put(Channel::new()); |
| 719 | let (s, mut r) = split(c); | 739 | let (s, mut r) = split(c); |
| 720 | assert!(executor | 740 | assert!(executor |
| @@ -729,7 +749,7 @@ mod tests { | |||
| 729 | async fn receiver_receives_given_try_send_async() { | 749 | async fn receiver_receives_given_try_send_async() { |
| 730 | let executor = ThreadPool::new().unwrap(); | 750 | let executor = ThreadPool::new().unwrap(); |
| 731 | 751 | ||
| 732 | static CHANNEL: Forever<Channel<CriticalSection, u32, 3>> = Forever::new(); | 752 | static CHANNEL: Forever<Channel<CriticalSectionRawMutex, u32, 3>> = Forever::new(); |
| 733 | let c = CHANNEL.put(Channel::new()); | 753 | let c = CHANNEL.put(Channel::new()); |
| 734 | let (s, mut r) = split(c); | 754 | let (s, mut r) = split(c); |
| 735 | assert!(executor | 755 | assert!(executor |
| @@ -742,7 +762,7 @@ mod tests { | |||
| 742 | 762 | ||
| 743 | #[futures_test::test] | 763 | #[futures_test::test] |
| 744 | async fn sender_send_completes_if_capacity() { | 764 | async fn sender_send_completes_if_capacity() { |
| 745 | let mut c = Channel::<CriticalSection, u32, 1>::new(); | 765 | let mut c = Channel::<CriticalSectionRawMutex, u32, 1>::new(); |
| 746 | let (s, mut r) = split(&mut c); | 766 | let (s, mut r) = split(&mut c); |
| 747 | assert!(s.send(1).await.is_ok()); | 767 | assert!(s.send(1).await.is_ok()); |
| 748 | assert_eq!(r.recv().await, Some(1)); | 768 | assert_eq!(r.recv().await, Some(1)); |
| @@ -750,7 +770,7 @@ mod tests { | |||
| 750 | 770 | ||
| 751 | #[futures_test::test] | 771 | #[futures_test::test] |
| 752 | async fn sender_send_completes_if_closed() { | 772 | async fn sender_send_completes_if_closed() { |
| 753 | static CHANNEL: Forever<Channel<CriticalSection, u32, 1>> = Forever::new(); | 773 | static CHANNEL: Forever<Channel<CriticalSectionRawMutex, u32, 1>> = Forever::new(); |
| 754 | let c = CHANNEL.put(Channel::new()); | 774 | let c = CHANNEL.put(Channel::new()); |
| 755 | let (s, r) = split(c); | 775 | let (s, r) = split(c); |
| 756 | drop(r); | 776 | drop(r); |
| @@ -764,7 +784,7 @@ mod tests { | |||
| 764 | async fn senders_sends_wait_until_capacity() { | 784 | async fn senders_sends_wait_until_capacity() { |
| 765 | let executor = ThreadPool::new().unwrap(); | 785 | let executor = ThreadPool::new().unwrap(); |
| 766 | 786 | ||
| 767 | static CHANNEL: Forever<Channel<CriticalSection, u32, 1>> = Forever::new(); | 787 | static CHANNEL: Forever<Channel<CriticalSectionRawMutex, u32, 1>> = Forever::new(); |
| 768 | let c = CHANNEL.put(Channel::new()); | 788 | let c = CHANNEL.put(Channel::new()); |
| 769 | let (s0, mut r) = split(c); | 789 | let (s0, mut r) = split(c); |
| 770 | assert!(s0.try_send(1).is_ok()); | 790 | assert!(s0.try_send(1).is_ok()); |
| @@ -784,7 +804,7 @@ mod tests { | |||
| 784 | 804 | ||
| 785 | #[futures_test::test] | 805 | #[futures_test::test] |
| 786 | async fn sender_close_completes_if_closing() { | 806 | async fn sender_close_completes_if_closing() { |
| 787 | static CHANNEL: Forever<Channel<CriticalSection, u32, 1>> = Forever::new(); | 807 | static CHANNEL: Forever<Channel<CriticalSectionRawMutex, u32, 1>> = Forever::new(); |
| 788 | let c = CHANNEL.put(Channel::new()); | 808 | let c = CHANNEL.put(Channel::new()); |
| 789 | let (s, mut r) = split(c); | 809 | let (s, mut r) = split(c); |
| 790 | r.close(); | 810 | r.close(); |
| @@ -793,7 +813,7 @@ mod tests { | |||
| 793 | 813 | ||
| 794 | #[futures_test::test] | 814 | #[futures_test::test] |
| 795 | async fn sender_close_completes_if_closed() { | 815 | async fn sender_close_completes_if_closed() { |
| 796 | static CHANNEL: Forever<Channel<CriticalSection, u32, 1>> = Forever::new(); | 816 | static CHANNEL: Forever<Channel<CriticalSectionRawMutex, u32, 1>> = Forever::new(); |
| 797 | let c = CHANNEL.put(Channel::new()); | 817 | let c = CHANNEL.put(Channel::new()); |
| 798 | let (s, r) = split(c); | 818 | let (s, r) = split(c); |
| 799 | drop(r); | 819 | drop(r); |
diff --git a/embassy/src/channel/signal.rs b/embassy/src/channel/signal.rs index 87922b2fd..027f4f47c 100644 --- a/embassy/src/channel/signal.rs +++ b/embassy/src/channel/signal.rs | |||
| @@ -32,13 +32,15 @@ enum State<T> { | |||
| 32 | unsafe impl<T: Send> Send for Signal<T> {} | 32 | unsafe impl<T: Send> Send for Signal<T> {} |
| 33 | unsafe impl<T: Send> Sync for Signal<T> {} | 33 | unsafe impl<T: Send> Sync for Signal<T> {} |
| 34 | 34 | ||
| 35 | impl<T: Send> Signal<T> { | 35 | impl<T> Signal<T> { |
| 36 | pub const fn new() -> Self { | 36 | pub const fn new() -> Self { |
| 37 | Self { | 37 | Self { |
| 38 | state: UnsafeCell::new(State::None), | 38 | state: UnsafeCell::new(State::None), |
| 39 | } | 39 | } |
| 40 | } | 40 | } |
| 41 | } | ||
| 41 | 42 | ||
| 43 | impl<T: Send> Signal<T> { | ||
| 42 | /// Mark this Signal as completed. | 44 | /// Mark this Signal as completed. |
| 43 | pub fn signal(&self, val: T) { | 45 | pub fn signal(&self, val: T) { |
| 44 | critical_section::with(|_| unsafe { | 46 | critical_section::with(|_| unsafe { |
diff --git a/embassy/src/executor/raw/mod.rs b/embassy/src/executor/raw/mod.rs index 5ee38715d..3ae52ae31 100644 --- a/embassy/src/executor/raw/mod.rs +++ b/embassy/src/executor/raw/mod.rs | |||
| @@ -57,6 +57,7 @@ pub struct TaskHeader { | |||
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | impl TaskHeader { | 59 | impl TaskHeader { |
| 60 | #[cfg(feature = "nightly")] | ||
| 60 | pub(crate) const fn new() -> Self { | 61 | pub(crate) const fn new() -> Self { |
| 61 | Self { | 62 | Self { |
| 62 | state: AtomicU32::new(0), | 63 | state: AtomicU32::new(0), |
| @@ -71,6 +72,21 @@ impl TaskHeader { | |||
| 71 | } | 72 | } |
| 72 | } | 73 | } |
| 73 | 74 | ||
| 75 | #[cfg(not(feature = "nightly"))] | ||
| 76 | pub(crate) fn new() -> Self { | ||
| 77 | Self { | ||
| 78 | state: AtomicU32::new(0), | ||
| 79 | run_queue_item: RunQueueItem::new(), | ||
| 80 | executor: Cell::new(ptr::null()), | ||
| 81 | poll_fn: UninitCell::uninit(), | ||
| 82 | |||
| 83 | #[cfg(feature = "time")] | ||
| 84 | expires_at: Cell::new(Instant::from_ticks(0)), | ||
| 85 | #[cfg(feature = "time")] | ||
| 86 | timer_queue_item: timer_queue::TimerQueueItem::new(), | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 74 | pub(crate) unsafe fn enqueue(&self) { | 90 | pub(crate) unsafe fn enqueue(&self) { |
| 75 | critical_section::with(|cs| { | 91 | critical_section::with(|cs| { |
| 76 | let state = self.state.load(Ordering::Relaxed); | 92 | let state = self.state.load(Ordering::Relaxed); |
| @@ -113,7 +129,8 @@ pub struct TaskStorage<F: Future + 'static> { | |||
| 113 | } | 129 | } |
| 114 | 130 | ||
| 115 | impl<F: Future + 'static> TaskStorage<F> { | 131 | impl<F: Future + 'static> TaskStorage<F> { |
| 116 | /// Create a new Task, in not-spawned state. | 132 | /// Create a new TaskStorage, in not-spawned state. |
| 133 | #[cfg(feature = "nightly")] | ||
| 117 | pub const fn new() -> Self { | 134 | pub const fn new() -> Self { |
| 118 | Self { | 135 | Self { |
| 119 | raw: TaskHeader::new(), | 136 | raw: TaskHeader::new(), |
| @@ -121,6 +138,15 @@ impl<F: Future + 'static> TaskStorage<F> { | |||
| 121 | } | 138 | } |
| 122 | } | 139 | } |
| 123 | 140 | ||
| 141 | /// Create a new TaskStorage, in not-spawned state. | ||
| 142 | #[cfg(not(feature = "nightly"))] | ||
| 143 | pub fn new() -> Self { | ||
| 144 | Self { | ||
| 145 | raw: TaskHeader::new(), | ||
| 146 | future: UninitCell::uninit(), | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 124 | /// Try to spawn a task in a pool. | 150 | /// Try to spawn a task in a pool. |
| 125 | /// | 151 | /// |
| 126 | /// See [`Self::spawn()`] for details. | 152 | /// See [`Self::spawn()`] for details. |
diff --git a/embassy/src/lib.rs b/embassy/src/lib.rs index 2be0e0052..acc71e105 100644 --- a/embassy/src/lib.rs +++ b/embassy/src/lib.rs | |||
| @@ -1,8 +1,13 @@ | |||
| 1 | #![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] | 1 | #![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] |
| 2 | #![feature(generic_associated_types)] | 2 | #![cfg_attr( |
| 3 | #![feature(const_fn_trait_bound)] | 3 | feature = "nightly", |
| 4 | #![feature(const_fn_fn_ptr_basics)] | 4 | feature( |
| 5 | #![feature(type_alias_impl_trait)] | 5 | const_fn_trait_bound, |
| 6 | const_fn_fn_ptr_basics, | ||
| 7 | generic_associated_types, | ||
| 8 | type_alias_impl_trait | ||
| 9 | ) | ||
| 10 | )] | ||
| 6 | #![allow(clippy::new_without_default)] | 11 | #![allow(clippy::new_without_default)] |
| 7 | 12 | ||
| 8 | // This mod MUST go first, so that the others see its macros. | 13 | // This mod MUST go first, so that the others see its macros. |
| @@ -20,7 +25,8 @@ pub mod io; | |||
| 20 | pub mod time; | 25 | pub mod time; |
| 21 | pub mod util; | 26 | pub mod util; |
| 22 | 27 | ||
| 23 | pub use embassy_macros::*; | 28 | #[cfg(feature = "nightly")] |
| 29 | pub use embassy_macros::{main, task}; | ||
| 24 | 30 | ||
| 25 | #[doc(hidden)] | 31 | #[doc(hidden)] |
| 26 | /// Implementation details for embassy macros. DO NOT USE. | 32 | /// Implementation details for embassy macros. DO NOT USE. |
diff --git a/embassy/src/time/delay.rs b/embassy/src/time/delay.rs index ff32941ee..27ec61fe6 100644 --- a/embassy/src/time/delay.rs +++ b/embassy/src/time/delay.rs | |||
| @@ -16,11 +16,7 @@ pub struct Delay; | |||
| 16 | 16 | ||
| 17 | #[cfg(feature = "unstable-traits")] | 17 | #[cfg(feature = "unstable-traits")] |
| 18 | mod eh1 { | 18 | mod eh1 { |
| 19 | use core::future::Future; | ||
| 20 | use futures::FutureExt; | ||
| 21 | |||
| 22 | use super::*; | 19 | use super::*; |
| 23 | use crate::time::Timer; | ||
| 24 | 20 | ||
| 25 | impl embedded_hal_1::delay::blocking::DelayUs for Delay { | 21 | impl embedded_hal_1::delay::blocking::DelayUs for Delay { |
| 26 | type Error = core::convert::Infallible; | 22 | type Error = core::convert::Infallible; |
| @@ -33,6 +29,14 @@ mod eh1 { | |||
| 33 | Ok(block_for(Duration::from_millis(ms as u64))) | 29 | Ok(block_for(Duration::from_millis(ms as u64))) |
| 34 | } | 30 | } |
| 35 | } | 31 | } |
| 32 | } | ||
| 33 | |||
| 34 | #[cfg(all(feature = "unstable-traits", feature = "nightly"))] | ||
| 35 | mod eh1a { | ||
| 36 | use super::*; | ||
| 37 | use crate::time::Timer; | ||
| 38 | use core::future::Future; | ||
| 39 | use futures::FutureExt; | ||
| 36 | 40 | ||
| 37 | impl embedded_hal_async::delay::DelayUs for Delay { | 41 | impl embedded_hal_async::delay::DelayUs for Delay { |
| 38 | type Error = core::convert::Infallible; | 42 | type Error = core::convert::Infallible; |
diff --git a/embassy/src/waitqueue/waker_agnostic.rs b/embassy/src/waitqueue/waker_agnostic.rs index f583fa6f4..89430aa4c 100644 --- a/embassy/src/waitqueue/waker_agnostic.rs +++ b/embassy/src/waitqueue/waker_agnostic.rs | |||
| @@ -2,7 +2,8 @@ use core::cell::Cell; | |||
| 2 | use core::mem; | 2 | use core::mem; |
| 3 | use core::task::Waker; | 3 | use core::task::Waker; |
| 4 | 4 | ||
| 5 | use crate::blocking_mutex::CriticalSectionMutex as Mutex; | 5 | use crate::blocking_mutex::raw::CriticalSectionRawMutex; |
| 6 | use crate::blocking_mutex::Mutex; | ||
| 6 | 7 | ||
| 7 | /// Utility struct to register and wake a waker. | 8 | /// Utility struct to register and wake a waker. |
| 8 | #[derive(Debug)] | 9 | #[derive(Debug)] |
| @@ -50,13 +51,13 @@ impl WakerRegistration { | |||
| 50 | 51 | ||
| 51 | /// Utility struct to register and wake a waker. | 52 | /// Utility struct to register and wake a waker. |
| 52 | pub struct AtomicWaker { | 53 | pub struct AtomicWaker { |
| 53 | waker: Mutex<Cell<Option<Waker>>>, | 54 | waker: Mutex<CriticalSectionRawMutex, Cell<Option<Waker>>>, |
| 54 | } | 55 | } |
| 55 | 56 | ||
| 56 | impl AtomicWaker { | 57 | impl AtomicWaker { |
| 57 | pub const fn new() -> Self { | 58 | pub const fn new() -> Self { |
| 58 | Self { | 59 | Self { |
| 59 | waker: Mutex::new(Cell::new(None)), | 60 | waker: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), |
| 60 | } | 61 | } |
| 61 | } | 62 | } |
| 62 | 63 | ||
diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index da16bcbaf..2d9c99530 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml | |||
| @@ -4,6 +4,9 @@ edition = "2018" | |||
| 4 | name = "embassy-nrf-examples" | 4 | name = "embassy-nrf-examples" |
| 5 | version = "0.1.0" | 5 | version = "0.1.0" |
| 6 | 6 | ||
| 7 | [features] | ||
| 8 | default = ["nightly"] | ||
| 9 | nightly = ["embassy-nrf/nightly"] | ||
| 7 | 10 | ||
| 8 | [dependencies] | 11 | [dependencies] |
| 9 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt"] } | 12 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt"] } |
diff --git a/examples/nrf/src/bin/mpsc.rs b/examples/nrf/src/bin/mpsc.rs index 454fb9541..d50736d82 100644 --- a/examples/nrf/src/bin/mpsc.rs +++ b/examples/nrf/src/bin/mpsc.rs | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | mod example_common; | 6 | mod example_common; |
| 7 | 7 | ||
| 8 | use defmt::unwrap; | 8 | use defmt::unwrap; |
| 9 | use embassy::blocking_mutex::kind::Noop; | 9 | use embassy::blocking_mutex::raw::NoopRawMutex; |
| 10 | use embassy::channel::mpsc::{self, Channel, Sender, TryRecvError}; | 10 | use embassy::channel::mpsc::{self, Channel, Sender, TryRecvError}; |
| 11 | use embassy::executor::Spawner; | 11 | use embassy::executor::Spawner; |
| 12 | use embassy::time::{Duration, Timer}; | 12 | use embassy::time::{Duration, Timer}; |
| @@ -19,10 +19,10 @@ enum LedState { | |||
| 19 | Off, | 19 | Off, |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | static CHANNEL: Forever<Channel<Noop, LedState, 1>> = Forever::new(); | 22 | static CHANNEL: Forever<Channel<NoopRawMutex, LedState, 1>> = Forever::new(); |
| 23 | 23 | ||
| 24 | #[embassy::task(pool_size = 1)] | 24 | #[embassy::task(pool_size = 1)] |
| 25 | async fn my_task(sender: Sender<'static, Noop, LedState, 1>) { | 25 | async fn my_task(sender: Sender<'static, NoopRawMutex, LedState, 1>) { |
| 26 | loop { | 26 | loop { |
| 27 | let _ = sender.send(LedState::On).await; | 27 | let _ = sender.send(LedState::On).await; |
| 28 | Timer::after(Duration::from_secs(1)).await; | 28 | Timer::after(Duration::from_secs(1)).await; |
diff --git a/examples/nrf/src/bin/uart_split.rs b/examples/nrf/src/bin/uart_split.rs index 750798378..19d438c40 100644 --- a/examples/nrf/src/bin/uart_split.rs +++ b/examples/nrf/src/bin/uart_split.rs | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | mod example_common; | 6 | mod example_common; |
| 7 | use example_common::*; | 7 | use example_common::*; |
| 8 | 8 | ||
| 9 | use embassy::blocking_mutex::kind::Noop; | 9 | use embassy::blocking_mutex::raw::NoopRawMutex; |
| 10 | use embassy::channel::mpsc::{self, Channel, Sender}; | 10 | use embassy::channel::mpsc::{self, Channel, Sender}; |
| 11 | use embassy::executor::Spawner; | 11 | use embassy::executor::Spawner; |
| 12 | use embassy::util::Forever; | 12 | use embassy::util::Forever; |
| @@ -14,7 +14,7 @@ use embassy_nrf::peripherals::UARTE0; | |||
| 14 | use embassy_nrf::uarte::UarteRx; | 14 | use embassy_nrf::uarte::UarteRx; |
| 15 | use embassy_nrf::{interrupt, uarte, Peripherals}; | 15 | use embassy_nrf::{interrupt, uarte, Peripherals}; |
| 16 | 16 | ||
| 17 | static CHANNEL: Forever<Channel<Noop, [u8; 8], 1>> = Forever::new(); | 17 | static CHANNEL: Forever<Channel<NoopRawMutex, [u8; 8], 1>> = Forever::new(); |
| 18 | 18 | ||
| 19 | #[embassy::main] | 19 | #[embassy::main] |
| 20 | async fn main(spawner: Spawner, p: Peripherals) { | 20 | async fn main(spawner: Spawner, p: Peripherals) { |
| @@ -56,7 +56,7 @@ async fn main(spawner: Spawner, p: Peripherals) { | |||
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | #[embassy::task] | 58 | #[embassy::task] |
| 59 | async fn reader(mut rx: UarteRx<'static, UARTE0>, s: Sender<'static, Noop, [u8; 8], 1>) { | 59 | async fn reader(mut rx: UarteRx<'static, UARTE0>, s: Sender<'static, NoopRawMutex, [u8; 8], 1>) { |
| 60 | let mut buf = [0; 8]; | 60 | let mut buf = [0; 8]; |
| 61 | loop { | 61 | loop { |
| 62 | info!("reading..."); | 62 | info!("reading..."); |
diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 450911fa1..ef60fe992 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml | |||
| @@ -5,7 +5,7 @@ name = "embassy-std-examples" | |||
| 5 | version = "0.1.0" | 5 | version = "0.1.0" |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy = { version = "0.1.0", path = "../../embassy", features = ["log", "std", "time"] } | 8 | embassy = { version = "0.1.0", path = "../../embassy", features = ["log", "std", "time", "nightly"] } |
| 9 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features=["std", "log", "medium-ethernet", "tcp", "dhcpv4"] } | 9 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features=["std", "log", "medium-ethernet", "tcp", "dhcpv4"] } |
| 10 | 10 | ||
| 11 | async-io = "1.6.0" | 11 | async-io = "1.6.0" |
diff --git a/examples/stm32f3/src/bin/button_events.rs b/examples/stm32f3/src/bin/button_events.rs index 720ed9d11..1218edd2b 100644 --- a/examples/stm32f3/src/bin/button_events.rs +++ b/examples/stm32f3/src/bin/button_events.rs | |||
| @@ -12,7 +12,7 @@ | |||
| 12 | 12 | ||
| 13 | #[path = "../example_common.rs"] | 13 | #[path = "../example_common.rs"] |
| 14 | mod example_common; | 14 | mod example_common; |
| 15 | use embassy::blocking_mutex::kind::Noop; | 15 | use embassy::blocking_mutex::raw::NoopRawMutex; |
| 16 | use embassy::channel::mpsc::{self, Channel, Receiver, Sender}; | 16 | use embassy::channel::mpsc::{self, Channel, Receiver, Sender}; |
| 17 | use embassy::executor::Spawner; | 17 | use embassy::executor::Spawner; |
| 18 | use embassy::time::{with_timeout, Duration, Timer}; | 18 | use embassy::time::{with_timeout, Duration, Timer}; |
| @@ -77,7 +77,7 @@ enum ButtonEvent { | |||
| 77 | Hold, | 77 | Hold, |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | static BUTTON_EVENTS_QUEUE: Forever<Channel<Noop, ButtonEvent, 4>> = Forever::new(); | 80 | static BUTTON_EVENTS_QUEUE: Forever<Channel<NoopRawMutex, ButtonEvent, 4>> = Forever::new(); |
| 81 | 81 | ||
| 82 | #[embassy::main] | 82 | #[embassy::main] |
| 83 | async fn main(spawner: Spawner, p: Peripherals) { | 83 | async fn main(spawner: Spawner, p: Peripherals) { |
| @@ -103,7 +103,10 @@ async fn main(spawner: Spawner, p: Peripherals) { | |||
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | #[embassy::task] | 105 | #[embassy::task] |
| 106 | async fn led_blinker(mut leds: Leds<'static>, queue: Receiver<'static, Noop, ButtonEvent, 4>) { | 106 | async fn led_blinker( |
| 107 | mut leds: Leds<'static>, | ||
| 108 | queue: Receiver<'static, NoopRawMutex, ButtonEvent, 4>, | ||
| 109 | ) { | ||
| 107 | loop { | 110 | loop { |
| 108 | leds.blink().await; | 111 | leds.blink().await; |
| 109 | match queue.try_recv() { | 112 | match queue.try_recv() { |
| @@ -121,7 +124,7 @@ async fn led_blinker(mut leds: Leds<'static>, queue: Receiver<'static, Noop, But | |||
| 121 | #[embassy::task] | 124 | #[embassy::task] |
| 122 | async fn button_waiter( | 125 | async fn button_waiter( |
| 123 | mut button: ExtiInput<'static, PA0>, | 126 | mut button: ExtiInput<'static, PA0>, |
| 124 | queue: Sender<'static, Noop, ButtonEvent, 4>, | 127 | queue: Sender<'static, NoopRawMutex, ButtonEvent, 4>, |
| 125 | ) { | 128 | ) { |
| 126 | const DOUBLE_CLICK_DELAY: u64 = 250; | 129 | const DOUBLE_CLICK_DELAY: u64 = 250; |
| 127 | const HOLD_DELAY: u64 = 1000; | 130 | const HOLD_DELAY: u64 = 1000; |
diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index c6218a805..6750f6a6c 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml | |||
| @@ -8,7 +8,7 @@ version = "0.1.0" | |||
| 8 | crate-type = ["cdylib"] | 8 | crate-type = ["cdylib"] |
| 9 | 9 | ||
| 10 | [dependencies] | 10 | [dependencies] |
| 11 | embassy = { version = "0.1.0", path = "../../embassy", features = ["log", "wasm"] } | 11 | embassy = { version = "0.1.0", path = "../../embassy", features = ["log", "wasm", "nightly"] } |
| 12 | 12 | ||
| 13 | wasm-logger = "0.2.0" | 13 | wasm-logger = "0.2.0" |
| 14 | wasm-bindgen = "0.2" | 14 | wasm-bindgen = "0.2" |
