diff options
Diffstat (limited to 'embassy-executor-macros/src/macros/task.rs')
| -rw-r--r-- | embassy-executor-macros/src/macros/task.rs | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs new file mode 100644 index 000000000..5161e1020 --- /dev/null +++ b/embassy-executor-macros/src/macros/task.rs | |||
| @@ -0,0 +1,114 @@ | |||
| 1 | use darling::export::NestedMeta; | ||
| 2 | use darling::FromMeta; | ||
| 3 | use proc_macro2::{Span, TokenStream}; | ||
| 4 | use quote::{format_ident, quote}; | ||
| 5 | use syn::{parse_quote, Expr, ExprLit, ItemFn, Lit, LitInt, ReturnType, Type}; | ||
| 6 | |||
| 7 | use crate::util::ctxt::Ctxt; | ||
| 8 | |||
| 9 | #[derive(Debug, FromMeta)] | ||
| 10 | struct Args { | ||
| 11 | #[darling(default)] | ||
| 12 | pool_size: Option<syn::Expr>, | ||
| 13 | } | ||
| 14 | |||
| 15 | pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStream> { | ||
| 16 | let args = Args::from_list(args).map_err(|e| e.write_errors())?; | ||
| 17 | |||
| 18 | let pool_size = args.pool_size.unwrap_or(Expr::Lit(ExprLit { | ||
| 19 | attrs: vec![], | ||
| 20 | lit: Lit::Int(LitInt::new("1", Span::call_site())), | ||
| 21 | })); | ||
| 22 | |||
| 23 | let ctxt = Ctxt::new(); | ||
| 24 | |||
| 25 | if f.sig.asyncness.is_none() { | ||
| 26 | ctxt.error_spanned_by(&f.sig, "task functions must be async"); | ||
| 27 | } | ||
| 28 | if !f.sig.generics.params.is_empty() { | ||
| 29 | ctxt.error_spanned_by(&f.sig, "task functions must not be generic"); | ||
| 30 | } | ||
| 31 | if !f.sig.generics.where_clause.is_none() { | ||
| 32 | ctxt.error_spanned_by(&f.sig, "task functions must not have `where` clauses"); | ||
| 33 | } | ||
| 34 | if !f.sig.abi.is_none() { | ||
| 35 | ctxt.error_spanned_by(&f.sig, "task functions must not have an ABI qualifier"); | ||
| 36 | } | ||
| 37 | if !f.sig.variadic.is_none() { | ||
| 38 | ctxt.error_spanned_by(&f.sig, "task functions must not be variadic"); | ||
| 39 | } | ||
| 40 | match &f.sig.output { | ||
| 41 | ReturnType::Default => {} | ||
| 42 | ReturnType::Type(_, ty) => match &**ty { | ||
| 43 | Type::Tuple(tuple) if tuple.elems.is_empty() => {} | ||
| 44 | Type::Never(_) => {} | ||
| 45 | _ => ctxt.error_spanned_by( | ||
| 46 | &f.sig, | ||
| 47 | "task functions must either not return a value, return `()` or return `!`", | ||
| 48 | ), | ||
| 49 | }, | ||
| 50 | } | ||
| 51 | |||
| 52 | let mut arg_names = Vec::new(); | ||
| 53 | let mut fargs = f.sig.inputs.clone(); | ||
| 54 | |||
| 55 | for arg in fargs.iter_mut() { | ||
| 56 | match arg { | ||
| 57 | syn::FnArg::Receiver(_) => { | ||
| 58 | ctxt.error_spanned_by(arg, "task functions must not have receiver arguments"); | ||
| 59 | } | ||
| 60 | syn::FnArg::Typed(t) => match t.pat.as_mut() { | ||
| 61 | syn::Pat::Ident(id) => { | ||
| 62 | arg_names.push(id.ident.clone()); | ||
| 63 | id.mutability = None; | ||
| 64 | } | ||
| 65 | _ => { | ||
| 66 | ctxt.error_spanned_by(arg, "pattern matching in task arguments is not yet supported"); | ||
| 67 | } | ||
| 68 | }, | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | ctxt.check()?; | ||
| 73 | |||
| 74 | let task_ident = f.sig.ident.clone(); | ||
| 75 | let task_inner_ident = format_ident!("__{}_task", task_ident); | ||
| 76 | |||
| 77 | let mut task_inner = f; | ||
| 78 | let visibility = task_inner.vis.clone(); | ||
| 79 | task_inner.vis = syn::Visibility::Inherited; | ||
| 80 | task_inner.sig.ident = task_inner_ident.clone(); | ||
| 81 | |||
| 82 | #[cfg(feature = "nightly")] | ||
| 83 | let mut task_outer: ItemFn = parse_quote! { | ||
| 84 | #visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> { | ||
| 85 | type Fut = impl ::core::future::Future + 'static; | ||
| 86 | const POOL_SIZE: usize = #pool_size; | ||
| 87 | static POOL: ::embassy_executor::raw::TaskPool<Fut, POOL_SIZE> = ::embassy_executor::raw::TaskPool::new(); | ||
| 88 | unsafe { POOL._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) } | ||
| 89 | } | ||
| 90 | }; | ||
| 91 | #[cfg(not(feature = "nightly"))] | ||
| 92 | let mut task_outer: ItemFn = parse_quote! { | ||
| 93 | #visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> { | ||
| 94 | const POOL_SIZE: usize = #pool_size; | ||
| 95 | static POOL: ::embassy_executor::_export::TaskPoolRef = ::embassy_executor::_export::TaskPoolRef::new(); | ||
| 96 | unsafe { POOL.get::<_, POOL_SIZE>()._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) } | ||
| 97 | } | ||
| 98 | }; | ||
| 99 | |||
| 100 | task_outer.attrs.append(&mut task_inner.attrs.clone()); | ||
| 101 | |||
| 102 | let result = quote! { | ||
| 103 | // This is the user's task function, renamed. | ||
| 104 | // We put it outside the #task_ident fn below, because otherwise | ||
| 105 | // the items defined there (such as POOL) would be in scope | ||
| 106 | // in the user's code. | ||
| 107 | #[doc(hidden)] | ||
| 108 | #task_inner | ||
| 109 | |||
| 110 | #task_outer | ||
| 111 | }; | ||
| 112 | |||
| 113 | Ok(result) | ||
| 114 | } | ||
