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
hello from async2
hello from async
hello from async with args a: 1, b: 2
idle

View file

@ -27,6 +27,7 @@ mod app {
hprintln!("init");
async_task::spawn().unwrap();
async_task_args::spawn(1, 2).unwrap();
async_task2::spawn().unwrap();
(Shared { a: 0 }, Local {})
@ -53,6 +54,11 @@ mod app {
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])]
async fn async_task2(cx: async_task2::Context) {
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 (input_args, input_tupled, input_untupled, input_ty) =
util::regroup_inputs(&spawnee.inputs);
// Spawn caller
items.push(quote!(
@ -153,17 +155,18 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
/// Spawns the task directly
#[allow(non_snake_case)]
#[doc(hidden)]
pub fn #internal_spawn_ident() -> Result<(), ()> {
pub fn #internal_spawn_ident(#(#input_args,)*) -> Result<(), #input_ty> {
if #exec_name.try_reserve() {
// This unsafe is protected by `try_reserve`, see its documentation for details
unsafe {
// TODO: Add args here
#exec_name.spawn_unchecked(#name(#name::Context::new()));
#exec_name.spawn_unchecked(#name(#name::Context::new() #(,#input_untupled)*));
}
#pend_interrupt
Ok(())
} else {
Err(())
Err(#input_tupled)
}
}
));

View file

@ -36,12 +36,13 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
let attrs = &task.attrs;
let cfgs = &task.cfgs;
let stmts = &task.stmts;
let inputs = &task.inputs;
user_tasks.push(quote!(
#(#attrs)*
#(#cfgs)*
#[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::prelude::*;

View file

@ -1,9 +1,8 @@
use core::sync::atomic::{AtomicUsize, Ordering};
use crate::syntax::{ast::App, Context};
use core::sync::atomic::{AtomicUsize, Ordering};
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::quote;
use syn::{Attribute, Ident};
use syn::{Attribute, Ident, PatType};
const RTIC_INTERNAL: &str = "__rtic_internal";
@ -94,6 +93,55 @@ pub fn link_section_uninit() -> TokenStream2 {
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
pub fn get_task_name(ctxt: Context, app: &App) -> Ident {
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();
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

View file

@ -1,6 +1,6 @@
//! 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;
@ -205,6 +205,9 @@ pub struct SoftwareTask {
/// The context argument
pub context: Box<Pat>,
/// The inputs of this software task
pub inputs: Vec<PatType>,
/// The statements that make up the task handler
pub stmts: Vec<Stmt>,

View file

@ -15,25 +15,20 @@ impl HardwareTask {
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 let Some(context) = util::parse_inputs(item.sig.inputs, &name) {
let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs);
if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) {
if rest.is_empty() {
let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs);
return Ok(HardwareTask {
args,
cfgs,
attrs,
context,
stmts: item.block.stmts,
is_extern: false,
});
return Ok(HardwareTask {
args,
cfgs,
attrs,
context,
stmts: item.block.stmts,
is_extern: false,
});
}
}
}
@ -56,25 +51,20 @@ impl HardwareTask {
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 let Some(context) = util::parse_inputs(item.sig.inputs, &name) {
let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs);
if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) {
if rest.is_empty() {
let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs);
return Ok(HardwareTask {
args,
cfgs,
attrs,
context,
stmts: Vec::<Stmt>::new(),
is_extern: true,
});
return Ok(HardwareTask {
args,
cfgs,
attrs,
context,
stmts: Vec::<Stmt>::new(),
is_extern: true,
});
}
}
}

View file

@ -21,14 +21,16 @@ impl Idle {
let name = item.sig.ident.to_string();
if valid_signature {
if let Some(context) = util::parse_inputs(item.sig.inputs, &name) {
return Ok(Idle {
args,
attrs: item.attrs,
context,
name: item.sig.ident,
stmts: item.block.stmts,
});
if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) {
if rest.is_empty() {
return Ok(Idle {
args,
attrs: item.attrs,
context,
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)) =
util::type_is_init_return(&item.sig.output)
{
if let Some(context) = util::parse_inputs(item.sig.inputs, &name) {
return Ok(Init {
args,
attrs: item.attrs,
context,
name: item.sig.ident,
stmts: item.block.stmts,
user_shared_struct,
user_local_struct,
});
if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) {
if rest.is_empty() {
return Ok(Init {
args,
attrs: item.attrs,
context,
name: item.sig.ident,
stmts: item.block.stmts,
user_shared_struct,
user_local_struct,
});
}
}
}
}

View file

@ -17,7 +17,7 @@ impl SoftwareTask {
let name = item.sig.ident.to_string();
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);
return Ok(SoftwareTask {
@ -25,6 +25,7 @@ impl SoftwareTask {
attrs,
cfgs,
context,
inputs,
stmts: item.block.stmts,
is_extern: false,
});
@ -33,7 +34,7 @@ impl SoftwareTask {
Err(parse::Error::new(
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();
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);
return Ok(SoftwareTask {
@ -60,6 +61,7 @@ impl SoftwareTask {
attrs,
cfgs,
context,
inputs,
stmts: Vec::<Stmt>::new(),
is_extern: true,
});
@ -68,7 +70,7 @@ impl SoftwareTask {
Err(parse::Error::new(
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},
punctuated::Punctuated,
spanned::Spanned,
Abi, AttrStyle, Attribute, Expr, FnArg, ForeignItemFn, Ident, ItemFn, Pat, Path, PathArguments,
ReturnType, Token, Type, Visibility,
Abi, AttrStyle, Attribute, Expr, FnArg, ForeignItemFn, Ident, ItemFn, Pat, PatType, Path,
PathArguments, ReturnType, Token, Type, Visibility,
};
use crate::syntax::{
@ -231,19 +231,29 @@ pub fn parse_local_resources(content: ParseStream<'_>) -> parse::Result<LocalRes
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();
if let Some(FnArg::Typed(first)) = inputs.next() {
if type_is_path(&first.ty, &[name, "Context"]) {
// No more inputs
if inputs.next().is_none() {
return Some(first.pat);
match inputs.next() {
Some(FnArg::Typed(first)) => {
if type_is_path(&first.ty, &[name, "Context"]) {
let rest = inputs
.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 {

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
|
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
|
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
|
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
|
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
|
15 | fn foo(_: foo::Context) {}