aboutsummaryrefslogtreecommitdiff
path: root/embassy-executor
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-executor')
-rw-r--r--embassy-executor/Cargo.toml1
-rw-r--r--embassy-executor/src/raw/mod.rs3
-rw-r--r--embassy-executor/src/raw/state_atomics.rs15
-rw-r--r--embassy-executor/src/raw/state_atomics_arm.rs2
-rw-r--r--embassy-executor/src/raw/state_critical_section.rs9
-rw-r--r--embassy-executor/tests/test.rs133
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]
56critical-section = { version = "1.1", features = ["std"] } 56critical-section = { version = "1.1", features = ["std"] }
57trybuild = "1.0" 57trybuild = "1.0"
58embassy-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`
79pub(crate) struct TaskHeader { 80pub(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]
141fn 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]
187fn 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]
141fn executor_task_cfg_args() { 274fn executor_task_cfg_args() {
142 // simulate cfg'ing away argument c 275 // simulate cfg'ing away argument c
143 #[task] 276 #[task]