From 5a07ea5d851768223e2e41342e69d14c1afb2b2b Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 9 Mar 2025 20:55:11 +0100 Subject: Add support for Cortex-A/R --- embassy-executor-macros/src/lib.rs | 25 +++++++++++++++++++++++++ embassy-executor-macros/src/macros/main.rs | 6 ++++++ 2 files changed, 31 insertions(+) (limited to 'embassy-executor-macros/src') diff --git a/embassy-executor-macros/src/lib.rs b/embassy-executor-macros/src/lib.rs index 8e737db6a..962ce2e75 100644 --- a/embassy-executor-macros/src/lib.rs +++ b/embassy-executor-macros/src/lib.rs @@ -70,6 +70,31 @@ pub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream { main::run(args.into(), item.into(), &main::ARCH_CORTEX_M).into() } +/// Creates a new `executor` instance and declares an application entry point for Cortex-A/R +/// spawning the corresponding function body as an async task. +/// +/// The following restrictions apply: +/// +/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it +/// can use to spawn additional tasks. +/// * The function must be declared `async`. +/// * The function must not use generics. +/// * Only a single `main` task may be declared. +/// +/// ## Examples +/// Spawning a task: +/// +/// ``` rust +/// #[embassy_executor::main] +/// async fn main(_s: embassy_executor::Spawner) { +/// // Function body +/// } +/// ``` +#[proc_macro_attribute] +pub fn main_cortex_ar(args: TokenStream, item: TokenStream) -> TokenStream { + main::run(args.into(), item.into(), &main::ARCH_CORTEX_AR).into() +} + /// Creates a new `executor` instance and declares an architecture agnostic application entry point spawning /// the corresponding function body as an async task. /// diff --git a/embassy-executor-macros/src/macros/main.rs b/embassy-executor-macros/src/macros/main.rs index 95722b19b..a0e7b3401 100644 --- a/embassy-executor-macros/src/macros/main.rs +++ b/embassy-executor-macros/src/macros/main.rs @@ -37,6 +37,12 @@ pub static ARCH_CORTEX_M: Arch = Arch { executor_required: false, }; +pub static ARCH_CORTEX_AR: Arch = Arch { + default_entry: None, + flavor: Flavor::Standard, + executor_required: false, +}; + pub static ARCH_SPIN: Arch = Arch { default_entry: None, flavor: Flavor::Standard, -- cgit From a4d4f62a1e0e808ec3dd93e282f517a2f8ad9fa5 Mon Sep 17 00:00:00 2001 From: Matthew Tran <0e4ef622@gmail.com> Date: Wed, 28 May 2025 22:00:25 -0500 Subject: Allow `-> impl Future` in #[task] --- embassy-executor-macros/src/macros/task.rs | 34 ++++++++++++++++++------------ 1 file changed, 20 insertions(+), 14 deletions(-) (limited to 'embassy-executor-macros/src') diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs index 91d6beee8..91bf8e940 100644 --- a/embassy-executor-macros/src/macros/task.rs +++ b/embassy-executor-macros/src/macros/task.rs @@ -51,7 +51,11 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { .embassy_executor .unwrap_or(Expr::Verbatim(TokenStream::from_str("::embassy_executor").unwrap())); - if f.sig.asyncness.is_none() { + let returns_impl_trait = match &f.sig.output { + ReturnType::Type(_, ty) => matches!(**ty, Type::ImplTrait(_)), + _ => false, + }; + if f.sig.asyncness.is_none() && !returns_impl_trait { error(&mut errors, &f.sig, "task functions must be async"); } if !f.sig.generics.params.is_empty() { @@ -66,17 +70,19 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { if !f.sig.variadic.is_none() { error(&mut errors, &f.sig, "task functions must not be variadic"); } - match &f.sig.output { - ReturnType::Default => {} - ReturnType::Type(_, ty) => match &**ty { - Type::Tuple(tuple) if tuple.elems.is_empty() => {} - Type::Never(_) => {} - _ => error( - &mut errors, - &f.sig, - "task functions must either not return a value, return `()` or return `!`", - ), - }, + if f.sig.asyncness.is_some() { + match &f.sig.output { + ReturnType::Default => {} + ReturnType::Type(_, ty) => match &**ty { + Type::Tuple(tuple) if tuple.elems.is_empty() => {} + Type::Never(_) => {} + _ => error( + &mut errors, + &f.sig, + "task functions must either not return a value, return `()` or return `!`", + ), + }, + } } let mut args = Vec::new(); @@ -128,12 +134,12 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { #[cfg(feature = "nightly")] let mut task_outer_body = quote! { trait _EmbassyInternalTaskTrait { - type Fut: ::core::future::Future + 'static; + type Fut: ::core::future::Future + 'static; fn construct(#fargs) -> Self::Fut; } impl _EmbassyInternalTaskTrait for () { - type Fut = impl core::future::Future + 'static; + type Fut = impl core::future::Future + 'static; fn construct(#fargs) -> Self::Fut { #task_inner_ident(#(#full_args,)*) } -- cgit From 2fe2a0cf9c15bf7e84134cd7fec48017bb0a0db7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 8 Jul 2025 20:19:01 +0200 Subject: excutor: fix Send unsoundness with `-> impl Future` tasks. --- embassy-executor-macros/src/macros/task.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'embassy-executor-macros/src') diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs index 91bf8e940..1c5e3571d 100644 --- a/embassy-executor-macros/src/macros/task.rs +++ b/embassy-executor-macros/src/macros/task.rs @@ -131,6 +131,12 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { )); } + let spawn = if returns_impl_trait { + quote!(spawn) + } else { + quote!(_spawn_async_fn) + }; + #[cfg(feature = "nightly")] let mut task_outer_body = quote! { trait _EmbassyInternalTaskTrait { @@ -147,7 +153,7 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { const POOL_SIZE: usize = #pool_size; static POOL: #embassy_executor::raw::TaskPool<<() as _EmbassyInternalTaskTrait>::Fut, POOL_SIZE> = #embassy_executor::raw::TaskPool::new(); - unsafe { POOL._spawn_async_fn(move || <() as _EmbassyInternalTaskTrait>::construct(#(#full_args,)*)) } + unsafe { POOL.#spawn(move || <() as _EmbassyInternalTaskTrait>::construct(#(#full_args,)*)) } }; #[cfg(not(feature = "nightly"))] let mut task_outer_body = quote! { @@ -164,7 +170,7 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { {#embassy_executor::_export::task_pool_size::<_, _, _, POOL_SIZE>(#task_inner_ident)}, {#embassy_executor::_export::task_pool_align::<_, _, _, POOL_SIZE>(#task_inner_ident)}, > = unsafe { ::core::mem::transmute(#embassy_executor::_export::task_pool_new::<_, _, _, POOL_SIZE>(#task_inner_ident)) }; - unsafe { __task_pool_get(#task_inner_ident)._spawn_async_fn(move || #task_inner_ident(#(#full_args,)*)) } + unsafe { __task_pool_get(#task_inner_ident).#spawn(move || #task_inner_ident(#(#full_args,)*)) } }; let task_outer_attrs = task_inner.attrs.clone(); -- cgit From a52965dc5d3d0c706310998d3eda8bc15cd45b02 Mon Sep 17 00:00:00 2001 From: Brezak Date: Tue, 22 Jul 2025 20:56:46 +0200 Subject: embassy-executor: unsafe tasks as unsafe --- embassy-executor-macros/src/macros/task.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'embassy-executor-macros/src') diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs index 1c5e3571d..f01cc3b6c 100644 --- a/embassy-executor-macros/src/macros/task.rs +++ b/embassy-executor-macros/src/macros/task.rs @@ -120,6 +120,18 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { task_inner.vis = syn::Visibility::Inherited; task_inner.sig.ident = task_inner_ident.clone(); + // Forcefully mark the inner task as safe. + // SAFETY: We only ever call task_inner in functions + // with the same safety preconditions as task_inner + task_inner.sig.unsafety = None; + let task_body = task_inner.body; + task_inner.body = quote! { + #[allow(unused_unsafe, reason = "Not all function bodies may require being in an unsafe block")] + unsafe { + #task_body + } + }; + // assemble the original input arguments, // including any attributes that may have // been applied previously @@ -186,6 +198,7 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { // Copy the generics + where clause to avoid more spurious errors. let generics = &f.sig.generics; let where_clause = &f.sig.generics.where_clause; + let unsafety = &f.sig.unsafety; let result = quote! { // This is the user's task function, renamed. @@ -196,7 +209,7 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { #task_inner #(#task_outer_attrs)* - #visibility fn #task_ident #generics (#fargs) -> #embassy_executor::SpawnToken #where_clause{ + #visibility #unsafety fn #task_ident #generics (#fargs) -> #embassy_executor::SpawnToken #where_clause{ #task_outer_body } -- cgit From 1b42e624246f9355a91ef98ddf96d5af1b9b3687 Mon Sep 17 00:00:00 2001 From: Brezak Date: Wed, 23 Jul 2025 19:20:09 +0200 Subject: embassy-executor: explicitly return impl Future in task inner task --- embassy-executor-macros/src/macros/task.rs | 78 ++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 26 deletions(-) (limited to 'embassy-executor-macros/src') diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs index f01cc3b6c..5b360b128 100644 --- a/embassy-executor-macros/src/macros/task.rs +++ b/embassy-executor-macros/src/macros/task.rs @@ -112,25 +112,11 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { } } - let task_ident = f.sig.ident.clone(); - let task_inner_ident = format_ident!("__{}_task", task_ident); - - let mut task_inner = f.clone(); - let visibility = task_inner.vis.clone(); - task_inner.vis = syn::Visibility::Inherited; - task_inner.sig.ident = task_inner_ident.clone(); - - // Forcefully mark the inner task as safe. - // SAFETY: We only ever call task_inner in functions - // with the same safety preconditions as task_inner - task_inner.sig.unsafety = None; - let task_body = task_inner.body; - task_inner.body = quote! { - #[allow(unused_unsafe, reason = "Not all function bodies may require being in an unsafe block")] - unsafe { - #task_body - } - }; + // Copy the generics + where clause to avoid more spurious errors. + let generics = &f.sig.generics; + let where_clause = &f.sig.generics.where_clause; + let unsafety = &f.sig.unsafety; + let visibility = &f.vis; // assemble the original input arguments, // including any attributes that may have @@ -143,6 +129,51 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { )); } + let task_ident = f.sig.ident.clone(); + let task_inner_ident = format_ident!("__{}_task", task_ident); + + let task_inner_future_output = match &f.sig.output { + ReturnType::Default => quote! {-> impl ::core::future::Future}, + // Special case the never type since we can't stuff it into a `impl Future` + ReturnType::Type(arrow, maybe_never) if matches!(**maybe_never, Type::Never(_)) => quote! { + #arrow #maybe_never + }, + // Grab the arrow span, why not + ReturnType::Type(arrow, typ) if f.sig.asyncness.is_some() => quote! { + #arrow impl ::core::future::Future + }, + // We assume that if `f` isn't async, it must return `-> impl Future<...>` + // This is checked using traits later + ReturnType::Type(arrow, typ) => quote! { + #arrow #typ + }, + }; + + let task_inner_body = if errors.is_empty() { + quote! { + #f + + // SAFETY: All the preconditions to `#task_ident` apply to + // all contexts `#task_inner_ident` is called in + #unsafety { + #task_ident(#(#full_args,)*) + } + } + } else { + quote! { + async {::core::todo!()} + } + }; + + let task_inner = quote! { + #visibility fn #task_inner_ident #generics (#fargs) + #task_inner_future_output + #where_clause + { + #task_inner_body + } + }; + let spawn = if returns_impl_trait { quote!(spawn) } else { @@ -185,7 +216,7 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { unsafe { __task_pool_get(#task_inner_ident).#spawn(move || #task_inner_ident(#(#full_args,)*)) } }; - let task_outer_attrs = task_inner.attrs.clone(); + let task_outer_attrs = &f.attrs; if !errors.is_empty() { task_outer_body = quote! { @@ -195,11 +226,6 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { }; } - // Copy the generics + where clause to avoid more spurious errors. - let generics = &f.sig.generics; - let where_clause = &f.sig.generics.where_clause; - let unsafety = &f.sig.unsafety; - let result = quote! { // This is the user's task function, renamed. // We put it outside the #task_ident fn below, because otherwise @@ -226,7 +252,7 @@ fn check_arg_ty(errors: &mut TokenStream, ty: &Type) { impl<'a, 'ast> Visit<'ast> for Visitor<'a> { fn visit_type_reference(&mut self, i: &'ast syn::TypeReference) { - // only check for elided lifetime here. If not elided, it's checked by `visit_lifetime`. + // Only check for elided lifetime here. If not elided, it's checked by `visit_lifetime`. if i.lifetime.is_none() { error( self.errors, -- cgit From 539ff78ebbdedbb75d0faf940e3ee69f5e7f276a Mon Sep 17 00:00:00 2001 From: Brezak Date: Wed, 23 Jul 2025 19:51:31 +0200 Subject: embassy-executor: explicitly return impl Future in task inner task --- embassy-executor-macros/src/macros/task.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'embassy-executor-macros/src') diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs index 5b360b128..fc8673743 100644 --- a/embassy-executor-macros/src/macros/task.rs +++ b/embassy-executor-macros/src/macros/task.rs @@ -5,7 +5,7 @@ use darling::FromMeta; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; use syn::visit::{self, Visit}; -use syn::{Expr, ExprLit, Lit, LitInt, ReturnType, Type}; +use syn::{Expr, ExprLit, Lit, LitInt, ReturnType, Type, Visibility}; use crate::util::*; @@ -135,6 +135,13 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { let task_inner_future_output = match &f.sig.output { ReturnType::Default => quote! {-> impl ::core::future::Future}, // Special case the never type since we can't stuff it into a `impl Future` + ReturnType::Type(arrow, maybe_never) + if f.sig.asyncness.is_some() && matches!(**maybe_never, Type::Never(_)) => + { + quote! { + #arrow impl ::core::future::Future + } + } ReturnType::Type(arrow, maybe_never) if matches!(**maybe_never, Type::Never(_)) => quote! { #arrow #maybe_never }, @@ -149,14 +156,20 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { }, }; + // We have to rename the function since it might be recursive; + let mut task_inner_function = f.clone(); + let task_inner_function_ident = format_ident!("__{}_task_inner_function", task_ident); + task_inner_function.sig.ident = task_inner_function_ident.clone(); + task_inner_function.vis = Visibility::Inherited; + let task_inner_body = if errors.is_empty() { quote! { - #f + #task_inner_function // SAFETY: All the preconditions to `#task_ident` apply to // all contexts `#task_inner_ident` is called in #unsafety { - #task_ident(#(#full_args,)*) + #task_inner_function_ident(#(#full_args,)*) } } } else { -- cgit From 8aec341f28a00012e1771d5c35d2647e11830755 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 9 Jul 2025 01:49:31 +0200 Subject: executor: return error when creating the spawntoken, not when spawning. --- embassy-executor-macros/src/macros/main.rs | 4 ++-- embassy-executor-macros/src/macros/task.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'embassy-executor-macros/src') diff --git a/embassy-executor-macros/src/macros/main.rs b/embassy-executor-macros/src/macros/main.rs index a0e7b3401..fcc04d9c0 100644 --- a/embassy-executor-macros/src/macros/main.rs +++ b/embassy-executor-macros/src/macros/main.rs @@ -181,7 +181,7 @@ For example: `#[embassy_executor::main(entry = ..., executor = \"some_crate::Exe let mut executor = #executor::new(); let executor = unsafe { __make_static(&mut executor) }; executor.run(|spawner| { - spawner.must_spawn(__embassy_main(spawner)); + spawner.spawn(__embassy_main(spawner).unwrap()); }) }, ), @@ -191,7 +191,7 @@ For example: `#[embassy_executor::main(entry = ..., executor = \"some_crate::Exe let executor = ::std::boxed::Box::leak(::std::boxed::Box::new(#executor::new())); executor.start(|spawner| { - spawner.must_spawn(__embassy_main(spawner)); + spawner.spawn(__embassy_main(spawner).unwrap()); }); Ok(()) diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs index fc8673743..755948882 100644 --- a/embassy-executor-macros/src/macros/task.rs +++ b/embassy-executor-macros/src/macros/task.rs @@ -234,7 +234,7 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { if !errors.is_empty() { task_outer_body = quote! { #![allow(unused_variables, unreachable_code)] - let _x: #embassy_executor::SpawnToken<()> = ::core::todo!(); + let _x: ::core::result::Result<#embassy_executor::SpawnToken<()>, #embassy_executor::SpawnError> = ::core::todo!(); _x }; } @@ -248,7 +248,7 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { #task_inner #(#task_outer_attrs)* - #visibility #unsafety fn #task_ident #generics (#fargs) -> #embassy_executor::SpawnToken #where_clause{ + #visibility #unsafety fn #task_ident #generics (#fargs) -> ::core::result::Result<#embassy_executor::SpawnToken, #embassy_executor::SpawnError> #where_clause{ #task_outer_body } -- cgit From ba5a75cd7d0b97bafd02d3b95baf40fd36522fac Mon Sep 17 00:00:00 2001 From: Florian Grandel Date: Sat, 5 Jul 2025 18:25:53 +0200 Subject: executor: rtos-trace: name main task Assigns a name to the main task when (rtos-)tracing is active. This improves tracing usability with the SystemView backend. Signed-off-by: Florian Grandel --- embassy-executor-macros/src/macros/main.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'embassy-executor-macros/src') diff --git a/embassy-executor-macros/src/macros/main.rs b/embassy-executor-macros/src/macros/main.rs index fcc04d9c0..f043ff7f6 100644 --- a/embassy-executor-macros/src/macros/main.rs +++ b/embassy-executor-macros/src/macros/main.rs @@ -170,6 +170,14 @@ For example: `#[embassy_executor::main(entry = ..., executor = \"some_crate::Exe let f_body = f.body; let out = &f.sig.output; + let name_main_task = if cfg!(feature = "rtos-trace") { + quote!( + main_task.metadata().set_name("main\0"); + ) + } else { + quote!() + }; + let (main_ret, mut main_body) = match arch.flavor { Flavor::Standard => ( quote!(!), @@ -181,7 +189,9 @@ For example: `#[embassy_executor::main(entry = ..., executor = \"some_crate::Exe let mut executor = #executor::new(); let executor = unsafe { __make_static(&mut executor) }; executor.run(|spawner| { - spawner.spawn(__embassy_main(spawner).unwrap()); + let main_task = __embassy_main(spawner).unwrap(); + #name_main_task + spawner.spawn(main_task); }) }, ), @@ -191,7 +201,9 @@ For example: `#[embassy_executor::main(entry = ..., executor = \"some_crate::Exe let executor = ::std::boxed::Box::leak(::std::boxed::Box::new(#executor::new())); executor.start(|spawner| { - spawner.spawn(__embassy_main(spawner).unwrap()); + let main_task = __embassy_main(spawner).unwrap(); + #name_main_task + spawner.spawn(main_task); }); Ok(()) -- cgit From 286d887529c66d8d1b4c7b56849e7a95386d79db Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 5 Sep 2025 14:57:05 +0200 Subject: executor: always name main task `main`, not just with rtos-trace. Also fixes the warning about the `rtos-trace` feature not existing in embassy-executor-macros. --- embassy-executor-macros/src/macros/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'embassy-executor-macros/src') diff --git a/embassy-executor-macros/src/macros/main.rs b/embassy-executor-macros/src/macros/main.rs index f043ff7f6..dc470e51c 100644 --- a/embassy-executor-macros/src/macros/main.rs +++ b/embassy-executor-macros/src/macros/main.rs @@ -170,7 +170,7 @@ For example: `#[embassy_executor::main(entry = ..., executor = \"some_crate::Exe let f_body = f.body; let out = &f.sig.output; - let name_main_task = if cfg!(feature = "rtos-trace") { + let name_main_task = if cfg!(feature = "metadata-name") { quote!( main_task.metadata().set_name("main\0"); ) -- cgit