diff options
| -rw-r--r-- | embassy-macros/src/macros/task.rs | 67 | ||||
| -rw-r--r-- | embassy/src/executor/arch/cortex_m.rs | 20 | ||||
| -rw-r--r-- | embassy/src/executor/spawner.rs | 2 | ||||
| -rw-r--r-- | examples/nrf/src/bin/multiprio.rs | 10 | ||||
| -rw-r--r-- | examples/nrf/src/bin/self_spawn.rs | 24 | ||||
| -rw-r--r-- | examples/stm32f3/src/bin/multiprio.rs | 10 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/multiprio.rs | 10 |
7 files changed, 79 insertions, 64 deletions
diff --git a/embassy-macros/src/macros/task.rs b/embassy-macros/src/macros/task.rs index 0cf6b423b..c37ad212c 100644 --- a/embassy-macros/src/macros/task.rs +++ b/embassy-macros/src/macros/task.rs | |||
| @@ -10,12 +10,10 @@ struct Args { | |||
| 10 | #[darling(default)] | 10 | #[darling(default)] |
| 11 | pool_size: Option<usize>, | 11 | pool_size: Option<usize>, |
| 12 | #[darling(default)] | 12 | #[darling(default)] |
| 13 | send: bool, | ||
| 14 | #[darling(default)] | ||
| 15 | embassy_prefix: ModulePrefix, | 13 | embassy_prefix: ModulePrefix, |
| 16 | } | 14 | } |
| 17 | 15 | ||
| 18 | pub fn run(args: syn::AttributeArgs, mut f: syn::ItemFn) -> Result<TokenStream, TokenStream> { | 16 | pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result<TokenStream, TokenStream> { |
| 19 | let args = Args::from_list(&args).map_err(|e| e.write_errors())?; | 17 | let args = Args::from_list(&args).map_err(|e| e.write_errors())?; |
| 20 | 18 | ||
| 21 | let embassy_prefix = args.embassy_prefix.append("embassy"); | 19 | let embassy_prefix = args.embassy_prefix.append("embassy"); |
| @@ -35,24 +33,27 @@ pub fn run(args: syn::AttributeArgs, mut f: syn::ItemFn) -> Result<TokenStream, | |||
| 35 | ctxt.error_spanned_by(&f.sig, "pool_size must be 1 or greater"); | 33 | ctxt.error_spanned_by(&f.sig, "pool_size must be 1 or greater"); |
| 36 | } | 34 | } |
| 37 | 35 | ||
| 38 | let mut arg_names: syn::punctuated::Punctuated<syn::Ident, syn::Token![,]> = | 36 | let mut arg_types = Vec::new(); |
| 39 | syn::punctuated::Punctuated::new(); | 37 | let mut arg_names = Vec::new(); |
| 38 | let mut arg_indexes = Vec::new(); | ||
| 40 | let mut fargs = f.sig.inputs.clone(); | 39 | let mut fargs = f.sig.inputs.clone(); |
| 41 | 40 | ||
| 42 | for arg in fargs.iter_mut() { | 41 | for (i, arg) in fargs.iter_mut().enumerate() { |
| 43 | match arg { | 42 | match arg { |
| 44 | syn::FnArg::Receiver(_) => { | 43 | syn::FnArg::Receiver(_) => { |
| 45 | ctxt.error_spanned_by(arg, "task functions must not have receiver arguments"); | 44 | ctxt.error_spanned_by(arg, "task functions must not have receiver arguments"); |
| 46 | } | 45 | } |
| 47 | syn::FnArg::Typed(t) => match t.pat.as_mut() { | 46 | syn::FnArg::Typed(t) => match t.pat.as_mut() { |
| 48 | syn::Pat::Ident(i) => { | 47 | syn::Pat::Ident(id) => { |
| 49 | arg_names.push(i.ident.clone()); | 48 | arg_names.push(id.ident.clone()); |
| 50 | i.mutability = None; | 49 | arg_types.push(t.ty.clone()); |
| 50 | arg_indexes.push(syn::Index::from(i)); | ||
| 51 | id.mutability = None; | ||
| 51 | } | 52 | } |
| 52 | _ => { | 53 | _ => { |
| 53 | ctxt.error_spanned_by( | 54 | ctxt.error_spanned_by( |
| 54 | arg, | 55 | arg, |
| 55 | "pattern matching in task arguments is not yet supporteds", | 56 | "pattern matching in task arguments is not yet supported", |
| 56 | ); | 57 | ); |
| 57 | } | 58 | } |
| 58 | }, | 59 | }, |
| @@ -63,40 +64,38 @@ pub fn run(args: syn::AttributeArgs, mut f: syn::ItemFn) -> Result<TokenStream, | |||
| 63 | 64 | ||
| 64 | let task_ident = f.sig.ident.clone(); | 65 | let task_ident = f.sig.ident.clone(); |
| 65 | let task_inner_ident = format_ident!("__{}_task", task_ident); | 66 | let task_inner_ident = format_ident!("__{}_task", task_ident); |
| 66 | let future_ident = format_ident!("__{}_Future", task_ident); | 67 | let mod_ident = format_ident!("__{}_mod", task_ident); |
| 67 | let pool_ident = format_ident!("__{}_POOL", task_ident); | 68 | let args_ident = format_ident!("__{}_args", task_ident); |
| 68 | let new_ts_ident = format_ident!("__{}_NEW_TASKSTORAGE", task_ident); | ||
| 69 | |||
| 70 | let visibility = &f.vis; | ||
| 71 | f.sig.ident = task_inner_ident.clone(); | ||
| 72 | let impl_ty = if args.send { | ||
| 73 | quote!(impl ::core::future::Future + Send + 'static) | ||
| 74 | } else { | ||
| 75 | quote!(impl ::core::future::Future + 'static) | ||
| 76 | }; | ||
| 77 | 69 | ||
| 78 | let attrs = &f.attrs; | 70 | let mut task_inner = f; |
| 79 | 71 | let visibility = task_inner.vis.clone(); | |
| 80 | let spawn_token = quote!(#embassy_path::executor::SpawnToken); | 72 | task_inner.vis = syn::Visibility::Inherited; |
| 81 | let task_storage = quote!(#embassy_path::executor::raw::TaskStorage); | 73 | task_inner.sig.ident = task_inner_ident.clone(); |
| 82 | 74 | ||
| 83 | let result = quote! { | 75 | let result = quote! { |
| 76 | #task_inner | ||
| 84 | 77 | ||
| 85 | #[allow(non_camel_case_types)] | 78 | #[allow(non_camel_case_types)] |
| 86 | type #future_ident = #impl_ty; | 79 | type #args_ident = (#(#arg_types,)*); |
| 80 | |||
| 81 | mod #mod_ident { | ||
| 82 | use #embassy_path::executor::SpawnToken; | ||
| 83 | use #embassy_path::executor::raw::TaskStorage; | ||
| 87 | 84 | ||
| 88 | #(#attrs)* | 85 | type Fut = impl ::core::future::Future + 'static; |
| 89 | #visibility fn #task_ident(#fargs) -> #spawn_token<#future_ident> { | ||
| 90 | #f | ||
| 91 | 86 | ||
| 92 | #[allow(non_upper_case_globals)] | ||
| 93 | #[allow(clippy::declare_interior_mutable_const)] | 87 | #[allow(clippy::declare_interior_mutable_const)] |
| 94 | const #new_ts_ident: #task_storage<#future_ident> = #task_storage::new(); | 88 | const NEW_TS: TaskStorage<Fut> = TaskStorage::new(); |
| 95 | 89 | ||
| 96 | #[allow(non_upper_case_globals)] | 90 | static POOL: [TaskStorage<Fut>; #pool_size] = [NEW_TS; #pool_size]; |
| 97 | static #pool_ident: [#task_storage<#future_ident>; #pool_size] = [#new_ts_ident; #pool_size]; | 91 | |
| 92 | pub(super) fn task(args: super::#args_ident) -> SpawnToken<Fut> { | ||
| 93 | unsafe { TaskStorage::spawn_pool(&POOL, move || super::#task_inner_ident(#(args.#arg_indexes),*)) } | ||
| 94 | } | ||
| 95 | } | ||
| 98 | 96 | ||
| 99 | unsafe { #task_storage::spawn_pool(&#pool_ident, move || #task_inner_ident(#arg_names)) } | 97 | #visibility fn #task_ident(#fargs) -> #embassy_path::executor::SpawnToken<impl ::core::future::Future + 'static> { |
| 98 | #mod_ident::task((#(#arg_names,)*)) | ||
| 100 | } | 99 | } |
| 101 | }; | 100 | }; |
| 102 | 101 | ||
diff --git a/embassy/src/executor/arch/cortex_m.rs b/embassy/src/executor/arch/cortex_m.rs index d23a595ff..87c9c3c87 100644 --- a/embassy/src/executor/arch/cortex_m.rs +++ b/embassy/src/executor/arch/cortex_m.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | use core::marker::PhantomData; | 1 | use core::marker::PhantomData; |
| 2 | use core::ptr; | 2 | use core::ptr; |
| 3 | 3 | ||
| 4 | use super::{raw, Spawner}; | 4 | use super::{raw, SendSpawner, Spawner}; |
| 5 | use crate::interrupt::{Interrupt, InterruptExt}; | 5 | use crate::interrupt::{Interrupt, InterruptExt}; |
| 6 | 6 | ||
| 7 | /// Thread mode executor, using WFE/SEV. | 7 | /// Thread mode executor, using WFE/SEV. |
| @@ -107,13 +107,13 @@ impl<I: Interrupt> InterruptExecutor<I> { | |||
| 107 | 107 | ||
| 108 | /// Start the executor. | 108 | /// Start the executor. |
| 109 | /// | 109 | /// |
| 110 | /// The `init` closure is called from interrupt mode, with a [`Spawner`] that spawns tasks on | 110 | /// This initializes the executor, configures and enables the interrupt, and returns. |
| 111 | /// this executor. Use it to spawn the initial task(s). After `init` returns, | 111 | /// The executor keeps running in the background through the interrupt. |
| 112 | /// the interrupt is configured so that the executor starts running the tasks. | ||
| 113 | /// Once the executor is started, `start` returns. | ||
| 114 | /// | 112 | /// |
| 115 | /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), | 113 | /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] |
| 116 | /// for example by passing it as an argument to the initial tasks. | 114 | /// is returned instead of a [`Spawner`] because the executor effectively runs in a |
| 115 | /// different "thread" (the interrupt), so spawning tasks on it is effectively | ||
| 116 | /// sending them. | ||
| 117 | /// | 117 | /// |
| 118 | /// This function requires `&'static mut self`. This means you have to store the | 118 | /// This function requires `&'static mut self`. This means you have to store the |
| 119 | /// Executor instance in a place where it'll live forever and grants you mutable | 119 | /// Executor instance in a place where it'll live forever and grants you mutable |
| @@ -122,16 +122,16 @@ impl<I: Interrupt> InterruptExecutor<I> { | |||
| 122 | /// - a [Forever](crate::util::Forever) (safe) | 122 | /// - a [Forever](crate::util::Forever) (safe) |
| 123 | /// - a `static mut` (unsafe) | 123 | /// - a `static mut` (unsafe) |
| 124 | /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) | 124 | /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) |
| 125 | pub fn start(&'static mut self, init: impl FnOnce(Spawner) + Send) { | 125 | pub fn start(&'static mut self) -> SendSpawner { |
| 126 | self.irq.disable(); | 126 | self.irq.disable(); |
| 127 | 127 | ||
| 128 | init(self.inner.spawner()); | ||
| 129 | |||
| 130 | self.irq.set_handler(|ctx| unsafe { | 128 | self.irq.set_handler(|ctx| unsafe { |
| 131 | let executor = &*(ctx as *const raw::Executor); | 129 | let executor = &*(ctx as *const raw::Executor); |
| 132 | executor.poll(); | 130 | executor.poll(); |
| 133 | }); | 131 | }); |
| 134 | self.irq.set_handler_context(&self.inner as *const _ as _); | 132 | self.irq.set_handler_context(&self.inner as *const _ as _); |
| 135 | self.irq.enable(); | 133 | self.irq.enable(); |
| 134 | |||
| 135 | self.inner.spawner().make_send() | ||
| 136 | } | 136 | } |
| 137 | } | 137 | } |
diff --git a/embassy/src/executor/spawner.rs b/embassy/src/executor/spawner.rs index ee0e60c2c..d1e4ff17d 100644 --- a/embassy/src/executor/spawner.rs +++ b/embassy/src/executor/spawner.rs | |||
| @@ -105,7 +105,6 @@ impl Spawner { | |||
| 105 | pub fn make_send(&self) -> SendSpawner { | 105 | pub fn make_send(&self) -> SendSpawner { |
| 106 | SendSpawner { | 106 | SendSpawner { |
| 107 | executor: self.executor, | 107 | executor: self.executor, |
| 108 | not_send: PhantomData, | ||
| 109 | } | 108 | } |
| 110 | } | 109 | } |
| 111 | } | 110 | } |
| @@ -120,7 +119,6 @@ impl Spawner { | |||
| 120 | #[derive(Copy, Clone)] | 119 | #[derive(Copy, Clone)] |
| 121 | pub struct SendSpawner { | 120 | pub struct SendSpawner { |
| 122 | executor: &'static raw::Executor, | 121 | executor: &'static raw::Executor, |
| 123 | not_send: PhantomData<*mut ()>, | ||
| 124 | } | 122 | } |
| 125 | 123 | ||
| 126 | unsafe impl Send for SendSpawner {} | 124 | unsafe impl Send for SendSpawner {} |
diff --git a/examples/nrf/src/bin/multiprio.rs b/examples/nrf/src/bin/multiprio.rs index e69f87d85..54f6606a9 100644 --- a/examples/nrf/src/bin/multiprio.rs +++ b/examples/nrf/src/bin/multiprio.rs | |||
| @@ -124,17 +124,15 @@ fn main() -> ! { | |||
| 124 | let irq = interrupt::take!(SWI1_EGU1); | 124 | let irq = interrupt::take!(SWI1_EGU1); |
| 125 | irq.set_priority(interrupt::Priority::P6); | 125 | irq.set_priority(interrupt::Priority::P6); |
| 126 | let executor = EXECUTOR_HIGH.put(InterruptExecutor::new(irq)); | 126 | let executor = EXECUTOR_HIGH.put(InterruptExecutor::new(irq)); |
| 127 | executor.start(|spawner| { | 127 | let spawner = executor.start(); |
| 128 | unwrap!(spawner.spawn(run_high())); | 128 | unwrap!(spawner.spawn(run_high())); |
| 129 | }); | ||
| 130 | 129 | ||
| 131 | // Medium-priority executor: SWI0_EGU0, priority level 7 | 130 | // Medium-priority executor: SWI0_EGU0, priority level 7 |
| 132 | let irq = interrupt::take!(SWI0_EGU0); | 131 | let irq = interrupt::take!(SWI0_EGU0); |
| 133 | irq.set_priority(interrupt::Priority::P7); | 132 | irq.set_priority(interrupt::Priority::P7); |
| 134 | let executor = EXECUTOR_MED.put(InterruptExecutor::new(irq)); | 133 | let executor = EXECUTOR_MED.put(InterruptExecutor::new(irq)); |
| 135 | executor.start(|spawner| { | 134 | let spawner = executor.start(); |
| 136 | unwrap!(spawner.spawn(run_med())); | 135 | unwrap!(spawner.spawn(run_med())); |
| 137 | }); | ||
| 138 | 136 | ||
| 139 | // Low priority executor: runs in thread mode, using WFE/SEV | 137 | // Low priority executor: runs in thread mode, using WFE/SEV |
| 140 | let executor = EXECUTOR_LOW.put(Executor::new()); | 138 | let executor = EXECUTOR_LOW.put(Executor::new()); |
diff --git a/examples/nrf/src/bin/self_spawn.rs b/examples/nrf/src/bin/self_spawn.rs new file mode 100644 index 000000000..35e73a8dd --- /dev/null +++ b/examples/nrf/src/bin/self_spawn.rs | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{info, unwrap}; | ||
| 6 | use embassy::executor::Spawner; | ||
| 7 | use embassy::time::{Duration, Timer}; | ||
| 8 | use embassy_nrf::Peripherals; | ||
| 9 | |||
| 10 | use defmt_rtt as _; // global logger | ||
| 11 | use panic_probe as _; | ||
| 12 | |||
| 13 | #[embassy::task(pool_size = 2)] | ||
| 14 | async fn my_task(spawner: Spawner, n: u32) { | ||
| 15 | Timer::after(Duration::from_secs(1)).await; | ||
| 16 | info!("Spawning self! {}", n); | ||
| 17 | unwrap!(spawner.spawn(my_task(spawner, n + 1))); | ||
| 18 | } | ||
| 19 | |||
| 20 | #[embassy::main] | ||
| 21 | async fn main(spawner: Spawner, _p: Peripherals) { | ||
| 22 | info!("Hello World!"); | ||
| 23 | unwrap!(spawner.spawn(my_task(spawner, 0))); | ||
| 24 | } | ||
diff --git a/examples/stm32f3/src/bin/multiprio.rs b/examples/stm32f3/src/bin/multiprio.rs index 1c9401549..02380de72 100644 --- a/examples/stm32f3/src/bin/multiprio.rs +++ b/examples/stm32f3/src/bin/multiprio.rs | |||
| @@ -124,17 +124,15 @@ fn main() -> ! { | |||
| 124 | let irq = interrupt::take!(UART4); | 124 | let irq = interrupt::take!(UART4); |
| 125 | irq.set_priority(interrupt::Priority::P6); | 125 | irq.set_priority(interrupt::Priority::P6); |
| 126 | let executor = EXECUTOR_HIGH.put(InterruptExecutor::new(irq)); | 126 | let executor = EXECUTOR_HIGH.put(InterruptExecutor::new(irq)); |
| 127 | executor.start(|spawner| { | 127 | let spawner = executor.start(); |
| 128 | unwrap!(spawner.spawn(run_high())); | 128 | unwrap!(spawner.spawn(run_high())); |
| 129 | }); | ||
| 130 | 129 | ||
| 131 | // Medium-priority executor: SWI0_EGU0, priority level 7 | 130 | // Medium-priority executor: SWI0_EGU0, priority level 7 |
| 132 | let irq = interrupt::take!(UART5); | 131 | let irq = interrupt::take!(UART5); |
| 133 | irq.set_priority(interrupt::Priority::P7); | 132 | irq.set_priority(interrupt::Priority::P7); |
| 134 | let executor = EXECUTOR_MED.put(InterruptExecutor::new(irq)); | 133 | let executor = EXECUTOR_MED.put(InterruptExecutor::new(irq)); |
| 135 | executor.start(|spawner| { | 134 | let spawner = executor.start(); |
| 136 | unwrap!(spawner.spawn(run_med())); | 135 | unwrap!(spawner.spawn(run_med())); |
| 137 | }); | ||
| 138 | 136 | ||
| 139 | // Low priority executor: runs in thread mode, using WFE/SEV | 137 | // Low priority executor: runs in thread mode, using WFE/SEV |
| 140 | let executor = EXECUTOR_LOW.put(Executor::new()); | 138 | let executor = EXECUTOR_LOW.put(Executor::new()); |
diff --git a/examples/stm32f4/src/bin/multiprio.rs b/examples/stm32f4/src/bin/multiprio.rs index 1c9401549..02380de72 100644 --- a/examples/stm32f4/src/bin/multiprio.rs +++ b/examples/stm32f4/src/bin/multiprio.rs | |||
| @@ -124,17 +124,15 @@ fn main() -> ! { | |||
| 124 | let irq = interrupt::take!(UART4); | 124 | let irq = interrupt::take!(UART4); |
| 125 | irq.set_priority(interrupt::Priority::P6); | 125 | irq.set_priority(interrupt::Priority::P6); |
| 126 | let executor = EXECUTOR_HIGH.put(InterruptExecutor::new(irq)); | 126 | let executor = EXECUTOR_HIGH.put(InterruptExecutor::new(irq)); |
| 127 | executor.start(|spawner| { | 127 | let spawner = executor.start(); |
| 128 | unwrap!(spawner.spawn(run_high())); | 128 | unwrap!(spawner.spawn(run_high())); |
| 129 | }); | ||
| 130 | 129 | ||
| 131 | // Medium-priority executor: SWI0_EGU0, priority level 7 | 130 | // Medium-priority executor: SWI0_EGU0, priority level 7 |
| 132 | let irq = interrupt::take!(UART5); | 131 | let irq = interrupt::take!(UART5); |
| 133 | irq.set_priority(interrupt::Priority::P7); | 132 | irq.set_priority(interrupt::Priority::P7); |
| 134 | let executor = EXECUTOR_MED.put(InterruptExecutor::new(irq)); | 133 | let executor = EXECUTOR_MED.put(InterruptExecutor::new(irq)); |
| 135 | executor.start(|spawner| { | 134 | let spawner = executor.start(); |
| 136 | unwrap!(spawner.spawn(run_med())); | 135 | unwrap!(spawner.spawn(run_med())); |
| 137 | }); | ||
| 138 | 136 | ||
| 139 | // Low priority executor: runs in thread mode, using WFE/SEV | 137 | // Low priority executor: runs in thread mode, using WFE/SEV |
| 140 | let executor = EXECUTOR_LOW.put(Executor::new()); | 138 | let executor = EXECUTOR_LOW.put(Executor::new()); |
