Merge #813
813: Document remaining public APIs in embassy crate r=lulf a=lulf This also includes the README.md in the toplevel crate documentation, fixing a few formatting issues with it as well. Please review the mutex in detail in case I've misunderstood/missed a few things there. Co-authored-by: Ulf Lilleengen <lulf@redhat.com>
This commit is contained in:
commit
23177ba7eb
17
README.md
17
README.md
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Embassy is the next-generation framework for embedded applications. Write safe, correct and energy-efficient embedded code faster, using the Rust programming language, its async facilities, and the Embassy libraries.
|
Embassy is the next-generation framework for embedded applications. Write safe, correct and energy-efficient embedded code faster, using the Rust programming language, its async facilities, and the Embassy libraries.
|
||||||
|
|
||||||
## [Documentation](https://embassy.dev/embassy/dev/index.html) - [API reference](https://docs.embassy.dev/) - [Website](https://embassy.dev/) - [Chat](https://matrix.to/#/#embassy-rs:matrix.org)
|
## <a href="https://embassy.dev/embassy/dev/index.html">Documentation</a> - <a href="https://docs.embassy.dev/">API reference</a> - <a href="https://embassy.dev/">Website</a> - <a href="https://matrix.to/#/#embassy-rs:matrix.org">Chat</a>
|
||||||
## Rust + async ❤️ embedded
|
## Rust + async ❤️ embedded
|
||||||
|
|
||||||
The Rust programming language is blazingly fast and memory-efficient, with no runtime, garbage collector or OS. It catches a wide variety of bugs at compile time, thanks to its full memory- and thread-safety, and expressive type system.
|
The Rust programming language is blazingly fast and memory-efficient, with no runtime, garbage collector or OS. It catches a wide variety of bugs at compile time, thanks to its full memory- and thread-safety, and expressive type system.
|
||||||
@ -42,7 +42,7 @@ The <a href="https://github.com/embassy-rs/nrf-softdevice">nrf-softdevice</a> cr
|
|||||||
|
|
||||||
## Sneak peek
|
## Sneak peek
|
||||||
|
|
||||||
```rust
|
```rust,ignore
|
||||||
use defmt::info;
|
use defmt::info;
|
||||||
use embassy::executor::Spawner;
|
use embassy::executor::Spawner;
|
||||||
use embassy::time::{Duration, Timer};
|
use embassy::time::{Duration, Timer};
|
||||||
@ -93,20 +93,21 @@ Examples are found in the `examples/` folder seperated by the chip manufacturer
|
|||||||
### Running examples
|
### Running examples
|
||||||
|
|
||||||
- Setup git submodules (needed for STM32 examples)
|
- Setup git submodules (needed for STM32 examples)
|
||||||
```
|
|
||||||
|
```bash
|
||||||
git submodule init
|
git submodule init
|
||||||
git submodule update
|
git submodule update
|
||||||
```
|
```
|
||||||
|
|
||||||
- Install `probe-run` with defmt support.
|
- Install `probe-run` with defmt support.
|
||||||
|
|
||||||
```
|
```bash
|
||||||
cargo install probe-run
|
cargo install probe-run
|
||||||
```
|
```
|
||||||
|
|
||||||
- Change directory to the sample's base directory. For example:
|
- Change directory to the sample's base directory. For example:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
cd examples/nrf
|
cd examples/nrf
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -114,7 +115,7 @@ cd examples/nrf
|
|||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
cargo run --bin blinky
|
cargo run --bin blinky
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -145,8 +146,8 @@ EMBedded ASYnc! :)
|
|||||||
This work is licensed under either of
|
This work is licensed under either of
|
||||||
|
|
||||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
http://www.apache.org/licenses/LICENSE-2.0)
|
<http://www.apache.org/licenses/LICENSE-2.0>)
|
||||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
||||||
|
|
||||||
at your option.
|
at your option.
|
||||||
|
|
||||||
|
@ -1,14 +1,27 @@
|
|||||||
//! Blocking mutex (not async)
|
//! Blocking mutex.
|
||||||
|
//!
|
||||||
|
//! This module provides a blocking mutex that can be used to synchronize data.
|
||||||
pub mod raw;
|
pub mod raw;
|
||||||
|
|
||||||
use core::cell::UnsafeCell;
|
use core::cell::UnsafeCell;
|
||||||
|
|
||||||
use self::raw::RawMutex;
|
use self::raw::RawMutex;
|
||||||
|
|
||||||
/// Any object implementing this trait guarantees exclusive access to the data contained
|
/// Blocking mutex (not async)
|
||||||
/// within the mutex for the duration of the lock.
|
///
|
||||||
/// Adapted from <https://github.com/rust-embedded/mutex-trait>.
|
/// Provides a blocking mutual exclusion primitive backed by an implementation of [`raw::RawMutex`].
|
||||||
|
///
|
||||||
|
/// Which implementation you select depends on the context in which you're using the mutex, and you can choose which kind
|
||||||
|
/// of interior mutability fits your use case.
|
||||||
|
///
|
||||||
|
/// Use [`CriticalSectionMutex`] when data can be shared between threads and interrupts.
|
||||||
|
///
|
||||||
|
/// Use [`NoopMutex`] when data is only shared between tasks running on the same executor.
|
||||||
|
///
|
||||||
|
/// Use [`ThreadModeMutex`] when data is shared between tasks running on the same executor but you want a global singleton.
|
||||||
|
///
|
||||||
|
/// In all cases, the blocking mutex is intended to be short lived and not held across await points.
|
||||||
|
/// Use the async [`Mutex`](crate::mutex::Mutex) if you need a lock that is held across await points.
|
||||||
pub struct Mutex<R, T: ?Sized> {
|
pub struct Mutex<R, T: ?Sized> {
|
||||||
// NOTE: `raw` must be FIRST, so when using ThreadModeMutex the "can't drop in non-thread-mode" gets
|
// NOTE: `raw` must be FIRST, so when using ThreadModeMutex the "can't drop in non-thread-mode" gets
|
||||||
// to run BEFORE dropping `data`.
|
// to run BEFORE dropping `data`.
|
||||||
@ -78,7 +91,18 @@ impl<R, T> Mutex<R, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A mutex that allows borrowing data across executors and interrupts.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This mutex is safe to share between different executors and interrupts.
|
||||||
pub type CriticalSectionMutex<T> = Mutex<raw::CriticalSectionRawMutex, T>;
|
pub type CriticalSectionMutex<T> = Mutex<raw::CriticalSectionRawMutex, T>;
|
||||||
|
|
||||||
|
/// A mutex that allows borrowing data in the context of a single executor.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// **This Mutex is only safe within a single executor.**
|
||||||
pub type NoopMutex<T> = Mutex<raw::NoopRawMutex, T>;
|
pub type NoopMutex<T> = Mutex<raw::NoopRawMutex, T>;
|
||||||
|
|
||||||
impl<T> Mutex<raw::CriticalSectionRawMutex, T> {
|
impl<T> Mutex<raw::CriticalSectionRawMutex, T> {
|
||||||
|
@ -1,11 +1,22 @@
|
|||||||
|
//! Mutex primitives.
|
||||||
|
//!
|
||||||
|
//! This module provides a trait for mutexes that can be used in different contexts.
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
/// Any object implementing this trait guarantees exclusive access to the data contained
|
||||||
|
/// within the mutex for the duration of the lock.
|
||||||
|
/// Adapted from <https://github.com/rust-embedded/mutex-trait>.
|
||||||
pub trait RawMutex {
|
pub trait RawMutex {
|
||||||
const INIT: Self;
|
const INIT: Self;
|
||||||
|
|
||||||
fn lock<R>(&self, f: impl FnOnce() -> R) -> R;
|
fn lock<R>(&self, f: impl FnOnce() -> R) -> R;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A mutex that allows borrowing data across executors and interrupts.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This mutex is safe to share between different executors and interrupts.
|
||||||
pub struct CriticalSectionRawMutex {
|
pub struct CriticalSectionRawMutex {
|
||||||
_phantom: PhantomData<()>,
|
_phantom: PhantomData<()>,
|
||||||
}
|
}
|
||||||
@ -28,6 +39,11 @@ impl RawMutex for CriticalSectionRawMutex {
|
|||||||
|
|
||||||
// ================
|
// ================
|
||||||
|
|
||||||
|
/// A mutex that allows borrowing data in the context of a single executor.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// **This Mutex is only safe within a single executor.**
|
||||||
pub struct NoopRawMutex {
|
pub struct NoopRawMutex {
|
||||||
_phantom: PhantomData<*mut ()>,
|
_phantom: PhantomData<*mut ()>,
|
||||||
}
|
}
|
||||||
@ -53,6 +69,13 @@ impl RawMutex for NoopRawMutex {
|
|||||||
mod thread_mode {
|
mod thread_mode {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
/// A "mutex" that only allows borrowing from thread mode.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// **This Mutex is only safe on single-core systems.**
|
||||||
|
///
|
||||||
|
/// On multi-core systems, a `ThreadModeRawMutex` **is not sufficient** to ensure exclusive access.
|
||||||
pub struct ThreadModeRawMutex {
|
pub struct ThreadModeRawMutex {
|
||||||
_phantom: PhantomData<()>,
|
_phantom: PhantomData<()>,
|
||||||
}
|
}
|
||||||
|
@ -433,7 +433,7 @@ where
|
|||||||
|
|
||||||
/// Attempt to immediately send a message.
|
/// Attempt to immediately send a message.
|
||||||
///
|
///
|
||||||
/// This method differs from [`send`] by returning immediately if the channel's
|
/// This method differs from [`send`](Channel::send) by returning immediately if the channel's
|
||||||
/// buffer is full, instead of waiting.
|
/// buffer is full, instead of waiting.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
//! A synchronization primitive for passing the latest value to a task.
|
||||||
use core::cell::UnsafeCell;
|
use core::cell::UnsafeCell;
|
||||||
use core::future::Future;
|
use core::future::Future;
|
||||||
use core::mem;
|
use core::mem;
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
//! ## WARNING: here be dragons!
|
//! ## WARNING: here be dragons!
|
||||||
//!
|
//!
|
||||||
//! Using this module requires respecting subtle safety contracts. If you can, prefer using the safe
|
//! Using this module requires respecting subtle safety contracts. If you can, prefer using the safe
|
||||||
//! executor wrappers in [`crate::executor`] and the [`crate::task`] macro, which are fully safe.
|
//! executor wrappers in [`executor`](crate::executor) and the [`embassy::task`](embassy_macros::task) macro, which are fully safe.
|
||||||
|
|
||||||
mod run_queue;
|
mod run_queue;
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
@ -115,7 +115,7 @@ impl TaskHeader {
|
|||||||
/// A `TaskStorage` must live forever, it may not be deallocated even after the task has finished
|
/// A `TaskStorage` must live forever, it may not be deallocated even after the task has finished
|
||||||
/// running. Hence the relevant methods require `&'static self`. It may be reused, however.
|
/// running. Hence the relevant methods require `&'static self`. It may be reused, however.
|
||||||
///
|
///
|
||||||
/// Internally, the [embassy::task](crate::task) macro allocates an array of `TaskStorage`s
|
/// Internally, the [embassy::task](embassy_macros::task) macro allocates an array of `TaskStorage`s
|
||||||
/// in a `static`. The most common reason to use the raw `Task` is to have control of where
|
/// in a `static`. The most common reason to use the raw `Task` is to have control of where
|
||||||
/// the memory for the task is allocated: on the stack, or on the heap with e.g. `Box::leak`, etc.
|
/// the memory for the task is allocated: on the stack, or on the heap with e.g. `Box::leak`, etc.
|
||||||
|
|
||||||
@ -158,7 +158,7 @@ impl<F: Future + 'static> TaskStorage<F> {
|
|||||||
///
|
///
|
||||||
/// This function will fail if the task is already spawned and has not finished running.
|
/// This function will fail if the task is already spawned and has not finished running.
|
||||||
/// In this case, the error is delayed: a "poisoned" SpawnToken is returned, which will
|
/// In this case, the error is delayed: a "poisoned" SpawnToken is returned, which will
|
||||||
/// cause [`Spawner::spawn()`] to return the error.
|
/// cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error.
|
||||||
///
|
///
|
||||||
/// Once the task has finished running, you may spawn it again. It is allowed to spawn it
|
/// Once the task has finished running, you may spawn it again. It is allowed to spawn it
|
||||||
/// on a different executor.
|
/// on a different executor.
|
||||||
@ -230,7 +230,7 @@ impl<F: Future + 'static, const N: usize> TaskPool<F, N> {
|
|||||||
///
|
///
|
||||||
/// This will loop over the pool and spawn the task in the first storage that
|
/// This will loop over the pool and spawn the task in the first storage that
|
||||||
/// is currently free. If none is free, a "poisoned" SpawnToken is returned,
|
/// is currently free. If none is free, a "poisoned" SpawnToken is returned,
|
||||||
/// which will cause [`Spawner::spawn()`] to return the error.
|
/// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error.
|
||||||
pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> {
|
pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> {
|
||||||
for task in &self.pool {
|
for task in &self.pool {
|
||||||
if task.spawn_mark_used() {
|
if task.spawn_mark_used() {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)]
|
#![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)]
|
||||||
#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))]
|
#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))]
|
||||||
#![allow(clippy::new_without_default)]
|
#![allow(clippy::new_without_default)]
|
||||||
|
#![doc = include_str!("../../README.md")]
|
||||||
|
|
||||||
// This mod MUST go first, so that the others see its macros.
|
// This mod MUST go first, so that the others see its macros.
|
||||||
pub(crate) mod fmt;
|
pub(crate) mod fmt;
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
/// Async mutex.
|
//! Async mutex.
|
||||||
///
|
//!
|
||||||
/// The mutex is generic over a blocking [`RawMutex`](crate::blocking_mutex::raw::RawMutex).
|
//! This module provides a mutex that can be used to synchronize data between asynchronous tasks.
|
||||||
/// The raw mutex is used to guard access to the internal "is locked" flag. It
|
|
||||||
/// is held for very short periods only, while locking and unlocking. It is *not* held
|
|
||||||
/// for the entire time the async Mutex is locked.
|
|
||||||
use core::cell::{RefCell, UnsafeCell};
|
use core::cell::{RefCell, UnsafeCell};
|
||||||
use core::ops::{Deref, DerefMut};
|
use core::ops::{Deref, DerefMut};
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
@ -24,6 +21,21 @@ struct State {
|
|||||||
waker: WakerRegistration,
|
waker: WakerRegistration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Async mutex.
|
||||||
|
///
|
||||||
|
/// The mutex is generic over a blocking [`RawMutex`](crate::blocking_mutex::raw::RawMutex).
|
||||||
|
/// The raw mutex is used to guard access to the internal "is locked" flag. It
|
||||||
|
/// is held for very short periods only, while locking and unlocking. It is *not* held
|
||||||
|
/// for the entire time the async Mutex is locked.
|
||||||
|
///
|
||||||
|
/// Which implementation you select depends on the context in which you're using the mutex.
|
||||||
|
///
|
||||||
|
/// Use [`CriticalSectionRawMutex`](crate::blocking_mutex::raw::CriticalSectionRawMutex) when data can be shared between threads and interrupts.
|
||||||
|
///
|
||||||
|
/// Use [`NoopRawMutex`](crate::blocking_mutex::raw::NoopRawMutex) when data is only shared between tasks running on the same executor.
|
||||||
|
///
|
||||||
|
/// Use [`ThreadModeRawMutex`](crate::blocking_mutex::raw::ThreadModeRawMutex) when data is shared between tasks running on the same executor but you want a singleton.
|
||||||
|
///
|
||||||
pub struct Mutex<M, T>
|
pub struct Mutex<M, T>
|
||||||
where
|
where
|
||||||
M: RawMutex,
|
M: RawMutex,
|
||||||
|
@ -1,3 +1,13 @@
|
|||||||
|
/// A type that can retrieved unsafely from anywhere.
|
||||||
pub trait Steal {
|
pub trait Steal {
|
||||||
|
/// Retrieve and instance of this type.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// It is the responsibility of the application to ensure that the
|
||||||
|
/// usage of the returned instance is not in conflict with other uses
|
||||||
|
/// of this instance.
|
||||||
|
///
|
||||||
|
/// The implementation may panic if the instance is already in use.
|
||||||
unsafe fn steal() -> Self;
|
unsafe fn steal() -> Self;
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,10 @@ use atomic_polyfill::{compiler_fence, AtomicPtr, Ordering};
|
|||||||
use crate::executor::raw::{task_from_waker, wake_task, TaskHeader};
|
use crate::executor::raw::{task_from_waker, wake_task, TaskHeader};
|
||||||
|
|
||||||
/// Utility struct to register and wake a waker.
|
/// Utility struct to register and wake a waker.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This type is optimized for (and only works with) embassy tasks.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct WakerRegistration {
|
pub struct WakerRegistration {
|
||||||
waker: Option<NonNull<TaskHeader>>,
|
waker: Option<NonNull<TaskHeader>>,
|
||||||
@ -53,6 +57,11 @@ impl WakerRegistration {
|
|||||||
unsafe impl Send for WakerRegistration {}
|
unsafe impl Send for WakerRegistration {}
|
||||||
unsafe impl Sync for WakerRegistration {}
|
unsafe impl Sync for WakerRegistration {}
|
||||||
|
|
||||||
|
/// Utility struct to atomically register and wake a waker.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This type is optimized for (and only works with) embassy tasks.
|
||||||
pub struct AtomicWaker {
|
pub struct AtomicWaker {
|
||||||
waker: AtomicPtr<TaskHeader>,
|
waker: AtomicPtr<TaskHeader>,
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user