aboutsummaryrefslogtreecommitdiff
path: root/examples/async2.rs
diff options
context:
space:
mode:
authorGravatar Emil Fresk <emil.fresk@gmail.com> 2022-06-10 20:08:46 +0200
committerGravatar Emil Fresk <emil.fresk@gmail.com> 2022-08-03 11:30:32 +0200
commitb2ec1fa65118813b400cf806e3ff492ea41f49ca (patch)
treea9e9e71a7f696ad2832392054af12cc0019756d7 /examples/async2.rs
parent13ccd92e630e2d2a477b5062a995a0fb1a2b7a28 (diff)
downloadrtic-b2ec1fa65118813b400cf806e3ff492ea41f49ca.tar.gz
rtic-b2ec1fa65118813b400cf806e3ff492ea41f49ca.tar.zst
rtic-b2ec1fa65118813b400cf806e3ff492ea41f49ca.zip
Example running, timeout and delay futures available
Diffstat (limited to 'examples/async2.rs')
-rw-r--r--examples/async2.rs361
1 files changed, 0 insertions, 361 deletions
diff --git a/examples/async2.rs b/examples/async2.rs
deleted file mode 100644
index 2d82ed3e..00000000
--- a/examples/async2.rs
+++ /dev/null
@@ -1,361 +0,0 @@
-#![no_main]
-#![no_std]
-#![feature(type_alias_impl_trait)]
-
-use core::future::Future;
-use core::mem;
-use core::pin::Pin;
-use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
-
-use cortex_m_semihosting::{debug, hprintln};
-use panic_semihosting as _;
-use systick_monotonic::*;
-
-// NOTES:
-//
-// - Async tasks cannot have `#[lock_free]` resources, as they can interleve and each async
-// task can have a mutable reference stored.
-// - Spawning an async task equates to it being polled at least once.
-// - ...
-
-#[rtic::app(device = lm3s6965, dispatchers = [SSI0], peripherals = true)]
-mod app {
- use crate::*;
-
- pub type AppInstant = <Systick<100> as rtic::Monotonic>::Instant;
- pub type AppDuration = <Systick<100> as rtic::Monotonic>::Duration;
-
- #[shared]
- struct Shared {
- s: u32,
- }
-
- #[local]
- struct Local {}
-
- #[monotonic(binds = SysTick, default = true)]
- type MyMono = Systick<100>;
-
- #[init]
- fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
- hprintln!("init").unwrap();
- task_executor::spawn().unwrap();
-
- (
- Shared { s: 0 },
- Local {},
- init::Monotonics(Systick::new(cx.core.SYST, 12_000_000)),
- )
- }
-
- #[idle]
- fn idle(_: idle::Context) -> ! {
- // debug::exit(debug::EXIT_SUCCESS);
- loop {
- // hprintln!("idle");
- cortex_m::asm::wfi(); // put the MCU in sleep mode until interrupt occurs
- }
- }
-
- // TODO: This should be the task, that is understood by the `syntax` proc-macro
- // #[task(priority = 2)]
- async fn task(cx: task_executor::Context<'_>) {
- #[allow(unused_imports)]
- use rtic::mutex_prelude::*;
-
- hprintln!("delay long time").ok();
-
- let fut = Delay::spawn(2500.millis());
-
- hprintln!("we have just created the future").ok();
- fut.await;
- hprintln!("long delay done").ok();
-
- hprintln!("delay short time").ok();
- sleep(500.millis()).await;
- hprintln!("short delay done").ok();
-
- hprintln!("test timeout").ok();
- let res = timeout(NeverEndingFuture {}, 1.secs()).await;
- hprintln!("timeout done: {:?}", res).ok();
-
- hprintln!("test timeout 2").ok();
- let res = timeout(Delay::spawn(500.millis()), 1.secs()).await;
- hprintln!("timeout done 2: {:?}", res).ok();
-
- debug::exit(debug::EXIT_SUCCESS);
- }
-
- //////////////////////////////////////////////
- // BEGIN BOILERPLATE
- //////////////////////////////////////////////
- type F = impl Future + 'static;
- static mut TASK: AsyncTaskExecutor<F> = AsyncTaskExecutor::new();
-
- // TODO: This should be a special case codegen for the `dispatcher`, which runs
- // in the dispatcher. Not as its own task, this is just to make it work
- // in this example.
- #[task(shared = [s])]
- fn task_executor(cx: task_executor::Context) {
- let task_storage = unsafe { &mut TASK };
- match task_storage {
- AsyncTaskExecutor::Idle => {
- // TODO: The context generated for async tasks need 'static lifetime,
- // use `mem::transmute` for now until codegen is fixed
- //
- // TODO: Check if there is some way to not need 'static lifetime
- hprintln!(" task_executor spawn").ok();
- task_storage.spawn(|| task(unsafe { mem::transmute(cx) }));
- task_executor::spawn().ok();
- }
- _ => {
- hprintln!(" task_executor run").ok();
- task_storage.poll(|| {
- task_executor::spawn().ok();
- });
- }
- };
- }
- //////////////////////////////////////////////
- // END BOILERPLATE
- //////////////////////////////////////////////
-
- // TODO: This is generated by the `delay` impl, it needs a capacity equal or grater
- // than the number of async tasks in the system. Should more likely be a part
- // of the monotonic codegen, not its own task.
- #[task(capacity = 12)]
- fn delay_handler(_: delay_handler::Context, waker: Waker) {
- waker.wake();
- }
-}
-
-//=============
-// Waker
-
-static WAKER_VTABLE: RawWakerVTable =
- RawWakerVTable::new(waker_clone, waker_wake, waker_wake, waker_drop);
-
-unsafe fn waker_clone(p: *const ()) -> RawWaker {
- RawWaker::new(p, &WAKER_VTABLE)
-}
-
-unsafe fn waker_wake(p: *const ()) {
- // The only thing we need from a waker is the function to call to pend the async
- // dispatcher.
- let f: fn() = mem::transmute(p);
- f();
-}
-
-unsafe fn waker_drop(_: *const ()) {
- // nop
-}
-
-//============
-// AsyncTaskExecutor
-
-enum AsyncTaskExecutor<F: Future + 'static> {
- Idle,
- Running(F),
-}
-
-impl<F: Future + 'static> AsyncTaskExecutor<F> {
- const fn new() -> Self {
- Self::Idle
- }
-
- fn spawn(&mut self, future: impl FnOnce() -> F) {
- *self = AsyncTaskExecutor::Running(future());
- }
-
- fn poll(&mut self, wake: fn()) {
- match self {
- AsyncTaskExecutor::Idle => {}
- AsyncTaskExecutor::Running(future) => unsafe {
- let waker_data: *const () = mem::transmute(wake);
- let waker = Waker::from_raw(RawWaker::new(waker_data, &WAKER_VTABLE));
- let mut cx = Context::from_waker(&waker);
- let future = Pin::new_unchecked(future);
-
- match future.poll(&mut cx) {
- Poll::Ready(_) => {
- *self = AsyncTaskExecutor::Idle;
- hprintln!(" task_executor idle").ok();
- }
- Poll::Pending => {}
- };
- },
- }
- }
-}
-
-//=============
-// Delay
-
-pub struct Delay {
- until: crate::app::AppInstant,
-}
-
-impl Delay {
- pub fn spawn(duration: crate::app::AppDuration) -> Self {
- let until = crate::app::monotonics::now() + duration;
-
- Delay { until }
- }
-}
-
-#[inline(always)]
-pub fn sleep(duration: crate::app::AppDuration) -> Delay {
- Delay::spawn(duration)
-}
-
-impl Future for Delay {
- type Output = ();
-
- fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
- let s = self.as_mut();
- let now = crate::app::monotonics::now();
-
- hprintln!(" poll Delay").ok();
-
- if now >= s.until {
- Poll::Ready(())
- } else {
- let waker = cx.waker().clone();
- crate::app::delay_handler::spawn_after(s.until - now, waker).ok();
-
- Poll::Pending
- }
- }
-}
-
-//=============
-// Timeout future
-
-#[derive(Copy, Clone, Debug)]
-pub struct TimeoutError;
-
-pub struct Timeout<F: Future> {
- future: F,
- until: crate::app::AppInstant,
- cancel_handle: Option<crate::app::delay_handler::SpawnHandle>,
-}
-
-impl<F> Timeout<F>
-where
- F: Future,
-{
- pub fn timeout(future: F, duration: crate::app::AppDuration) -> Self {
- let until = crate::app::monotonics::now() + duration;
- Self {
- future,
- until,
- cancel_handle: None,
- }
- }
-}
-
-#[inline(always)]
-pub fn timeout<F: Future>(future: F, duration: crate::app::AppDuration) -> Timeout<F> {
- Timeout::timeout(future, duration)
-}
-
-impl<F> Future for Timeout<F>
-where
- F: Future,
-{
- type Output = Result<F::Output, TimeoutError>;
-
- fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
- let now = crate::app::monotonics::now();
-
- // SAFETY: We don't move the underlying pinned value.
- let mut s = unsafe { self.get_unchecked_mut() };
- let future = unsafe { Pin::new_unchecked(&mut s.future) };
-
- hprintln!(" poll Timeout").ok();
-
- match future.poll(cx) {
- Poll::Ready(r) => {
- if let Some(ch) = s.cancel_handle.take() {
- ch.cancel().ok();
- }
-
- Poll::Ready(Ok(r))
- }
- Poll::Pending => {
- if now >= s.until {
- Poll::Ready(Err(TimeoutError))
- } else if s.cancel_handle.is_none() {
- let waker = cx.waker().clone();
- let sh = crate::app::delay_handler::spawn_after(s.until - now, waker)
- .expect("Internal RTIC bug, this should never fail");
- s.cancel_handle = Some(sh);
-
- Poll::Pending
- } else {
- Poll::Pending
- }
- }
- }
- }
-}
-
-pub struct NeverEndingFuture {}
-
-impl Future for NeverEndingFuture {
- type Output = ();
-
- fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
- // Never finish
- hprintln!(" polling NeverEndingFuture").ok();
- Poll::Pending
- }
-}
-
-//=============
-// Async SPI driver
-
-// #[task]
-async fn test_spi(async_spi_driver: &mut AsyncSpi) {
- let transfer = Transaction {
- buf: [0; 16],
- n_write: 1,
- n_read: 5,
- };
-
- let ret = async_spi_driver.transfer(transfer).await;
-
- // do_something(ret);
-}
-
-/// A DMA transaction.
-///
-/// NOTE: Don't leak this `Future`, if you do there is immediate UB!
-struct Transaction {
- pub buf: [u8; 16],
- pub n_write: usize,
- pub n_read: usize,
-}
-
-struct AsyncSpi {
- transaction: Option<Transaction>,
- queue: heapless::spsc::Queue<Waker, 8>,
-}
-
-impl AsyncSpi {
- pub fn transfer(&mut self, transfer: Transaction) -> AsyncSpiTransaction {
- todo!()
- }
-}
-
-struct AsyncSpiTransaction {
- // ...
-}
-
-impl Future for AsyncSpiTransaction {
- type Output = ();
-
- fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
- todo!()
- }
-}