diff options
Diffstat (limited to 'embassy-executor/src/spawner.rs')
| -rw-r--r-- | embassy-executor/src/spawner.rs | 120 |
1 files changed, 109 insertions, 11 deletions
diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index 271606244..522d97db3 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs | |||
| @@ -1,9 +1,12 @@ | |||
| 1 | use core::future::poll_fn; | 1 | use core::future::{poll_fn, Future}; |
| 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 | #[cfg(feature = "trace")] | ||
| 9 | use crate::raw::trace::TaskRefTrace; | ||
| 7 | 10 | ||
| 8 | /// Token to spawn a newly-created task in an executor. | 11 | /// Token to spawn a newly-created task in an executor. |
| 9 | /// | 12 | /// |
| @@ -21,7 +24,7 @@ use super::raw; | |||
| 21 | /// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it. | 24 | /// 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()"] | 25 | #[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> { | 26 | pub struct SpawnToken<S> { |
| 24 | raw_task: Option<raw::TaskRef>, | 27 | pub(crate) raw_task: Option<raw::TaskRef>, |
| 25 | phantom: PhantomData<*mut S>, | 28 | phantom: PhantomData<*mut S>, |
| 26 | } | 29 | } |
| 27 | 30 | ||
| @@ -33,6 +36,15 @@ impl<S> SpawnToken<S> { | |||
| 33 | } | 36 | } |
| 34 | } | 37 | } |
| 35 | 38 | ||
| 39 | /// Returns the task id if available, otherwise 0 | ||
| 40 | /// This can be used in combination with rtos-trace to match task names with id's | ||
| 41 | pub fn id(&self) -> u32 { | ||
| 42 | match self.raw_task { | ||
| 43 | None => 0, | ||
| 44 | Some(t) => t.as_ptr() as u32, | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 36 | /// Return a SpawnToken that represents a failed spawn. | 48 | /// Return a SpawnToken that represents a failed spawn. |
| 37 | pub fn new_failed() -> Self { | 49 | pub fn new_failed() -> Self { |
| 38 | Self { | 50 | Self { |
| @@ -50,8 +62,7 @@ impl<S> Drop for SpawnToken<S> { | |||
| 50 | } | 62 | } |
| 51 | 63 | ||
| 52 | /// Error returned when spawning a task. | 64 | /// Error returned when spawning a task. |
| 53 | #[derive(Copy, Clone, Debug)] | 65 | #[derive(Copy, Clone)] |
| 54 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 55 | pub enum SpawnError { | 66 | pub enum SpawnError { |
| 56 | /// Too many instances of this task are already running. | 67 | /// Too many instances of this task are already running. |
| 57 | /// | 68 | /// |
| @@ -61,6 +72,31 @@ pub enum SpawnError { | |||
| 61 | Busy, | 72 | Busy, |
| 62 | } | 73 | } |
| 63 | 74 | ||
| 75 | impl core::fmt::Debug for SpawnError { | ||
| 76 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 77 | core::fmt::Display::fmt(self, f) | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | impl core::fmt::Display for SpawnError { | ||
| 82 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 83 | match self { | ||
| 84 | SpawnError::Busy => write!(f, "Busy - Too many instances of this task are already running. Check the `pool_size` attribute of the task."), | ||
| 85 | } | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | #[cfg(feature = "defmt")] | ||
| 90 | impl defmt::Format for SpawnError { | ||
| 91 | fn format(&self, f: defmt::Formatter) { | ||
| 92 | match self { | ||
| 93 | SpawnError::Busy => defmt::write!(f, "Busy - Too many instances of this task are already running. Check the `pool_size` attribute of the task."), | ||
| 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 | ||
| @@ -89,14 +125,19 @@ impl Spawner { | |||
| 89 | /// # Panics | 125 | /// # Panics |
| 90 | /// | 126 | /// |
| 91 | /// Panics if the current executor is not an Embassy executor. | 127 | /// Panics if the current executor is not an Embassy executor. |
| 92 | pub async fn for_current_executor() -> Self { | 128 | pub fn for_current_executor() -> impl Future<Output = Self> { |
| 93 | poll_fn(|cx| { | 129 | poll_fn(|cx| { |
| 94 | let task = raw::task_from_waker(cx.waker()); | 130 | let task = raw::task_from_waker(cx.waker()); |
| 95 | let executor = unsafe { task.header().executor.get().unwrap_unchecked() }; | 131 | let executor = unsafe { |
| 132 | task.header() | ||
| 133 | .executor | ||
| 134 | .load(Ordering::Relaxed) | ||
| 135 | .as_ref() | ||
| 136 | .unwrap_unchecked() | ||
| 137 | }; | ||
| 96 | let executor = unsafe { raw::Executor::wrap(executor) }; | 138 | let executor = unsafe { raw::Executor::wrap(executor) }; |
| 97 | Poll::Ready(Self::new(executor)) | 139 | Poll::Ready(Self::new(executor)) |
| 98 | }) | 140 | }) |
| 99 | .await | ||
| 100 | } | 141 | } |
| 101 | 142 | ||
| 102 | /// Spawn a task into an executor. | 143 | /// Spawn a task into an executor. |
| @@ -134,6 +175,58 @@ impl Spawner { | |||
| 134 | pub fn make_send(&self) -> SendSpawner { | 175 | pub fn make_send(&self) -> SendSpawner { |
| 135 | SendSpawner::new(&self.executor.inner) | 176 | SendSpawner::new(&self.executor.inner) |
| 136 | } | 177 | } |
| 178 | |||
| 179 | /// Return the unique ID of this Spawner's Executor. | ||
| 180 | pub fn executor_id(&self) -> usize { | ||
| 181 | self.executor.id() | ||
| 182 | } | ||
| 183 | } | ||
| 184 | |||
| 185 | /// Extension trait adding tracing capabilities to the Spawner | ||
| 186 | /// | ||
| 187 | /// This trait provides an additional method to spawn tasks with an associated name, | ||
| 188 | /// which can be useful for debugging and tracing purposes. | ||
| 189 | pub trait SpawnerTraceExt { | ||
| 190 | /// Spawns a new task with a specified name. | ||
| 191 | /// | ||
| 192 | /// # Arguments | ||
| 193 | /// * `name` - Static string name to associate with the task | ||
| 194 | /// * `token` - Token representing the task to spawn | ||
| 195 | /// | ||
| 196 | /// # Returns | ||
| 197 | /// Result indicating whether the spawn was successful | ||
| 198 | fn spawn_named<S>(&self, name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError>; | ||
| 199 | } | ||
| 200 | |||
| 201 | /// Implementation of the SpawnerTraceExt trait for Spawner when trace is enabled | ||
| 202 | #[cfg(feature = "trace")] | ||
| 203 | impl SpawnerTraceExt for Spawner { | ||
| 204 | fn spawn_named<S>(&self, name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError> { | ||
| 205 | let task = token.raw_task; | ||
| 206 | core::mem::forget(token); | ||
| 207 | |||
| 208 | match task { | ||
| 209 | Some(task) => { | ||
| 210 | // Set the name and ID when trace is enabled | ||
| 211 | task.set_name(Some(name)); | ||
| 212 | let task_id = task.as_ptr() as u32; | ||
| 213 | task.set_id(task_id); | ||
| 214 | |||
| 215 | unsafe { self.executor.spawn(task) }; | ||
| 216 | Ok(()) | ||
| 217 | } | ||
| 218 | None => Err(SpawnError::Busy), | ||
| 219 | } | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 223 | /// Implementation of the SpawnerTraceExt trait for Spawner when trace is disabled | ||
| 224 | #[cfg(not(feature = "trace"))] | ||
| 225 | impl SpawnerTraceExt for Spawner { | ||
| 226 | fn spawn_named<S>(&self, _name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError> { | ||
| 227 | // When trace is disabled, just forward to regular spawn and ignore the name | ||
| 228 | self.spawn(token) | ||
| 229 | } | ||
| 137 | } | 230 | } |
| 138 | 231 | ||
| 139 | /// Handle to spawn tasks into an executor from any thread. | 232 | /// Handle to spawn tasks into an executor from any thread. |
| @@ -161,13 +254,18 @@ impl SendSpawner { | |||
| 161 | /// # Panics | 254 | /// # Panics |
| 162 | /// | 255 | /// |
| 163 | /// Panics if the current executor is not an Embassy executor. | 256 | /// Panics if the current executor is not an Embassy executor. |
| 164 | pub async fn for_current_executor() -> Self { | 257 | pub fn for_current_executor() -> impl Future<Output = Self> { |
| 165 | poll_fn(|cx| { | 258 | poll_fn(|cx| { |
| 166 | let task = raw::task_from_waker(cx.waker()); | 259 | let task = raw::task_from_waker(cx.waker()); |
| 167 | let executor = unsafe { task.header().executor.get().unwrap_unchecked() }; | 260 | let executor = unsafe { |
| 261 | task.header() | ||
| 262 | .executor | ||
| 263 | .load(Ordering::Relaxed) | ||
| 264 | .as_ref() | ||
| 265 | .unwrap_unchecked() | ||
| 266 | }; | ||
| 168 | Poll::Ready(Self::new(executor)) | 267 | Poll::Ready(Self::new(executor)) |
| 169 | }) | 268 | }) |
| 170 | .await | ||
| 171 | } | 269 | } |
| 172 | 270 | ||
| 173 | /// Spawn a task into an executor. | 271 | /// Spawn a task into an executor. |
