diff options
Diffstat (limited to 'embassy-executor')
| -rw-r--r-- | embassy-executor/Cargo.toml | 1 | ||||
| -rw-r--r-- | embassy-executor/src/raw/mod.rs | 3 | ||||
| -rw-r--r-- | embassy-executor/src/raw/state_atomics.rs | 15 | ||||
| -rw-r--r-- | embassy-executor/src/raw/state_atomics_arm.rs | 2 | ||||
| -rw-r--r-- | embassy-executor/src/raw/state_critical_section.rs | 9 | ||||
| -rw-r--r-- | embassy-executor/tests/test.rs | 133 |
6 files changed, 142 insertions, 21 deletions
diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 2a64b9c83..5effaca65 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml | |||
| @@ -55,6 +55,7 @@ avr-device = { version = "0.5.3", optional = true } | |||
| 55 | [dev-dependencies] | 55 | [dev-dependencies] |
| 56 | critical-section = { version = "1.1", features = ["std"] } | 56 | critical-section = { version = "1.1", features = ["std"] } |
| 57 | trybuild = "1.0" | 57 | trybuild = "1.0" |
| 58 | embassy-sync = { path = "../embassy-sync" } | ||
| 58 | 59 | ||
| 59 | [features] | 60 | [features] |
| 60 | 61 | ||
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 0ac569946..6503b556f 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs | |||
| @@ -52,7 +52,7 @@ use super::SpawnToken; | |||
| 52 | /// ```text | 52 | /// ```text |
| 53 | /// ┌────────────┐ ┌────────────────────────┐ | 53 | /// ┌────────────┐ ┌────────────────────────┐ |
| 54 | /// ┌─►│Not spawned │◄─6┤Not spawned|Run enqueued│ | 54 | /// ┌─►│Not spawned │◄─6┤Not spawned|Run enqueued│ |
| 55 | /// │ │ �� │ │ | 55 | /// │ │ ��7��►�� │ |
| 56 | /// │ └─────┬──────┘ └──────▲─────────────────┘ | 56 | /// │ └─────┬──────┘ └──────▲─────────────────┘ |
| 57 | /// │ 1 │ | 57 | /// │ 1 │ |
| 58 | /// │ │ ┌────────────┘ | 58 | /// │ │ ┌────────────┘ |
| @@ -76,6 +76,7 @@ use super::SpawnToken; | |||
| 76 | /// - 4: Task exits - `TaskStorage::poll -> Poll::Ready` | 76 | /// - 4: Task exits - `TaskStorage::poll -> Poll::Ready` |
| 77 | /// - 5: A run-queued task exits - `TaskStorage::poll -> Poll::Ready` | 77 | /// - 5: A run-queued task exits - `TaskStorage::poll -> Poll::Ready` |
| 78 | /// - 6: Task is dequeued and then ignored via `State::run_dequeue` | 78 | /// - 6: Task is dequeued and then ignored via `State::run_dequeue` |
| 79 | /// - 7: A task is waken when it is not spawned - `wake_task -> State::run_enqueue` | ||
| 79 | pub(crate) struct TaskHeader { | 80 | pub(crate) struct TaskHeader { |
| 80 | pub(crate) state: State, | 81 | pub(crate) state: State, |
| 81 | pub(crate) run_queue_item: RunQueueItem, | 82 | pub(crate) run_queue_item: RunQueueItem, |
diff --git a/embassy-executor/src/raw/state_atomics.rs b/embassy-executor/src/raw/state_atomics.rs index 6f5266bda..bdd317b53 100644 --- a/embassy-executor/src/raw/state_atomics.rs +++ b/embassy-executor/src/raw/state_atomics.rs | |||
| @@ -44,19 +44,8 @@ impl State { | |||
| 44 | /// function if the task was successfully marked. | 44 | /// function if the task was successfully marked. |
| 45 | #[inline(always)] | 45 | #[inline(always)] |
| 46 | pub fn run_enqueue(&self, f: impl FnOnce(Token)) { | 46 | pub fn run_enqueue(&self, f: impl FnOnce(Token)) { |
| 47 | if self | 47 | let prev = self.state.fetch_or(STATE_RUN_QUEUED, Ordering::AcqRel); |
| 48 | .state | 48 | if prev & STATE_RUN_QUEUED == 0 { |
| 49 | .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| { | ||
| 50 | // If already scheduled, or if not started, | ||
| 51 | if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) { | ||
| 52 | None | ||
| 53 | } else { | ||
| 54 | // Mark it as scheduled | ||
| 55 | Some(state | STATE_RUN_QUEUED) | ||
| 56 | } | ||
| 57 | }) | ||
| 58 | .is_ok() | ||
| 59 | { | ||
| 60 | locked(f); | 49 | locked(f); |
| 61 | } | 50 | } |
| 62 | } | 51 | } |
diff --git a/embassy-executor/src/raw/state_atomics_arm.rs b/embassy-executor/src/raw/state_atomics_arm.rs index 4896b33c5..06bf24343 100644 --- a/embassy-executor/src/raw/state_atomics_arm.rs +++ b/embassy-executor/src/raw/state_atomics_arm.rs | |||
| @@ -72,7 +72,7 @@ impl State { | |||
| 72 | let state: u32; | 72 | let state: u32; |
| 73 | asm!("ldrex {}, [{}]", out(reg) state, in(reg) self, options(nostack)); | 73 | asm!("ldrex {}, [{}]", out(reg) state, in(reg) self, options(nostack)); |
| 74 | 74 | ||
| 75 | if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) { | 75 | if state & STATE_RUN_QUEUED != 0 { |
| 76 | asm!("clrex", options(nomem, nostack)); | 76 | asm!("clrex", options(nomem, nostack)); |
| 77 | return; | 77 | return; |
| 78 | } | 78 | } |
diff --git a/embassy-executor/src/raw/state_critical_section.rs b/embassy-executor/src/raw/state_critical_section.rs index 29b10f6e3..4733af278 100644 --- a/embassy-executor/src/raw/state_critical_section.rs +++ b/embassy-executor/src/raw/state_critical_section.rs | |||
| @@ -56,12 +56,9 @@ impl State { | |||
| 56 | pub fn run_enqueue(&self, f: impl FnOnce(Token)) { | 56 | pub fn run_enqueue(&self, f: impl FnOnce(Token)) { |
| 57 | critical_section::with(|cs| { | 57 | critical_section::with(|cs| { |
| 58 | if self.update_with_cs(cs, |s| { | 58 | if self.update_with_cs(cs, |s| { |
| 59 | if (*s & STATE_RUN_QUEUED != 0) || (*s & STATE_SPAWNED == 0) { | 59 | let ok = *s & STATE_RUN_QUEUED == 0; |
| 60 | false | 60 | *s |= STATE_RUN_QUEUED; |
| 61 | } else { | 61 | ok |
| 62 | *s |= STATE_RUN_QUEUED; | ||
| 63 | true | ||
| 64 | } | ||
| 65 | }) { | 62 | }) { |
| 66 | f(cs); | 63 | f(cs); |
| 67 | } | 64 | } |
diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs index 0ce1f1891..78c49c071 100644 --- a/embassy-executor/tests/test.rs +++ b/embassy-executor/tests/test.rs | |||
| @@ -138,6 +138,139 @@ fn executor_task_self_wake_twice() { | |||
| 138 | } | 138 | } |
| 139 | 139 | ||
| 140 | #[test] | 140 | #[test] |
| 141 | fn waking_after_completion_does_not_poll() { | ||
| 142 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 143 | |||
| 144 | #[task] | ||
| 145 | async fn task1(trace: Trace, waker: &'static AtomicWaker) { | ||
| 146 | poll_fn(|cx| { | ||
| 147 | trace.push("poll task1"); | ||
| 148 | waker.register(cx.waker()); | ||
| 149 | Poll::Ready(()) | ||
| 150 | }) | ||
| 151 | .await | ||
| 152 | } | ||
| 153 | |||
| 154 | let waker = Box::leak(Box::new(AtomicWaker::new())); | ||
| 155 | |||
| 156 | let (executor, trace) = setup(); | ||
| 157 | executor.spawner().spawn(task1(trace.clone(), waker)).unwrap(); | ||
| 158 | |||
| 159 | unsafe { executor.poll() }; | ||
| 160 | waker.wake(); | ||
| 161 | unsafe { executor.poll() }; | ||
| 162 | |||
| 163 | // Exited task may be waken but is not polled | ||
| 164 | waker.wake(); | ||
| 165 | waker.wake(); | ||
| 166 | unsafe { executor.poll() }; // Clears running status | ||
| 167 | |||
| 168 | // Can respawn waken-but-dead task | ||
| 169 | executor.spawner().spawn(task1(trace.clone(), waker)).unwrap(); | ||
| 170 | |||
| 171 | unsafe { executor.poll() }; | ||
| 172 | |||
| 173 | assert_eq!( | ||
| 174 | trace.get(), | ||
| 175 | &[ | ||
| 176 | "pend", // spawning a task pends the executor | ||
| 177 | "poll task1", // | ||
| 178 | "pend", // manual wake, gets cleared by poll | ||
| 179 | "pend", // manual wake, single pend for two wakes | ||
| 180 | "pend", // respawning a task pends the executor | ||
| 181 | "poll task1", // | ||
| 182 | ] | ||
| 183 | ) | ||
| 184 | } | ||
| 185 | |||
| 186 | #[test] | ||
| 187 | fn waking_with_old_waker_after_respawn() { | ||
| 188 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 189 | |||
| 190 | async fn yield_now(trace: Trace) { | ||
| 191 | let mut yielded = false; | ||
| 192 | poll_fn(|cx| { | ||
| 193 | if yielded { | ||
| 194 | Poll::Ready(()) | ||
| 195 | } else { | ||
| 196 | trace.push("yield_now"); | ||
| 197 | yielded = true; | ||
| 198 | cx.waker().wake_by_ref(); | ||
| 199 | Poll::Pending | ||
| 200 | } | ||
| 201 | }) | ||
| 202 | .await | ||
| 203 | } | ||
| 204 | |||
| 205 | #[task] | ||
| 206 | async fn task1(trace: Trace, waker: &'static AtomicWaker) { | ||
| 207 | yield_now(trace.clone()).await; | ||
| 208 | poll_fn(|cx| { | ||
| 209 | trace.push("poll task1"); | ||
| 210 | waker.register(cx.waker()); | ||
| 211 | Poll::Ready(()) | ||
| 212 | }) | ||
| 213 | .await; | ||
| 214 | } | ||
| 215 | |||
| 216 | let waker = Box::leak(Box::new(AtomicWaker::new())); | ||
| 217 | |||
| 218 | let (executor, trace) = setup(); | ||
| 219 | executor.spawner().spawn(task1(trace.clone(), waker)).unwrap(); | ||
| 220 | |||
| 221 | unsafe { executor.poll() }; | ||
| 222 | unsafe { executor.poll() }; // progress to registering the waker | ||
| 223 | waker.wake(); | ||
| 224 | unsafe { executor.poll() }; | ||
| 225 | // Task has exited | ||
| 226 | |||
| 227 | assert_eq!( | ||
| 228 | trace.get(), | ||
| 229 | &[ | ||
| 230 | "pend", // spawning a task pends the executor | ||
| 231 | "yield_now", // | ||
| 232 | "pend", // yield_now wakes the task | ||
| 233 | "poll task1", // | ||
| 234 | "pend", // task self-wakes | ||
| 235 | ] | ||
| 236 | ); | ||
| 237 | |||
| 238 | // Can respawn task on another executor | ||
| 239 | let (other_executor, other_trace) = setup(); | ||
| 240 | other_executor | ||
| 241 | .spawner() | ||
| 242 | .spawn(task1(other_trace.clone(), waker)) | ||
| 243 | .unwrap(); | ||
| 244 | |||
| 245 | unsafe { other_executor.poll() }; // just run to the yield_now | ||
| 246 | waker.wake(); // trigger old waker registration | ||
| 247 | unsafe { executor.poll() }; | ||
| 248 | unsafe { other_executor.poll() }; | ||
| 249 | |||
| 250 | // First executor's trace has not changed | ||
| 251 | assert_eq!( | ||
| 252 | trace.get(), | ||
| 253 | &[ | ||
| 254 | "pend", // spawning a task pends the executor | ||
| 255 | "yield_now", // | ||
| 256 | "pend", // yield_now wakes the task | ||
| 257 | "poll task1", // | ||
| 258 | "pend", // task self-wakes | ||
| 259 | ] | ||
| 260 | ); | ||
| 261 | |||
| 262 | assert_eq!( | ||
| 263 | other_trace.get(), | ||
| 264 | &[ | ||
| 265 | "pend", // spawning a task pends the executor | ||
| 266 | "yield_now", // | ||
| 267 | "pend", // manual wake, gets cleared by poll | ||
| 268 | "poll task1", // | ||
| 269 | ] | ||
| 270 | ); | ||
| 271 | } | ||
| 272 | |||
| 273 | #[test] | ||
| 141 | fn executor_task_cfg_args() { | 274 | fn executor_task_cfg_args() { |
| 142 | // simulate cfg'ing away argument c | 275 | // simulate cfg'ing away argument c |
| 143 | #[task] | 276 | #[task] |
