mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-27 14:04:56 +01:00
Async tasks can now take arguments at spawn again
This commit is contained in:
parent
ad2bf4e77c
commit
340b08f053
17 changed files with 153 additions and 80 deletions
|
@ -1,4 +1,5 @@
|
|||
init
|
||||
hello from async2
|
||||
hello from async
|
||||
hello from async with args a: 1, b: 2
|
||||
idle
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
));
|
||||
|
|
|
@ -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::*;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>,
|
||||
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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, ..)`"),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) -> ! {
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -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) {}
|
||||
|
|
|
@ -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) {}
|
||||
|
|
|
@ -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) {}
|
||||
|
|
Loading…
Reference in a new issue