mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-27 14:04:56 +01:00
Added examples for async crates + fixed codegen for non-Copy arguments
This commit is contained in:
parent
a04f127a09
commit
8a1503ef3e
13 changed files with 152 additions and 77 deletions
2
rtic-channel/.gitignore
vendored
Normal file
2
rtic-channel/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Cargo.lock
|
||||||
|
target/
|
|
@ -67,7 +67,7 @@ impl<T, const N: usize> Channel<T, N> {
|
||||||
/// Split the queue into a `Sender`/`Receiver` pair.
|
/// Split the queue into a `Sender`/`Receiver` pair.
|
||||||
pub fn split<'a>(&'a mut self) -> (Sender<'a, T, N>, Receiver<'a, T, N>) {
|
pub fn split<'a>(&'a mut self) -> (Sender<'a, T, N>, Receiver<'a, T, N>) {
|
||||||
// Fill free queue
|
// Fill free queue
|
||||||
for idx in 0..(N - 1) as u8 {
|
for idx in 0..N as u8 {
|
||||||
debug_assert!(!self.freeq.get_mut().is_full());
|
debug_assert!(!self.freeq.get_mut().is_full());
|
||||||
|
|
||||||
// SAFETY: This safe as the loop goes from 0 to the capacity of the underlying queue.
|
// SAFETY: This safe as the loop goes from 0 to the capacity of the underlying queue.
|
||||||
|
@ -114,11 +114,26 @@ macro_rules! make_channel {
|
||||||
/// Error state for when the receiver has been dropped.
|
/// Error state for when the receiver has been dropped.
|
||||||
pub struct NoReceiver<T>(pub T);
|
pub struct NoReceiver<T>(pub T);
|
||||||
|
|
||||||
|
impl<T> core::fmt::Debug for NoReceiver<T>
|
||||||
|
where
|
||||||
|
T: core::fmt::Debug,
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "NoReceiver({:?})", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A `Sender` can send to the channel and can be cloned.
|
/// A `Sender` can send to the channel and can be cloned.
|
||||||
pub struct Sender<'a, T, const N: usize>(&'a Channel<T, N>);
|
pub struct Sender<'a, T, const N: usize>(&'a Channel<T, N>);
|
||||||
|
|
||||||
unsafe impl<'a, T, const N: usize> Send for Sender<'a, T, N> {}
|
unsafe impl<'a, T, const N: usize> Send for Sender<'a, T, N> {}
|
||||||
|
|
||||||
|
impl<'a, T, const N: usize> core::fmt::Debug for Sender<'a, T, N> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "Sender")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, T, const N: usize> Sender<'a, T, N> {
|
impl<'a, T, const N: usize> Sender<'a, T, N> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn send_footer(&mut self, idx: u8, val: T) {
|
fn send_footer(&mut self, idx: u8, val: T) {
|
||||||
|
@ -204,7 +219,7 @@ impl<'a, T, const N: usize> Sender<'a, T, N> {
|
||||||
// Return the index
|
// Return the index
|
||||||
Poll::Ready(Ok(idx))
|
Poll::Ready(Ok(idx))
|
||||||
} else {
|
} else {
|
||||||
return Poll::Pending;
|
Poll::Pending
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -267,16 +282,29 @@ impl<'a, T, const N: usize> Clone for Sender<'a, T, N> {
|
||||||
/// A receiver of the channel. There can only be one receiver at any time.
|
/// A receiver of the channel. There can only be one receiver at any time.
|
||||||
pub struct Receiver<'a, T, const N: usize>(&'a Channel<T, N>);
|
pub struct Receiver<'a, T, const N: usize>(&'a Channel<T, N>);
|
||||||
|
|
||||||
|
unsafe impl<'a, T, const N: usize> Send for Receiver<'a, T, N> {}
|
||||||
|
|
||||||
|
impl<'a, T, const N: usize> core::fmt::Debug for Receiver<'a, T, N> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "Receiver")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Error state for when all senders has been dropped.
|
/// Error state for when all senders has been dropped.
|
||||||
pub struct NoSender;
|
pub struct NoSender;
|
||||||
|
|
||||||
|
impl core::fmt::Debug for NoSender {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "NoSender")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, T, const N: usize> Receiver<'a, T, N> {
|
impl<'a, T, const N: usize> Receiver<'a, T, N> {
|
||||||
/// Receives a value if there is one in the channel, non-blocking.
|
/// Receives a value if there is one in the channel, non-blocking.
|
||||||
/// Note; this does not check if the channel is closed.
|
/// Note; this does not check if the channel is closed.
|
||||||
pub fn try_recv(&mut self) -> Option<T> {
|
pub fn try_recv(&mut self) -> Option<T> {
|
||||||
// Try to get a ready slot.
|
// Try to get a ready slot.
|
||||||
let ready_slot =
|
let ready_slot = critical_section::with(|cs| self.0.access(cs).readyq.pop_front());
|
||||||
critical_section::with(|cs| self.0.access(cs).readyq.pop_front().map(|rs| rs));
|
|
||||||
|
|
||||||
if let Some(rs) = ready_slot {
|
if let Some(rs) = ready_slot {
|
||||||
// Read the value from the slots, note; this memcpy is not under a critical section.
|
// Read the value from the slots, note; this memcpy is not under a critical section.
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
//! ...
|
//! ...
|
||||||
|
|
||||||
use core::cell::UnsafeCell;
|
|
||||||
use core::marker::PhantomPinned;
|
use core::marker::PhantomPinned;
|
||||||
use core::ptr::null_mut;
|
use core::ptr::null_mut;
|
||||||
use core::sync::atomic::{AtomicPtr, Ordering};
|
use core::sync::atomic::{AtomicPtr, Ordering};
|
||||||
|
@ -9,27 +8,6 @@ use critical_section as cs;
|
||||||
|
|
||||||
pub type WaitQueue = LinkedList<Waker>;
|
pub type WaitQueue = LinkedList<Waker>;
|
||||||
|
|
||||||
struct MyLinkPtr<T>(UnsafeCell<*mut Link<T>>);
|
|
||||||
|
|
||||||
impl<T> MyLinkPtr<T> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn new(val: *mut Link<T>) -> Self {
|
|
||||||
Self(UnsafeCell::new(val))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// SAFETY: Only use this in a critical section, and don't forget them barriers.
|
|
||||||
#[inline(always)]
|
|
||||||
unsafe fn load_relaxed(&self) -> *mut Link<T> {
|
|
||||||
unsafe { *self.0.get() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// SAFETY: Only use this in a critical section, and don't forget them barriers.
|
|
||||||
#[inline(always)]
|
|
||||||
unsafe fn store_relaxed(&self, val: *mut Link<T>) {
|
|
||||||
unsafe { self.0.get().write(val) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A FIFO linked list for a wait queue.
|
/// A FIFO linked list for a wait queue.
|
||||||
pub struct LinkedList<T> {
|
pub struct LinkedList<T> {
|
||||||
head: AtomicPtr<Link<T>>, // UnsafeCell<*mut Link<T>>
|
head: AtomicPtr<Link<T>>, // UnsafeCell<*mut Link<T>>
|
||||||
|
|
|
@ -10,3 +10,4 @@ cortex-m = { version = "0.7.6" }
|
||||||
embedded-hal-async = "0.2.0-alpha.0"
|
embedded-hal-async = "0.2.0-alpha.0"
|
||||||
fugit = { version = "0.3.6", features = ["defmt"] }
|
fugit = { version = "0.3.6", features = ["defmt"] }
|
||||||
rtic-time = { version = "1.0.0", path = "../rtic-time" }
|
rtic-time = { version = "1.0.0", path = "../rtic-time" }
|
||||||
|
atomic-polyfill = "1"
|
||||||
|
|
|
@ -2,13 +2,11 @@
|
||||||
|
|
||||||
use super::Monotonic;
|
use super::Monotonic;
|
||||||
pub use super::{TimeoutError, TimerQueue};
|
pub use super::{TimeoutError, TimerQueue};
|
||||||
use core::{
|
use atomic_polyfill::{AtomicU32, Ordering};
|
||||||
ops::Deref,
|
use core::ops::Deref;
|
||||||
sync::atomic::{AtomicU32, Ordering},
|
|
||||||
};
|
|
||||||
use cortex_m::peripheral::SYST;
|
use cortex_m::peripheral::SYST;
|
||||||
use embedded_hal_async::delay::DelayUs;
|
use embedded_hal_async::delay::DelayUs;
|
||||||
use fugit::ExtU32;
|
pub use fugit::ExtU32;
|
||||||
|
|
||||||
const TIMER_HZ: u32 = 1_000;
|
const TIMER_HZ: u32 = 1_000;
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,9 @@ version_check = "0.9"
|
||||||
lm3s6965 = "0.1.3"
|
lm3s6965 = "0.1.3"
|
||||||
cortex-m-semihosting = "0.5.0"
|
cortex-m-semihosting = "0.5.0"
|
||||||
systick-monotonic = "1.0.0"
|
systick-monotonic = "1.0.0"
|
||||||
|
rtic-time = { path = "../rtic-time" }
|
||||||
|
rtic-channel = { path = "../rtic-channel" }
|
||||||
|
rtic-monotonics = { path = "../rtic-monotonics" }
|
||||||
|
|
||||||
[dev-dependencies.panic-semihosting]
|
[dev-dependencies.panic-semihosting]
|
||||||
features = ["exit"]
|
features = ["exit"]
|
||||||
|
|
6
rtic/ci/expected/async-channel.run
Normal file
6
rtic/ci/expected/async-channel.run
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
Sender 1 sending: 1
|
||||||
|
Sender 2 sending: 2
|
||||||
|
Sender 3 sending: 3
|
||||||
|
Receiver got: 1
|
||||||
|
Receiver got: 2
|
||||||
|
Receiver got: 5
|
|
@ -1,3 +1 @@
|
||||||
foo 1, 1
|
foo 1, 1
|
||||||
foo 1, 2
|
|
||||||
foo 2, 3
|
|
||||||
|
|
61
rtic/examples/async-channel.rs
Normal file
61
rtic/examples/async-channel.rs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
//! examples/async-channel.rs
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use panic_semihosting as _;
|
||||||
|
|
||||||
|
#[rtic::app(device = lm3s6965, dispatchers = [SSI0])]
|
||||||
|
mod app {
|
||||||
|
use cortex_m_semihosting::{debug, hprintln};
|
||||||
|
use rtic_channel::*;
|
||||||
|
|
||||||
|
#[shared]
|
||||||
|
struct Shared {}
|
||||||
|
|
||||||
|
#[local]
|
||||||
|
struct Local {}
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init(_: init::Context) -> (Shared, Local) {
|
||||||
|
let (s, r) = make_channel!(u32, 5);
|
||||||
|
|
||||||
|
receiver::spawn(r).unwrap();
|
||||||
|
sender1::spawn(s.clone()).unwrap();
|
||||||
|
sender2::spawn(s.clone()).unwrap();
|
||||||
|
sender3::spawn(s).unwrap();
|
||||||
|
|
||||||
|
(Shared {}, Local {})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task]
|
||||||
|
async fn receiver(_c: receiver::Context, mut receiver: Receiver<'static, u32, 5>) {
|
||||||
|
while let Ok(val) = receiver.recv().await {
|
||||||
|
hprintln!("Receiver got: {}", val);
|
||||||
|
if val == 5 {
|
||||||
|
debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task]
|
||||||
|
async fn sender1(_c: sender1::Context, mut sender: Sender<'static, u32, 5>) {
|
||||||
|
hprintln!("Sender 1 sending: 1");
|
||||||
|
sender.send(1).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task]
|
||||||
|
async fn sender2(_c: sender2::Context, mut sender: Sender<'static, u32, 5>) {
|
||||||
|
hprintln!("Sender 2 sending: 2");
|
||||||
|
sender.send(2).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task]
|
||||||
|
async fn sender3(_c: sender3::Context, mut sender: Sender<'static, u32, 5>) {
|
||||||
|
hprintln!("Sender 3 sending: 3");
|
||||||
|
sender.send(5).await.unwrap();
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,9 @@ use panic_semihosting as _;
|
||||||
#[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)]
|
#[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)]
|
||||||
mod app {
|
mod app {
|
||||||
use cortex_m_semihosting::{debug, hprintln};
|
use cortex_m_semihosting::{debug, hprintln};
|
||||||
use systick_monotonic::*;
|
use rtic_monotonics::systick_monotonic::*;
|
||||||
|
|
||||||
|
rtic_monotonics::make_systick_timer_queue!(TIMER);
|
||||||
|
|
||||||
#[shared]
|
#[shared]
|
||||||
struct Shared {}
|
struct Shared {}
|
||||||
|
@ -15,12 +17,12 @@ mod app {
|
||||||
#[local]
|
#[local]
|
||||||
struct Local {}
|
struct Local {}
|
||||||
|
|
||||||
#[monotonic(binds = SysTick, default = true)]
|
|
||||||
type MyMono = Systick<100>;
|
|
||||||
|
|
||||||
#[init]
|
#[init]
|
||||||
fn init(cx: init::Context) -> (Shared, Local) {
|
fn init(cx: init::Context) -> (Shared, Local) {
|
||||||
hprintln!("init").unwrap();
|
hprintln!("init");
|
||||||
|
|
||||||
|
let systick = Systick::start(cx.core.SYST, 12_000_000);
|
||||||
|
TIMER.initialize(systick);
|
||||||
|
|
||||||
foo::spawn().ok();
|
foo::spawn().ok();
|
||||||
bar::spawn().ok();
|
bar::spawn().ok();
|
||||||
|
@ -40,23 +42,23 @@ mod app {
|
||||||
|
|
||||||
#[task]
|
#[task]
|
||||||
async fn foo(_cx: foo::Context) {
|
async fn foo(_cx: foo::Context) {
|
||||||
hprintln!("hello from foo").ok();
|
hprintln!("hello from foo");
|
||||||
monotonics::delay(100.millis()).await;
|
TIMER.delay(100.millis()).await;
|
||||||
hprintln!("bye from foo").ok();
|
hprintln!("bye from foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task]
|
#[task]
|
||||||
async fn bar(_cx: bar::Context) {
|
async fn bar(_cx: bar::Context) {
|
||||||
hprintln!("hello from bar").ok();
|
hprintln!("hello from bar");
|
||||||
monotonics::delay(200.millis()).await;
|
TIMER.delay(200.millis()).await;
|
||||||
hprintln!("bye from bar").ok();
|
hprintln!("bye from bar");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task]
|
#[task]
|
||||||
async fn baz(_cx: baz::Context) {
|
async fn baz(_cx: baz::Context) {
|
||||||
hprintln!("hello from baz").ok();
|
hprintln!("hello from baz");
|
||||||
monotonics::delay(300.millis()).await;
|
TIMER.delay(300.millis()).await;
|
||||||
hprintln!("bye from baz").ok();
|
hprintln!("bye from baz");
|
||||||
|
|
||||||
debug::exit(debug::EXIT_SUCCESS);
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
}
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
use panic_semihosting as _;
|
use panic_semihosting as _;
|
||||||
|
|
||||||
|
@ -18,20 +19,16 @@ mod app {
|
||||||
struct Local {}
|
struct Local {}
|
||||||
|
|
||||||
#[init]
|
#[init]
|
||||||
fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
|
fn init(_: init::Context) -> (Shared, Local) {
|
||||||
foo::spawn(1, 1).unwrap();
|
foo::spawn(1, 1).unwrap();
|
||||||
foo::spawn(1, 2).unwrap();
|
|
||||||
foo::spawn(2, 3).unwrap();
|
|
||||||
assert!(foo::spawn(1, 4).is_err()); // The capacity of `foo` is reached
|
assert!(foo::spawn(1, 4).is_err()); // The capacity of `foo` is reached
|
||||||
|
|
||||||
(Shared {}, Local {}, init::Monotonics())
|
(Shared {}, Local {})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(capacity = 3)]
|
#[task]
|
||||||
fn foo(_c: foo::Context, x: i32, y: u32) {
|
async fn foo(_c: foo::Context, x: i32, y: u32) {
|
||||||
hprintln!("foo {}, {}", x, y).unwrap();
|
hprintln!("foo {}, {}", x, y);
|
||||||
if x == 2 {
|
debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator
|
||||||
debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -157,14 +157,17 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn #internal_spawn_ident(#(#input_args,)*) -> Result<(), #input_ty> {
|
pub fn #internal_spawn_ident(#(#input_args,)*) -> Result<(), #input_ty> {
|
||||||
|
// SAFETY: If `try_allocate` suceeds one must call `spawn`, which we do.
|
||||||
|
unsafe {
|
||||||
|
if #exec_name.try_allocate() {
|
||||||
|
let f = #name(unsafe { #name::Context::new() } #(,#input_untupled)*);
|
||||||
|
#exec_name.spawn(f);
|
||||||
|
#pend_interrupt
|
||||||
|
|
||||||
if #exec_name.spawn(|| #name(unsafe { #name::Context::new() } #(,#input_untupled)*) ) {
|
Ok(())
|
||||||
|
} else {
|
||||||
#pend_interrupt
|
Err(#input_tupled)
|
||||||
|
}
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(#input_tupled)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,25 +70,23 @@ impl<F: Future> AsyncTaskExecutor<F> {
|
||||||
self.pending.store(true, Ordering::Release);
|
self.pending.store(true, Ordering::Release);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawn a future
|
/// Allocate the executor. To use with `spawn`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn spawn(&self, future: impl Fn() -> F) -> bool {
|
pub unsafe fn try_allocate(&self) -> bool {
|
||||||
// Try to reserve the executor for a future.
|
// Try to reserve the executor for a future.
|
||||||
if self
|
self.running
|
||||||
.running
|
|
||||||
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Relaxed)
|
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Relaxed)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
}
|
||||||
// This unsafe is protected by `running` being false and the atomic setting it to true.
|
|
||||||
unsafe {
|
|
||||||
self.task.get().write(MaybeUninit::new(future()));
|
|
||||||
}
|
|
||||||
self.set_pending();
|
|
||||||
|
|
||||||
true
|
/// Spawn a future
|
||||||
} else {
|
#[inline(always)]
|
||||||
false
|
pub unsafe fn spawn(&self, future: F) {
|
||||||
|
// This unsafe is protected by `running` being false and the atomic setting it to true.
|
||||||
|
unsafe {
|
||||||
|
self.task.get().write(MaybeUninit::new(future));
|
||||||
}
|
}
|
||||||
|
self.set_pending();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Poll the future in the executor.
|
/// Poll the future in the executor.
|
||||||
|
|
Loading…
Reference in a new issue