From 340b08f053487b251e9d82778faea3a7c49eda8d Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 10 Jan 2023 21:03:10 +0100 Subject: [PATCH] Async tasks can now take arguments at spawn again --- ci/expected/async-task.run | 1 + examples/async-task.rs | 6 +++ macros/src/codegen/module.rs | 11 +++-- macros/src/codegen/software_tasks.rs | 3 +- macros/src/codegen/util.rs | 54 ++++++++++++++++++++-- macros/src/syntax/analyze.rs | 5 ++ macros/src/syntax/ast.rs | 5 +- macros/src/syntax/parse/hardware_task.rs | 58 ++++++++++-------------- macros/src/syntax/parse/idle.rs | 18 ++++---- macros/src/syntax/parse/init.rs | 22 +++++---- macros/src/syntax/parse/software_task.rs | 10 ++-- macros/src/syntax/parse/util.rs | 30 ++++++++---- macros/ui/task-divergent.stderr | 2 +- macros/ui/task-no-context.stderr | 2 +- macros/ui/task-pub.stderr | 2 +- macros/ui/task-unsafe.stderr | 2 +- macros/ui/task-zero-prio.stderr | 2 +- 17 files changed, 153 insertions(+), 80 deletions(-) diff --git a/ci/expected/async-task.run b/ci/expected/async-task.run index 6787fa8fae..1f93a4c766 100644 --- a/ci/expected/async-task.run +++ b/ci/expected/async-task.run @@ -1,4 +1,5 @@ init hello from async2 hello from async +hello from async with args a: 1, b: 2 idle diff --git a/examples/async-task.rs b/examples/async-task.rs index 780bc08157..e1ab143258 100644 --- a/examples/async-task.rs +++ b/examples/async-task.rs @@ -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; diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 19cf2417bb..666bd0420a 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -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) } } )); diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index b923283269..34fc851a8c 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -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::*; diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index 0558d9d102..e121487c70 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -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, + // tupled e.g. `_0`, `(_0, _1)` + TokenStream2, + // untupled e.g. &[`_0`], &[`_0`, `_1`] + Vec, + // 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 { diff --git a/macros/src/syntax/analyze.rs b/macros/src/syntax/analyze.rs index b70ceb8b38..3ed1487741 100644 --- a/macros/src/syntax/analyze.rs +++ b/macros/src/syntax/analyze.rs @@ -287,6 +287,11 @@ pub(crate) fn app(app: &App) -> Result { 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 diff --git a/macros/src/syntax/ast.rs b/macros/src/syntax/ast.rs index da6016add8..27e6773f7f 100644 --- a/macros/src/syntax/ast.rs +++ b/macros/src/syntax/ast.rs @@ -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, + /// The inputs of this software task + pub inputs: Vec, + /// The statements that make up the task handler pub stmts: Vec, diff --git a/macros/src/syntax/parse/hardware_task.rs b/macros/src/syntax/parse/hardware_task.rs index c13426f4a3..7f6dfbe4c3 100644 --- a/macros/src/syntax/parse/hardware_task.rs +++ b/macros/src/syntax/parse/hardware_task.rs @@ -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::::new(), - is_extern: true, - }); + return Ok(HardwareTask { + args, + cfgs, + attrs, + context, + stmts: Vec::::new(), + is_extern: true, + }); + } } } diff --git a/macros/src/syntax/parse/idle.rs b/macros/src/syntax/parse/idle.rs index f049cca85e..124c136684 100644 --- a/macros/src/syntax/parse/idle.rs +++ b/macros/src/syntax/parse/idle.rs @@ -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, + }); + } } } diff --git a/macros/src/syntax/parse/init.rs b/macros/src/syntax/parse/init.rs index 23130c85ac..0aea20bd32 100644 --- a/macros/src/syntax/parse/init.rs +++ b/macros/src/syntax/parse/init.rs @@ -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, + }); + } } } } diff --git a/macros/src/syntax/parse/software_task.rs b/macros/src/syntax/parse/software_task.rs index fb9b37c442..769aa653da 100644 --- a/macros/src/syntax/parse/software_task.rs +++ b/macros/src/syntax/parse/software_task.rs @@ -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::::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, ..)`"), )) } } diff --git a/macros/src/syntax/parse/util.rs b/macros/src/syntax/parse/util.rs index 900ef9d66f..5a5e0c0ef2 100644 --- a/macros/src/syntax/parse/util.rs +++ b/macros/src/syntax/parse/util.rs @@ -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, name: &str) -> Option> { +type ParseInputResult = Option<(Box, Result, FnArg>)>; + +pub fn parse_inputs(inputs: Punctuated, 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::, _>>(); + + Some((first.pat, rest)) + } else { + None } } - } - None + _ => None, + } } pub fn type_is_bottom(ty: &ReturnType) -> bool { diff --git a/macros/ui/task-divergent.stderr b/macros/ui/task-divergent.stderr index bd22bd3358..dd002080b7 100644 --- a/macros/ui/task-divergent.stderr +++ b/macros/ui/task-divergent.stderr @@ -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) -> ! { diff --git a/macros/ui/task-no-context.stderr b/macros/ui/task-no-context.stderr index 91239a17b0..62147aab95 100644 --- a/macros/ui/task-no-context.stderr +++ b/macros/ui/task-no-context.stderr @@ -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() {} diff --git a/macros/ui/task-pub.stderr b/macros/ui/task-pub.stderr index 72c4e637d1..7b9813d848 100644 --- a/macros/ui/task-pub.stderr +++ b/macros/ui/task-pub.stderr @@ -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) {} diff --git a/macros/ui/task-unsafe.stderr b/macros/ui/task-unsafe.stderr index 4908481311..90ac76fe01 100644 --- a/macros/ui/task-unsafe.stderr +++ b/macros/ui/task-unsafe.stderr @@ -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) {} diff --git a/macros/ui/task-zero-prio.stderr b/macros/ui/task-zero-prio.stderr index f2d82231e3..1ab9aab690 100644 --- a/macros/ui/task-zero-prio.stderr +++ b/macros/ui/task-zero-prio.stderr @@ -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) {}