diff options
| author | Dion Dokter <[email protected]> | 2025-11-20 13:22:38 +0100 |
|---|---|---|
| committer | Dion Dokter <[email protected]> | 2025-11-20 13:22:38 +0100 |
| commit | 4f2c36e447455e8d33607d586859d3d075cabf1d (patch) | |
| tree | 003cd822d688acd7c074dd229663b4648d100f71 /embassy-executor/src/spawner.rs | |
| parent | 663732d85abbae400f2dbab2c411802a5b60e9b1 (diff) | |
| parent | 661874d11de7d93ed52e08e020a9d4c7ee11122d (diff) | |
Merge branch 'main' into u0-lcd
Diffstat (limited to 'embassy-executor/src/spawner.rs')
| -rw-r--r-- | embassy-executor/src/spawner.rs | 147 |
1 files changed, 89 insertions, 58 deletions
diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index 271606244..b73a1e7c6 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs | |||
| @@ -1,9 +1,11 @@ | |||
| 1 | use core::future::poll_fn; | 1 | use core::future::{Future, poll_fn}; |
| 2 | use core::marker::PhantomData; | 2 | use core::marker::PhantomData; |
| 3 | use core::mem; | 3 | use core::mem; |
| 4 | use core::sync::atomic::Ordering; | ||
| 4 | use core::task::Poll; | 5 | use core::task::Poll; |
| 5 | 6 | ||
| 6 | use super::raw; | 7 | use super::raw; |
| 8 | use crate::Metadata; | ||
| 7 | 9 | ||
| 8 | /// Token to spawn a newly-created task in an executor. | 10 | /// Token to spawn a newly-created task in an executor. |
| 9 | /// | 11 | /// |
| @@ -21,24 +23,28 @@ use super::raw; | |||
| 21 | /// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it. | 23 | /// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it. |
| 22 | #[must_use = "Calling a task function does nothing on its own. You must spawn the returned SpawnToken, typically with Spawner::spawn()"] | 24 | #[must_use = "Calling a task function does nothing on its own. You must spawn the returned SpawnToken, typically with Spawner::spawn()"] |
| 23 | pub struct SpawnToken<S> { | 25 | pub struct SpawnToken<S> { |
| 24 | raw_task: Option<raw::TaskRef>, | 26 | pub(crate) raw_task: raw::TaskRef, |
| 25 | phantom: PhantomData<*mut S>, | 27 | phantom: PhantomData<*mut S>, |
| 26 | } | 28 | } |
| 27 | 29 | ||
| 28 | impl<S> SpawnToken<S> { | 30 | impl<S> SpawnToken<S> { |
| 29 | pub(crate) unsafe fn new(raw_task: raw::TaskRef) -> Self { | 31 | pub(crate) unsafe fn new(raw_task: raw::TaskRef) -> Self { |
| 30 | Self { | 32 | Self { |
| 31 | raw_task: Some(raw_task), | 33 | raw_task, |
| 32 | phantom: PhantomData, | 34 | phantom: PhantomData, |
| 33 | } | 35 | } |
| 34 | } | 36 | } |
| 35 | 37 | ||
| 36 | /// Return a SpawnToken that represents a failed spawn. | 38 | /// Returns the task ID. |
| 37 | pub fn new_failed() -> Self { | 39 | /// This can be used in combination with rtos-trace to match task names with IDs |
| 38 | Self { | 40 | pub fn id(&self) -> u32 { |
| 39 | raw_task: None, | 41 | self.raw_task.id() |
| 40 | phantom: PhantomData, | 42 | } |
| 41 | } | 43 | |
| 44 | /// Get the metadata for this task. You can use this to set metadata fields | ||
| 45 | /// prior to spawning it. | ||
| 46 | pub fn metadata(&self) -> &Metadata { | ||
| 47 | self.raw_task.metadata() | ||
| 42 | } | 48 | } |
| 43 | } | 49 | } |
| 44 | 50 | ||
| @@ -50,8 +56,7 @@ impl<S> Drop for SpawnToken<S> { | |||
| 50 | } | 56 | } |
| 51 | 57 | ||
| 52 | /// Error returned when spawning a task. | 58 | /// Error returned when spawning a task. |
| 53 | #[derive(Copy, Clone, Debug)] | 59 | #[derive(Copy, Clone)] |
| 54 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 55 | pub enum SpawnError { | 60 | pub enum SpawnError { |
| 56 | /// Too many instances of this task are already running. | 61 | /// Too many instances of this task are already running. |
| 57 | /// | 62 | /// |
| @@ -61,6 +66,37 @@ pub enum SpawnError { | |||
| 61 | Busy, | 66 | Busy, |
| 62 | } | 67 | } |
| 63 | 68 | ||
| 69 | impl core::fmt::Debug for SpawnError { | ||
| 70 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 71 | core::fmt::Display::fmt(self, f) | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | impl core::fmt::Display for SpawnError { | ||
| 76 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 77 | match self { | ||
| 78 | SpawnError::Busy => write!( | ||
| 79 | f, | ||
| 80 | "Busy - Too many instances of this task are already running. Check the `pool_size` attribute of the task." | ||
| 81 | ), | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | #[cfg(feature = "defmt")] | ||
| 87 | impl defmt::Format for SpawnError { | ||
| 88 | fn format(&self, f: defmt::Formatter) { | ||
| 89 | match self { | ||
| 90 | SpawnError::Busy => defmt::write!( | ||
| 91 | f, | ||
| 92 | "Busy - Too many instances of this task are already running. Check the `pool_size` attribute of the task." | ||
| 93 | ), | ||
| 94 | } | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | impl core::error::Error for SpawnError {} | ||
| 99 | |||
| 64 | /// Handle to spawn tasks into an executor. | 100 | /// Handle to spawn tasks into an executor. |
| 65 | /// | 101 | /// |
| 66 | /// This Spawner can spawn any task (Send and non-Send ones), but it can | 102 | /// This Spawner can spawn any task (Send and non-Send ones), but it can |
| @@ -69,7 +105,7 @@ pub enum SpawnError { | |||
| 69 | /// If you want to spawn tasks from another thread, use [SendSpawner]. | 105 | /// If you want to spawn tasks from another thread, use [SendSpawner]. |
| 70 | #[derive(Copy, Clone)] | 106 | #[derive(Copy, Clone)] |
| 71 | pub struct Spawner { | 107 | pub struct Spawner { |
| 72 | executor: &'static raw::Executor, | 108 | pub(crate) executor: &'static raw::Executor, |
| 73 | not_send: PhantomData<*mut ()>, | 109 | not_send: PhantomData<*mut ()>, |
| 74 | } | 110 | } |
| 75 | 111 | ||
| @@ -86,46 +122,47 @@ impl Spawner { | |||
| 86 | /// This function is `async` just to get access to the current async | 122 | /// This function is `async` just to get access to the current async |
| 87 | /// context. It returns instantly, it does not block/yield. | 123 | /// context. It returns instantly, it does not block/yield. |
| 88 | /// | 124 | /// |
| 125 | /// Using this method is discouraged due to it being unsafe. Consider the following | ||
| 126 | /// alternatives instead: | ||
| 127 | /// | ||
| 128 | /// - Pass the initial `Spawner` as an argument to tasks. Note that it's `Copy`, so you can | ||
| 129 | /// make as many copies of it as you want. | ||
| 130 | /// - Use `SendSpawner::for_current_executor()` instead, which is safe but can only be used | ||
| 131 | /// if task arguments are `Send`. | ||
| 132 | /// | ||
| 133 | /// The only case where using this method is absolutely required is obtaining the `Spawner` | ||
| 134 | /// for an `InterruptExecutor`. | ||
| 135 | /// | ||
| 136 | /// # Safety | ||
| 137 | /// | ||
| 138 | /// You must only execute this with an async `Context` created by the Embassy executor. | ||
| 139 | /// You must not execute it with manually-created `Context`s. | ||
| 140 | /// | ||
| 89 | /// # Panics | 141 | /// # Panics |
| 90 | /// | 142 | /// |
| 91 | /// Panics if the current executor is not an Embassy executor. | 143 | /// Panics if the current executor is not an Embassy executor. |
| 92 | pub async fn for_current_executor() -> Self { | 144 | pub unsafe fn for_current_executor() -> impl Future<Output = Self> { |
| 93 | poll_fn(|cx| { | 145 | poll_fn(|cx| { |
| 94 | let task = raw::task_from_waker(cx.waker()); | 146 | let task = raw::task_from_waker(cx.waker()); |
| 95 | let executor = unsafe { task.header().executor.get().unwrap_unchecked() }; | 147 | let executor = unsafe { |
| 148 | task.header() | ||
| 149 | .executor | ||
| 150 | .load(Ordering::Relaxed) | ||
| 151 | .as_ref() | ||
| 152 | .unwrap_unchecked() | ||
| 153 | }; | ||
| 96 | let executor = unsafe { raw::Executor::wrap(executor) }; | 154 | let executor = unsafe { raw::Executor::wrap(executor) }; |
| 97 | Poll::Ready(Self::new(executor)) | 155 | Poll::Ready(Self::new(executor)) |
| 98 | }) | 156 | }) |
| 99 | .await | ||
| 100 | } | 157 | } |
| 101 | 158 | ||
| 102 | /// Spawn a task into an executor. | 159 | /// Spawn a task into an executor. |
| 103 | /// | 160 | /// |
| 104 | /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy_executor::task]`). | 161 | /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy_executor::task]`). |
| 105 | pub fn spawn<S>(&self, token: SpawnToken<S>) -> Result<(), SpawnError> { | 162 | pub fn spawn<S>(&self, token: SpawnToken<S>) { |
| 106 | let task = token.raw_task; | 163 | let task = token.raw_task; |
| 107 | mem::forget(token); | 164 | mem::forget(token); |
| 108 | 165 | unsafe { self.executor.spawn(task) } | |
| 109 | match task { | ||
| 110 | Some(task) => { | ||
| 111 | unsafe { self.executor.spawn(task) }; | ||
| 112 | Ok(()) | ||
| 113 | } | ||
| 114 | None => Err(SpawnError::Busy), | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | // Used by the `embassy_executor_macros::main!` macro to throw an error when spawn | ||
| 119 | // fails. This is here to allow conditional use of `defmt::unwrap!` | ||
| 120 | // without introducing a `defmt` feature in the `embassy_executor_macros` package, | ||
| 121 | // which would require use of `-Z namespaced-features`. | ||
| 122 | /// Spawn a task into an executor, panicking on failure. | ||
| 123 | /// | ||
| 124 | /// # Panics | ||
| 125 | /// | ||
| 126 | /// Panics if the spawning fails. | ||
| 127 | pub fn must_spawn<S>(&self, token: SpawnToken<S>) { | ||
| 128 | unwrap!(self.spawn(token)); | ||
| 129 | } | 166 | } |
| 130 | 167 | ||
| 131 | /// Convert this Spawner to a SendSpawner. This allows you to send the | 168 | /// Convert this Spawner to a SendSpawner. This allows you to send the |
| @@ -134,6 +171,11 @@ impl Spawner { | |||
| 134 | pub fn make_send(&self) -> SendSpawner { | 171 | pub fn make_send(&self) -> SendSpawner { |
| 135 | SendSpawner::new(&self.executor.inner) | 172 | SendSpawner::new(&self.executor.inner) |
| 136 | } | 173 | } |
| 174 | |||
| 175 | /// Return the unique ID of this Spawner's Executor. | ||
| 176 | pub fn executor_id(&self) -> usize { | ||
| 177 | self.executor.id() | ||
| 178 | } | ||
| 137 | } | 179 | } |
| 138 | 180 | ||
| 139 | /// Handle to spawn tasks into an executor from any thread. | 181 | /// Handle to spawn tasks into an executor from any thread. |
| @@ -161,37 +203,26 @@ impl SendSpawner { | |||
| 161 | /// # Panics | 203 | /// # Panics |
| 162 | /// | 204 | /// |
| 163 | /// Panics if the current executor is not an Embassy executor. | 205 | /// Panics if the current executor is not an Embassy executor. |
| 164 | pub async fn for_current_executor() -> Self { | 206 | pub fn for_current_executor() -> impl Future<Output = Self> { |
| 165 | poll_fn(|cx| { | 207 | poll_fn(|cx| { |
| 166 | let task = raw::task_from_waker(cx.waker()); | 208 | let task = raw::task_from_waker(cx.waker()); |
| 167 | let executor = unsafe { task.header().executor.get().unwrap_unchecked() }; | 209 | let executor = unsafe { |
| 210 | task.header() | ||
| 211 | .executor | ||
| 212 | .load(Ordering::Relaxed) | ||
| 213 | .as_ref() | ||
| 214 | .unwrap_unchecked() | ||
| 215 | }; | ||
| 168 | Poll::Ready(Self::new(executor)) | 216 | Poll::Ready(Self::new(executor)) |
| 169 | }) | 217 | }) |
| 170 | .await | ||
| 171 | } | 218 | } |
| 172 | 219 | ||
| 173 | /// Spawn a task into an executor. | 220 | /// Spawn a task into an executor. |
| 174 | /// | 221 | /// |
| 175 | /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy_executor::task]`). | 222 | /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy_executor::task]`). |
| 176 | pub fn spawn<S: Send>(&self, token: SpawnToken<S>) -> Result<(), SpawnError> { | 223 | pub fn spawn<S: Send>(&self, token: SpawnToken<S>) { |
| 177 | let header = token.raw_task; | 224 | let header = token.raw_task; |
| 178 | mem::forget(token); | 225 | mem::forget(token); |
| 179 | 226 | unsafe { self.executor.spawn(header) } | |
| 180 | match header { | ||
| 181 | Some(header) => { | ||
| 182 | unsafe { self.executor.spawn(header) }; | ||
| 183 | Ok(()) | ||
| 184 | } | ||
| 185 | None => Err(SpawnError::Busy), | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | /// Spawn a task into an executor, panicking on failure. | ||
| 190 | /// | ||
| 191 | /// # Panics | ||
| 192 | /// | ||
| 193 | /// Panics if the spawning fails. | ||
| 194 | pub fn must_spawn<S: Send>(&self, token: SpawnToken<S>) { | ||
| 195 | unwrap!(self.spawn(token)); | ||
| 196 | } | 227 | } |
| 197 | } | 228 | } |
