diff options
| author | Dario Nieuwenhuis <[email protected]> | 2022-04-27 03:23:54 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2022-04-27 04:56:41 +0200 |
| commit | 293f54d13406850d24d1226eb77989f4fa8db9f4 (patch) | |
| tree | c8831ae9d2d046549b3d64294408920b9b8a5597 | |
| parent | df814f9bbd5cc03f4d60eb8cb19374d23f0a84a0 (diff) | |
executor: add raw::TaskPool.
This simplifies the macro code a bit.
| -rw-r--r-- | embassy-macros/src/macros/task.rs | 9 | ||||
| -rw-r--r-- | embassy/src/executor/raw/mod.rs | 56 |
2 files changed, 42 insertions, 23 deletions
diff --git a/embassy-macros/src/macros/task.rs b/embassy-macros/src/macros/task.rs index 396ce18f2..c450982c9 100644 --- a/embassy-macros/src/macros/task.rs +++ b/embassy-macros/src/macros/task.rs | |||
| @@ -76,14 +76,11 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result<TokenStream, Toke | |||
| 76 | #visibility fn #task_ident(#fargs) -> #embassy_path::executor::SpawnToken<impl ::core::future::Future + 'static> { | 76 | #visibility fn #task_ident(#fargs) -> #embassy_path::executor::SpawnToken<impl ::core::future::Future + 'static> { |
| 77 | use ::core::future::Future; | 77 | use ::core::future::Future; |
| 78 | use #embassy_path::executor::SpawnToken; | 78 | use #embassy_path::executor::SpawnToken; |
| 79 | use #embassy_path::executor::raw::TaskStorage; | 79 | use #embassy_path::executor::raw::TaskPool; |
| 80 | 80 | ||
| 81 | type Fut = impl Future + 'static; | 81 | type Fut = impl Future + 'static; |
| 82 | 82 | ||
| 83 | #[allow(clippy::declare_interior_mutable_const)] | 83 | static POOL: TaskPool<Fut, #pool_size> = TaskPool::new(); |
| 84 | const NEW_TS: TaskStorage<Fut> = TaskStorage::new(); | ||
| 85 | |||
| 86 | static POOL: [TaskStorage<Fut>; #pool_size] = [NEW_TS; #pool_size]; | ||
| 87 | 84 | ||
| 88 | // Opaque type laundering, to obscure its origin! | 85 | // Opaque type laundering, to obscure its origin! |
| 89 | // Workaround for "opaque type's hidden type cannot be another opaque type from the same scope" | 86 | // Workaround for "opaque type's hidden type cannot be another opaque type from the same scope" |
| @@ -92,7 +89,7 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result<TokenStream, Toke | |||
| 92 | token | 89 | token |
| 93 | } | 90 | } |
| 94 | 91 | ||
| 95 | launder_tait(unsafe { TaskStorage::spawn_pool(&POOL, move || #task_inner_ident(#(#arg_names,)*)) }) | 92 | launder_tait(POOL.spawn(move || #task_inner_ident(#(#arg_names,)*))) |
| 96 | } | 93 | } |
| 97 | }; | 94 | }; |
| 98 | 95 | ||
diff --git a/embassy/src/executor/raw/mod.rs b/embassy/src/executor/raw/mod.rs index 3ae52ae31..5af35d868 100644 --- a/embassy/src/executor/raw/mod.rs +++ b/embassy/src/executor/raw/mod.rs | |||
| @@ -121,7 +121,7 @@ impl TaskHeader { | |||
| 121 | /// the memory for the task is allocated: on the stack, or on the heap with e.g. `Box::leak`, etc. | 121 | /// the memory for the task is allocated: on the stack, or on the heap with e.g. `Box::leak`, etc. |
| 122 | 122 | ||
| 123 | // repr(C) is needed to guarantee that the Task is located at offset 0 | 123 | // repr(C) is needed to guarantee that the Task is located at offset 0 |
| 124 | // This makes it safe to cast between Task and Task pointers. | 124 | // This makes it safe to cast between TaskHeader and TaskStorage pointers. |
| 125 | #[repr(C)] | 125 | #[repr(C)] |
| 126 | pub struct TaskStorage<F: Future + 'static> { | 126 | pub struct TaskStorage<F: Future + 'static> { |
| 127 | raw: TaskHeader, | 127 | raw: TaskHeader, |
| @@ -129,6 +129,9 @@ pub struct TaskStorage<F: Future + 'static> { | |||
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | impl<F: Future + 'static> TaskStorage<F> { | 131 | impl<F: Future + 'static> TaskStorage<F> { |
| 132 | #[cfg(feature = "nightly")] | ||
| 133 | const NEW: Self = Self::new(); | ||
| 134 | |||
| 132 | /// Create a new TaskStorage, in not-spawned state. | 135 | /// Create a new TaskStorage, in not-spawned state. |
| 133 | #[cfg(feature = "nightly")] | 136 | #[cfg(feature = "nightly")] |
| 134 | pub const fn new() -> Self { | 137 | pub const fn new() -> Self { |
| @@ -147,22 +150,6 @@ impl<F: Future + 'static> TaskStorage<F> { | |||
| 147 | } | 150 | } |
| 148 | } | 151 | } |
| 149 | 152 | ||
| 150 | /// Try to spawn a task in a pool. | ||
| 151 | /// | ||
| 152 | /// See [`Self::spawn()`] for details. | ||
| 153 | /// | ||
| 154 | /// This will loop over the pool and spawn the task in the first storage that | ||
| 155 | /// is currently free. If none is free, | ||
| 156 | pub fn spawn_pool(pool: &'static [Self], future: impl FnOnce() -> F) -> SpawnToken<F> { | ||
| 157 | for task in pool { | ||
| 158 | if task.spawn_allocate() { | ||
| 159 | return unsafe { task.spawn_initialize(future) }; | ||
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 163 | SpawnToken::new_failed() | ||
| 164 | } | ||
| 165 | |||
| 166 | /// Try to spawn the task. | 153 | /// Try to spawn the task. |
| 167 | /// | 154 | /// |
| 168 | /// The `future` closure constructs the future. It's only called if spawning is | 155 | /// The `future` closure constructs the future. It's only called if spawning is |
| @@ -222,6 +209,41 @@ impl<F: Future + 'static> TaskStorage<F> { | |||
| 222 | 209 | ||
| 223 | unsafe impl<F: Future + 'static> Sync for TaskStorage<F> {} | 210 | unsafe impl<F: Future + 'static> Sync for TaskStorage<F> {} |
| 224 | 211 | ||
| 212 | /// Raw storage that can hold up to N tasks of the same type. | ||
| 213 | /// | ||
| 214 | /// This is essentially a `[TaskStorage<F>; N]`. | ||
| 215 | #[cfg(feature = "nightly")] | ||
| 216 | pub struct TaskPool<F: Future + 'static, const N: usize> { | ||
| 217 | pool: [TaskStorage<F>; N], | ||
| 218 | } | ||
| 219 | |||
| 220 | #[cfg(feature = "nightly")] | ||
| 221 | impl<F: Future + 'static, const N: usize> TaskPool<F, N> { | ||
| 222 | /// Create a new TaskPool, with all tasks in non-spawned state. | ||
| 223 | pub const fn new() -> Self { | ||
| 224 | Self { | ||
| 225 | pool: [TaskStorage::NEW; N], | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | /// Try to spawn a task in the pool. | ||
| 230 | /// | ||
| 231 | /// See [`TaskStorage::spawn()`] for details. | ||
| 232 | /// | ||
| 233 | /// This will loop over the pool and spawn the task in the first storage that | ||
| 234 | /// is currently free. If none is free, a "poisoned" SpawnToken is returned, | ||
| 235 | /// which will cause [`Executor::spawn()`] to return the error. | ||
| 236 | pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<F> { | ||
| 237 | for task in &self.pool { | ||
| 238 | if task.spawn_allocate() { | ||
| 239 | return unsafe { task.spawn_initialize(future) }; | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | SpawnToken::new_failed() | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 225 | /// Raw executor. | 247 | /// Raw executor. |
| 226 | /// | 248 | /// |
| 227 | /// This is the core of the Embassy executor. It is low-level, requiring manual | 249 | /// This is the core of the Embassy executor. It is low-level, requiring manual |
