mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-25 21:19:35 +01:00
Added examples for async crates + fixed codegen for non-Copy arguments
This commit is contained in:
parent
d23de62823
commit
922f1ad0eb
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.
|
||||
pub fn split<'a>(&'a mut self) -> (Sender<'a, T, N>, Receiver<'a, T, N>) {
|
||||
// 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());
|
||||
|
||||
// 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.
|
||||
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.
|
||||
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> {}
|
||||
|
||||
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> {
|
||||
#[inline(always)]
|
||||
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
|
||||
Poll::Ready(Ok(idx))
|
||||
} else {
|
||||
return Poll::Pending;
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.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.
|
||||
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.
|
||||
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> {
|
||||
/// Receives a value if there is one in the channel, non-blocking.
|
||||
/// Note; this does not check if the channel is closed.
|
||||
pub fn try_recv(&mut self) -> Option<T> {
|
||||
// Try to get a ready slot.
|
||||
let ready_slot =
|
||||
critical_section::with(|cs| self.0.access(cs).readyq.pop_front().map(|rs| rs));
|
||||
let ready_slot = critical_section::with(|cs| self.0.access(cs).readyq.pop_front());
|
||||
|
||||
if let Some(rs) = ready_slot {
|
||||
// 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::ptr::null_mut;
|
||||
use core::sync::atomic::{AtomicPtr, Ordering};
|
||||
|
@ -9,27 +8,6 @@ use critical_section as cs;
|
|||
|
||||
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.
|
||||
pub struct LinkedList<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"
|
||||
fugit = { version = "0.3.6", features = ["defmt"] }
|
||||
rtic-time = { version = "1.0.0", path = "../rtic-time" }
|
||||
atomic-polyfill = "1"
|
||||
|
|
|
@ -2,13 +2,11 @@
|
|||
|
||||
use super::Monotonic;
|
||||
pub use super::{TimeoutError, TimerQueue};
|
||||
use core::{
|
||||
ops::Deref,
|
||||
sync::atomic::{AtomicU32, Ordering},
|
||||
};
|
||||
use atomic_polyfill::{AtomicU32, Ordering};
|
||||
use core::ops::Deref;
|
||||
use cortex_m::peripheral::SYST;
|
||||
use embedded_hal_async::delay::DelayUs;
|
||||
use fugit::ExtU32;
|
||||
pub use fugit::ExtU32;
|
||||
|
||||
const TIMER_HZ: u32 = 1_000;
|
||||
|
||||
|
|
|
@ -38,6 +38,9 @@ version_check = "0.9"
|
|||
lm3s6965 = "0.1.3"
|
||||
cortex-m-semihosting = "0.5.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]
|
||||
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, 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)]
|
||||
mod app {
|
||||
use cortex_m_semihosting::{debug, hprintln};
|
||||
use systick_monotonic::*;
|
||||
use rtic_monotonics::systick_monotonic::*;
|
||||
|
||||
rtic_monotonics::make_systick_timer_queue!(TIMER);
|
||||
|
||||
#[shared]
|
||||
struct Shared {}
|
||||
|
@ -15,12 +17,12 @@ mod app {
|
|||
#[local]
|
||||
struct Local {}
|
||||
|
||||
#[monotonic(binds = SysTick, default = true)]
|
||||
type MyMono = Systick<100>;
|
||||
|
||||
#[init]
|
||||
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();
|
||||
bar::spawn().ok();
|
||||
|
@ -40,23 +42,23 @@ mod app {
|
|||
|
||||
#[task]
|
||||
async fn foo(_cx: foo::Context) {
|
||||
hprintln!("hello from foo").ok();
|
||||
monotonics::delay(100.millis()).await;
|
||||
hprintln!("bye from foo").ok();
|
||||
hprintln!("hello from foo");
|
||||
TIMER.delay(100.millis()).await;
|
||||
hprintln!("bye from foo");
|
||||
}
|
||||
|
||||
#[task]
|
||||
async fn bar(_cx: bar::Context) {
|
||||
hprintln!("hello from bar").ok();
|
||||
monotonics::delay(200.millis()).await;
|
||||
hprintln!("bye from bar").ok();
|
||||
hprintln!("hello from bar");
|
||||
TIMER.delay(200.millis()).await;
|
||||
hprintln!("bye from bar");
|
||||
}
|
||||
|
||||
#[task]
|
||||
async fn baz(_cx: baz::Context) {
|
||||
hprintln!("hello from baz").ok();
|
||||
monotonics::delay(300.millis()).await;
|
||||
hprintln!("bye from baz").ok();
|
||||
hprintln!("hello from baz");
|
||||
TIMER.delay(300.millis()).await;
|
||||
hprintln!("bye from baz");
|
||||
|
||||
debug::exit(debug::EXIT_SUCCESS);
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
#![deny(warnings)]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use panic_semihosting as _;
|
||||
|
||||
|
@ -18,20 +19,16 @@ mod app {
|
|||
struct Local {}
|
||||
|
||||
#[init]
|
||||
fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
|
||||
fn init(_: init::Context) -> (Shared, Local) {
|
||||
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
|
||||
|
||||
(Shared {}, Local {}, init::Monotonics())
|
||||
(Shared {}, Local {})
|
||||
}
|
||||
|
||||
#[task(capacity = 3)]
|
||||
fn foo(_c: foo::Context, x: i32, y: u32) {
|
||||
hprintln!("foo {}, {}", x, y).unwrap();
|
||||
if x == 2 {
|
||||
debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator
|
||||
}
|
||||
#[task]
|
||||
async fn foo(_c: foo::Context, x: i32, y: u32) {
|
||||
hprintln!("foo {}, {}", x, y);
|
||||
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)]
|
||||
#[doc(hidden)]
|
||||
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)*) ) {
|
||||
|
||||
#pend_interrupt
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(#input_tupled)
|
||||
Ok(())
|
||||
} else {
|
||||
Err(#input_tupled)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -70,25 +70,23 @@ impl<F: Future> AsyncTaskExecutor<F> {
|
|||
self.pending.store(true, Ordering::Release);
|
||||
}
|
||||
|
||||
/// Spawn a future
|
||||
/// Allocate the executor. To use with `spawn`.
|
||||
#[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.
|
||||
if self
|
||||
.running
|
||||
self.running
|
||||
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Relaxed)
|
||||
.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
|
||||
} else {
|
||||
false
|
||||
/// Spawn a future
|
||||
#[inline(always)]
|
||||
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.
|
||||
|
|
Loading…
Reference in a new issue