More work on new spawn/executor

This commit is contained in:
Emil Fresk 2023-01-09 21:02:53 +01:00 committed by Henrik Tjäder
parent b1cadd79ee
commit ad2bf4e77c
8 changed files with 43 additions and 108 deletions

View file

@ -28,6 +28,8 @@ rtic-monotonic = "1.0.0"
rtic-core = "1.0.0" rtic-core = "1.0.0"
heapless = "0.7.7" heapless = "0.7.7"
bare-metal = "1.0.0" bare-metal = "1.0.0"
#portable-atomic = { version = "0.3.19" }
atomic-polyfill = "1"
[build-dependencies] [build-dependencies]
version_check = "0.9" version_check = "0.9"

View file

@ -16,11 +16,10 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
items.push(quote!( items.push(quote!(
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
type #type_name = impl core::future::Future + 'static; type #type_name = impl core::future::Future;
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
static #exec_name: static #exec_name: rtic::export::executor::AsyncTaskExecutor<#type_name> =
rtic::RacyCell<rtic::export::executor::AsyncTaskExecutor<#type_name>> = rtic::export::executor::AsyncTaskExecutor::new();
rtic::RacyCell::new(rtic::export::executor::AsyncTaskExecutor::new());
)); ));
} }
@ -47,38 +46,13 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
let exec_name = util::internal_task_ident(name, "EXEC"); let exec_name = util::internal_task_ident(name, "EXEC");
// let task = &app.software_tasks[name]; // let task = &app.software_tasks[name];
// let cfgs = &task.cfgs; // let cfgs = &task.cfgs;
let executor_run_ident = util::executor_run_ident(name);
let rq = util::rq_async_ident(name);
items.push(quote!(
#[doc(hidden)]
#[allow(non_camel_case_types)]
#[allow(non_upper_case_globals)]
static #rq: core::sync::atomic::AtomicBool = core::sync::atomic::AtomicBool::new(false);
));
stmts.push(quote!( stmts.push(quote!(
if !(&*#exec_name.get()).is_running() { if #exec_name.check_and_clear_pending() {
// TODO Fix this to be compare and swap #exec_name.poll(|| {
if #rq.load(core::sync::atomic::Ordering::Relaxed) { #exec_name.set_pending();
#rq.store(false, core::sync::atomic::Ordering::Relaxed);
(&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new()));
#executor_run_ident.store(true, core::sync::atomic::Ordering::Relaxed);
}
}
if #executor_run_ident.load(core::sync::atomic::Ordering::Relaxed) {
#executor_run_ident.store(false, core::sync::atomic::Ordering::Relaxed);
if (&mut *#exec_name.get_mut()).poll(|| {
#executor_run_ident.store(true, core::sync::atomic::Ordering::Release);
#pend_interrupt #pend_interrupt
}) && #rq.load(core::sync::atomic::Ordering::Relaxed) { });
// If the ready queue is not empty and the executor finished, restart this
// dispatch to check if the executor should be restarted.
#pend_interrupt
}
} }
)); ));
} }
@ -96,12 +70,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
const PRIORITY: u8 = #level; const PRIORITY: u8 = #level;
rtic::export::run(PRIORITY, || { rtic::export::run(PRIORITY, || {
// Have the acquire/release semantics outside the checks to no overdo it
core::sync::atomic::fence(core::sync::atomic::Ordering::Acquire);
#(#stmts)* #(#stmts)*
core::sync::atomic::fence(core::sync::atomic::Ordering::Release);
}); });
} }
)); ));
@ -110,12 +79,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
#[allow(non_snake_case)] #[allow(non_snake_case)]
unsafe fn #dispatcher_name() -> ! { unsafe fn #dispatcher_name() -> ! {
loop { loop {
// Have the acquire/release semantics outside the checks to no overdo it
core::sync::atomic::fence(core::sync::atomic::Ordering::Acquire);
#(#stmts)* #(#stmts)*
core::sync::atomic::fence(core::sync::atomic::Ordering::Release);
} }
} }
)); ));

View file

@ -98,6 +98,7 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
}; };
let internal_context_name = util::internal_task_ident(name, "Context"); let internal_context_name = util::internal_task_ident(name, "Context");
let exec_name = util::internal_task_ident(name, "EXEC");
items.push(quote!( items.push(quote!(
#(#cfgs)* #(#cfgs)*
@ -147,25 +148,25 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
let internal_spawn_ident = util::internal_task_ident(name, "spawn"); let internal_spawn_ident = util::internal_task_ident(name, "spawn");
// Spawn caller // Spawn caller
let rq = util::rq_async_ident(name);
items.push(quote!( items.push(quote!(
#(#cfgs)* #(#cfgs)*
/// Spawns the task directly /// Spawns the task directly
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[doc(hidden)] #[doc(hidden)]
pub fn #internal_spawn_ident() -> Result<(), ()> { pub fn #internal_spawn_ident() -> Result<(), ()> {
if #exec_name.try_reserve() {
unsafe { unsafe {
// TODO: Fix this to be compare and swap // TODO: Add args here
if #rq.load(core::sync::atomic::Ordering::Acquire) { #exec_name.spawn_unchecked(#name(#name::Context::new()));
Err(()) }
} else {
#rq.store(true, core::sync::atomic::Ordering::Release);
#pend_interrupt #pend_interrupt
Ok(()) Ok(())
} else {
Err(())
} }
} }
})); ));
module_items.push(quote!( module_items.push(quote!(
#(#cfgs)* #(#cfgs)*

View file

@ -1,7 +1,7 @@
use crate::syntax::{ast::App, Context}; use crate::syntax::{ast::App, Context};
use crate::{ use crate::{
analyze::Analysis, analyze::Analysis,
codegen::{local_resources_struct, module, shared_resources_struct, util}, codegen::{local_resources_struct, module, shared_resources_struct},
}; };
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
use quote::quote; use quote::quote;
@ -13,18 +13,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
// Any task // Any task
for (name, task) in app.software_tasks.iter() { for (name, task) in app.software_tasks.iter() {
let executor_ident = util::executor_run_ident(name);
mod_app.push(quote!(
#[allow(non_camel_case_types)]
#[allow(non_upper_case_globals)]
#[doc(hidden)]
static #executor_ident: core::sync::atomic::AtomicBool =
core::sync::atomic::AtomicBool::new(false);
));
// `${task}Resources`
// `${task}Locals`
if !task.args.local_resources.is_empty() { if !task.args.local_resources.is_empty() {
let (item, constructor) = let (item, constructor) =
local_resources_struct::codegen(Context::SoftwareTask(name), app); local_resources_struct::codegen(Context::SoftwareTask(name), app);

View file

@ -49,11 +49,6 @@ pub fn impl_mutex(
) )
} }
/// Generates an identifier for the `EXECUTOR_RUN` atomics (`async` API)
pub fn executor_run_ident(task: &Ident) -> Ident {
mark_internal_name(&format!("{task}_EXECUTOR_RUN"))
}
pub fn interrupt_ident() -> Ident { pub fn interrupt_ident() -> Ident {
let span = Span::call_site(); let span = Span::call_site();
Ident::new("interrupt", span) Ident::new("interrupt", span)
@ -151,11 +146,6 @@ pub fn local_resources_ident(ctxt: Context, app: &App) -> Ident {
mark_internal_name(&s) mark_internal_name(&s)
} }
/// Generates an identifier for a ready queue, async task version
pub fn rq_async_ident(async_task_name: &Ident) -> Ident {
mark_internal_name(&format!("ASYNC_TASK_{async_task_name}_RQ"))
}
/// Suffixed identifier /// Suffixed identifier
pub fn suffixed(name: &str) -> Ident { pub fn suffixed(name: &str) -> Ident {
let span = Span::call_site(); let span = Span::call_site();

View file

@ -1,5 +1,4 @@
pub use bare_metal::CriticalSection; pub use bare_metal::CriticalSection;
use core::sync::atomic::{AtomicBool, Ordering};
pub use cortex_m::{ pub use cortex_m::{
asm::nop, asm::nop,
asm::wfi, asm::wfi,
@ -7,6 +6,8 @@ pub use cortex_m::{
peripheral::{scb::SystemHandler, DWT, NVIC, SCB, SYST}, peripheral::{scb::SystemHandler, DWT, NVIC, SCB, SYST},
Peripherals, Peripherals,
}; };
//pub use portable_atomic as atomic;
pub use atomic_polyfill as atomic;
pub mod executor; pub mod executor;
@ -72,28 +73,6 @@ where
f(); f();
} }
pub struct Barrier {
inner: AtomicBool,
}
impl Barrier {
pub const fn new() -> Self {
Barrier {
inner: AtomicBool::new(false),
}
}
pub fn release(&self) {
self.inner.store(true, Ordering::Release);
}
pub fn wait(&self) {
while !self.inner.load(Ordering::Acquire) {
core::hint::spin_loop()
}
}
}
/// Const helper to check architecture /// Const helper to check architecture
pub const fn have_basepri() -> bool { pub const fn have_basepri() -> bool {
#[cfg(have_basepri)] #[cfg(have_basepri)]

View file

@ -1,9 +1,9 @@
use super::atomic::{AtomicBool, Ordering};
use core::{ use core::{
cell::UnsafeCell, cell::UnsafeCell,
future::Future, future::Future,
mem::{self, MaybeUninit}, mem::{self, MaybeUninit},
pin::Pin, pin::Pin,
sync::atomic::{AtomicBool, Ordering},
task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
}; };
@ -53,9 +53,11 @@ impl<F: Future> AsyncTaskExecutor<F> {
self.running.load(Ordering::Relaxed) self.running.load(Ordering::Relaxed)
} }
/// Checks if a waker has pended the executor. /// Checks if a waker has pended the executor and simultaneously clears the flag.
pub fn is_pending(&self) -> bool { pub fn check_and_clear_pending(&self) -> bool {
self.pending.load(Ordering::Relaxed) self.pending
.compare_exchange(true, false, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
} }
// Used by wakers to indicate that the executor needs to run. // Used by wakers to indicate that the executor needs to run.
@ -80,6 +82,7 @@ impl<F: Future> AsyncTaskExecutor<F> {
debug_assert!(self.running.load(Ordering::Relaxed)); debug_assert!(self.running.load(Ordering::Relaxed));
self.task.get().write(MaybeUninit::new(future)); self.task.get().write(MaybeUninit::new(future));
self.set_pending();
} }
/// Poll the future in the executor. /// Poll the future in the executor.

View file

@ -70,7 +70,15 @@ impl<'a> CargoCommand<'a> {
features, features,
mode, mode,
} => { } => {
let mut args = vec!["+nightly", self.name(), "--examples", "--target", target]; let mut args = vec![
"+nightly",
self.name(),
"--examples",
"--target",
target,
"--features",
"test-critical-section",
];
if let Some(feature_name) = features { if let Some(feature_name) = features {
args.extend_from_slice(&["--features", feature_name]); args.extend_from_slice(&["--features", feature_name]);