Async tasks can now take arguments at spawn again

This commit is contained in:
Emil Fresk 2023-01-10 21:03:10 +01:00 committed by Henrik Tjäder
parent ad2bf4e77c
commit 340b08f053
17 changed files with 153 additions and 80 deletions

View file

@ -1,4 +1,5 @@
init init
hello from async2 hello from async2
hello from async hello from async
hello from async with args a: 1, b: 2
idle idle

View file

@ -27,6 +27,7 @@ mod app {
hprintln!("init"); hprintln!("init");
async_task::spawn().unwrap(); async_task::spawn().unwrap();
async_task_args::spawn(1, 2).unwrap();
async_task2::spawn().unwrap(); async_task2::spawn().unwrap();
(Shared { a: 0 }, Local {}) (Shared { a: 0 }, Local {})
@ -53,6 +54,11 @@ mod app {
hprintln!("hello from async"); hprintln!("hello from async");
} }
#[task]
async fn async_task_args(_cx: async_task_args::Context, a: u32, b: i32) {
hprintln!("hello from async with args a: {}, b: {}", a, b);
}
#[task(priority = 2, shared = [a])] #[task(priority = 2, shared = [a])]
async fn async_task2(cx: async_task2::Context) { async fn async_task2(cx: async_task2::Context) {
let async_task2::SharedResources { a: _, .. } = cx.shared; let async_task2::SharedResources { a: _, .. } = cx.shared;

View file

@ -146,6 +146,8 @@ 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");
let (input_args, input_tupled, input_untupled, input_ty) =
util::regroup_inputs(&spawnee.inputs);
// Spawn caller // Spawn caller
items.push(quote!( items.push(quote!(
@ -153,17 +155,18 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
/// 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(#(#input_args,)*) -> Result<(), #input_ty> {
if #exec_name.try_reserve() { if #exec_name.try_reserve() {
// This unsafe is protected by `try_reserve`, see its documentation for details
unsafe { unsafe {
// TODO: Add args here #exec_name.spawn_unchecked(#name(#name::Context::new() #(,#input_untupled)*));
#exec_name.spawn_unchecked(#name(#name::Context::new()));
} }
#pend_interrupt #pend_interrupt
Ok(()) Ok(())
} else { } else {
Err(()) Err(#input_tupled)
} }
} }
)); ));

View file

@ -36,12 +36,13 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
let attrs = &task.attrs; let attrs = &task.attrs;
let cfgs = &task.cfgs; let cfgs = &task.cfgs;
let stmts = &task.stmts; let stmts = &task.stmts;
let inputs = &task.inputs;
user_tasks.push(quote!( user_tasks.push(quote!(
#(#attrs)* #(#attrs)*
#(#cfgs)* #(#cfgs)*
#[allow(non_snake_case)] #[allow(non_snake_case)]
async fn #name(#context: #name::Context<'static>) { async fn #name<'a>(#context: #name::Context<'a> #(,#inputs)*) {
use rtic::Mutex as _; use rtic::Mutex as _;
use rtic::mutex::prelude::*; use rtic::mutex::prelude::*;

View file

@ -1,9 +1,8 @@
use core::sync::atomic::{AtomicUsize, Ordering};
use crate::syntax::{ast::App, Context}; use crate::syntax::{ast::App, Context};
use core::sync::atomic::{AtomicUsize, Ordering};
use proc_macro2::{Span, TokenStream as TokenStream2}; use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::quote; use quote::quote;
use syn::{Attribute, Ident}; use syn::{Attribute, Ident, PatType};
const RTIC_INTERNAL: &str = "__rtic_internal"; const RTIC_INTERNAL: &str = "__rtic_internal";
@ -94,6 +93,55 @@ pub fn link_section_uninit() -> TokenStream2 {
quote!(#[link_section = #section]) quote!(#[link_section = #section])
} }
/// Regroups the inputs of a task
///
/// `inputs` could be &[`input: Foo`] OR &[`mut x: i32`, `ref y: i64`]
pub fn regroup_inputs(
inputs: &[PatType],
) -> (
// args e.g. &[`_0`], &[`_0: i32`, `_1: i64`]
Vec<TokenStream2>,
// tupled e.g. `_0`, `(_0, _1)`
TokenStream2,
// untupled e.g. &[`_0`], &[`_0`, `_1`]
Vec<TokenStream2>,
// ty e.g. `Foo`, `(i32, i64)`
TokenStream2,
) {
if inputs.len() == 1 {
let ty = &inputs[0].ty;
(
vec![quote!(_0: #ty)],
quote!(_0),
vec![quote!(_0)],
quote!(#ty),
)
} else {
let mut args = vec![];
let mut pats = vec![];
let mut tys = vec![];
for (i, input) in inputs.iter().enumerate() {
let i = Ident::new(&format!("_{}", i), Span::call_site());
let ty = &input.ty;
args.push(quote!(#i: #ty));
pats.push(quote!(#i));
tys.push(quote!(#ty));
}
let tupled = {
let pats = pats.clone();
quote!((#(#pats,)*))
};
let ty = quote!((#(#tys,)*));
(args, tupled, pats, ty)
}
}
/// Get the ident for the name of the task /// Get the ident for the name of the task
pub fn get_task_name(ctxt: Context, app: &App) -> Ident { pub fn get_task_name(ctxt: Context, app: &App) -> Ident {
let s = match ctxt { let s = match ctxt {

View file

@ -287,6 +287,11 @@ pub(crate) fn app(app: &App) -> Result<Analysis, syn::Error> {
let channel = channels.entry(spawnee_prio).or_default(); let channel = channels.entry(spawnee_prio).or_default();
channel.tasks.insert(name.clone()); channel.tasks.insert(name.clone());
// All inputs are send as we do not know from where they may be spawned.
spawnee.inputs.iter().for_each(|input| {
send_types.insert(input.ty.clone());
});
} }
// No channel should ever be empty // No channel should ever be empty

View file

@ -1,6 +1,6 @@
//! Abstract Syntax Tree //! Abstract Syntax Tree
use syn::{Attribute, Expr, Ident, Item, ItemUse, Pat, Path, Stmt, Type}; use syn::{Attribute, Expr, Ident, Item, ItemUse, Pat, PatType, Path, Stmt, Type};
use crate::syntax::Map; use crate::syntax::Map;
@ -205,6 +205,9 @@ pub struct SoftwareTask {
/// The context argument /// The context argument
pub context: Box<Pat>, pub context: Box<Pat>,
/// The inputs of this software task
pub inputs: Vec<PatType>,
/// The statements that make up the task handler /// The statements that make up the task handler
pub stmts: Vec<Stmt>, pub stmts: Vec<Stmt>,

View file

@ -15,25 +15,20 @@ impl HardwareTask {
let name = item.sig.ident.to_string(); let name = item.sig.ident.to_string();
if name == "init" || name == "idle" {
return Err(parse::Error::new(
span,
"tasks cannot be named `init` or `idle`",
));
}
if valid_signature { if valid_signature {
if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) {
let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); if rest.is_empty() {
let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs);
return Ok(HardwareTask { return Ok(HardwareTask {
args, args,
cfgs, cfgs,
attrs, attrs,
context, context,
stmts: item.block.stmts, stmts: item.block.stmts,
is_extern: false, is_extern: false,
}); });
}
} }
} }
@ -56,25 +51,20 @@ impl HardwareTask {
let name = item.sig.ident.to_string(); let name = item.sig.ident.to_string();
if name == "init" || name == "idle" {
return Err(parse::Error::new(
span,
"tasks cannot be named `init` or `idle`",
));
}
if valid_signature { if valid_signature {
if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) {
let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); if rest.is_empty() {
let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs);
return Ok(HardwareTask { return Ok(HardwareTask {
args, args,
cfgs, cfgs,
attrs, attrs,
context, context,
stmts: Vec::<Stmt>::new(), stmts: Vec::<Stmt>::new(),
is_extern: true, is_extern: true,
}); });
}
} }
} }

View file

@ -21,14 +21,16 @@ impl Idle {
let name = item.sig.ident.to_string(); let name = item.sig.ident.to_string();
if valid_signature { if valid_signature {
if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) {
return Ok(Idle { if rest.is_empty() {
args, return Ok(Idle {
attrs: item.attrs, args,
context, attrs: item.attrs,
name: item.sig.ident, context,
stmts: item.block.stmts, name: item.sig.ident,
}); stmts: item.block.stmts,
});
}
} }
} }

View file

@ -25,16 +25,18 @@ impl Init {
if let Ok((user_shared_struct, user_local_struct)) = if let Ok((user_shared_struct, user_local_struct)) =
util::type_is_init_return(&item.sig.output) util::type_is_init_return(&item.sig.output)
{ {
if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) {
return Ok(Init { if rest.is_empty() {
args, return Ok(Init {
attrs: item.attrs, args,
context, attrs: item.attrs,
name: item.sig.ident, context,
stmts: item.block.stmts, name: item.sig.ident,
user_shared_struct, stmts: item.block.stmts,
user_local_struct, user_shared_struct,
}); user_local_struct,
});
}
} }
} }
} }

View file

@ -17,7 +17,7 @@ impl SoftwareTask {
let name = item.sig.ident.to_string(); let name = item.sig.ident.to_string();
if valid_signature { if valid_signature {
if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { if let Some((context, Ok(inputs))) = util::parse_inputs(item.sig.inputs, &name) {
let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs);
return Ok(SoftwareTask { return Ok(SoftwareTask {
@ -25,6 +25,7 @@ impl SoftwareTask {
attrs, attrs,
cfgs, cfgs,
context, context,
inputs,
stmts: item.block.stmts, stmts: item.block.stmts,
is_extern: false, is_extern: false,
}); });
@ -33,7 +34,7 @@ impl SoftwareTask {
Err(parse::Error::new( Err(parse::Error::new(
span, span,
format!("this task handler must have type signature `async fn({name}::Context)`"), format!("this task handler must have type signature `async fn({name}::Context, ..)`"),
)) ))
} }
} }
@ -52,7 +53,7 @@ impl SoftwareTask {
let name = item.sig.ident.to_string(); let name = item.sig.ident.to_string();
if valid_signature { if valid_signature {
if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { if let Some((context, Ok(inputs))) = util::parse_inputs(item.sig.inputs, &name) {
let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs);
return Ok(SoftwareTask { return Ok(SoftwareTask {
@ -60,6 +61,7 @@ impl SoftwareTask {
attrs, attrs,
cfgs, cfgs,
context, context,
inputs,
stmts: Vec::<Stmt>::new(), stmts: Vec::<Stmt>::new(),
is_extern: true, is_extern: true,
}); });
@ -68,7 +70,7 @@ impl SoftwareTask {
Err(parse::Error::new( Err(parse::Error::new(
span, span,
format!("this task handler must have type signature `async fn({name}::Context)`"), format!("this task handler must have type signature `async fn({name}::Context, ..)`"),
)) ))
} }
} }

View file

@ -3,8 +3,8 @@ use syn::{
parse::{self, ParseStream}, parse::{self, ParseStream},
punctuated::Punctuated, punctuated::Punctuated,
spanned::Spanned, spanned::Spanned,
Abi, AttrStyle, Attribute, Expr, FnArg, ForeignItemFn, Ident, ItemFn, Pat, Path, PathArguments, Abi, AttrStyle, Attribute, Expr, FnArg, ForeignItemFn, Ident, ItemFn, Pat, PatType, Path,
ReturnType, Token, Type, Visibility, PathArguments, ReturnType, Token, Type, Visibility,
}; };
use crate::syntax::{ use crate::syntax::{
@ -231,19 +231,29 @@ pub fn parse_local_resources(content: ParseStream<'_>) -> parse::Result<LocalRes
Ok(resources) Ok(resources)
} }
pub fn parse_inputs(inputs: Punctuated<FnArg, Token![,]>, name: &str) -> Option<Box<Pat>> { type ParseInputResult = Option<(Box<Pat>, Result<Vec<PatType>, FnArg>)>;
pub fn parse_inputs(inputs: Punctuated<FnArg, Token![,]>, name: &str) -> ParseInputResult {
let mut inputs = inputs.into_iter(); let mut inputs = inputs.into_iter();
if let Some(FnArg::Typed(first)) = inputs.next() { match inputs.next() {
if type_is_path(&first.ty, &[name, "Context"]) { Some(FnArg::Typed(first)) => {
// No more inputs if type_is_path(&first.ty, &[name, "Context"]) {
if inputs.next().is_none() { let rest = inputs
return Some(first.pat); .map(|arg| match arg {
FnArg::Typed(arg) => Ok(arg),
_ => Err(arg),
})
.collect::<Result<Vec<_>, _>>();
Some((first.pat, rest))
} else {
None
} }
} }
}
None _ => None,
}
} }
pub fn type_is_bottom(ty: &ReturnType) -> bool { pub fn type_is_bottom(ty: &ReturnType) -> bool {

View file

@ -1,4 +1,4 @@
error: this task handler must have type signature `async fn(foo::Context)` error: this task handler must have type signature `async fn(foo::Context, ..)`
--> ui/task-divergent.rs:6:14 --> ui/task-divergent.rs:6:14
| |
6 | async fn foo(_: foo::Context) -> ! { 6 | async fn foo(_: foo::Context) -> ! {

View file

@ -1,4 +1,4 @@
error: this task handler must have type signature `async fn(foo::Context)` error: this task handler must have type signature `async fn(foo::Context, ..)`
--> ui/task-no-context.rs:6:14 --> ui/task-no-context.rs:6:14
| |
6 | async fn foo() {} 6 | async fn foo() {}

View file

@ -1,4 +1,4 @@
error: this task handler must have type signature `async fn(foo::Context)` error: this task handler must have type signature `async fn(foo::Context, ..)`
--> ui/task-pub.rs:6:18 --> ui/task-pub.rs:6:18
| |
6 | pub async fn foo(_: foo::Context) {} 6 | pub async fn foo(_: foo::Context) {}

View file

@ -1,4 +1,4 @@
error: this task handler must have type signature `async fn(foo::Context)` error: this task handler must have type signature `async fn(foo::Context, ..)`
--> ui/task-unsafe.rs:6:21 --> ui/task-unsafe.rs:6:21
| |
6 | async unsafe fn foo(_: foo::Context) {} 6 | async unsafe fn foo(_: foo::Context) {}

View file

@ -1,4 +1,4 @@
error: this task handler must have type signature `async fn(foo::Context)` error: this task handler must have type signature `async fn(foo::Context, ..)`
--> ui/task-zero-prio.rs:15:8 --> ui/task-zero-prio.rs:15:8
| |
15 | fn foo(_: foo::Context) {} 15 | fn foo(_: foo::Context) {}