aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-executor/src/raw/mod.rs21
-rw-r--r--embassy-executor/src/raw/timer_queue.rs5
2 files changed, 18 insertions, 8 deletions
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs
index b825fa6c2..7da14468d 100644
--- a/embassy-executor/src/raw/mod.rs
+++ b/embassy-executor/src/raw/mod.rs
@@ -94,13 +94,14 @@ impl TaskRef {
94 &self.header().timer_queue_item 94 &self.header().timer_queue_item
95 } 95 }
96 96
97 /// Mark the task as timer-queued. Return whether it was newly queued (i.e. not queued before) 97 /// Mark the task as timer-queued. Return whether it should be actually enqueued
98 /// using `_embassy_time_schedule_wake`.
98 /// 99 ///
99 /// Entering this state prevents the task from being respawned while in a timer queue. 100 /// Entering this state prevents the task from being respawned while in a timer queue.
100 /// 101 ///
101 /// Safety: 102 /// Safety:
102 /// 103 ///
103 /// This functions should only be called by the timer queue implementation, before 104 /// This functions should only be called by the timer queue driver, before
104 /// enqueueing the timer item. 105 /// enqueueing the timer item.
105 pub unsafe fn timer_enqueue(&self) -> timer_queue::TimerEnqueueOperation { 106 pub unsafe fn timer_enqueue(&self) -> timer_queue::TimerEnqueueOperation {
106 self.header().state.timer_enqueue() 107 self.header().state.timer_enqueue()
@@ -193,16 +194,24 @@ impl<F: Future + 'static> TaskStorage<F> {
193 Poll::Ready(_) => { 194 Poll::Ready(_) => {
194 this.future.drop_in_place(); 195 this.future.drop_in_place();
195 196
196 // Mark this task to be timer queued, to prevent re-queueing it. 197 // Mark this task to be timer queued.
197 this.raw.state.timer_enqueue(); 198 // We're splitting the enqueue in two parts, so that we can change task state
199 // to something that prevent re-queueing.
200 let op = this.raw.state.timer_enqueue();
198 201
199 // Now mark the task as not spawned, so that 202 // Now mark the task as not spawned, so that
200 // - it can be spawned again once it has been removed from the timer queue 203 // - it can be spawned again once it has been removed from the timer queue
201 // - it can not be timer-queued again 204 // - it can not be timer-queued again
205 // We must do this before scheduling the wake, to prevent the task from being
206 // dequeued by the time driver while it's still SPAWNED.
202 this.raw.state.despawn(); 207 this.raw.state.despawn();
203 208
204 // Schedule the task by hand in the past, so it runs immediately. 209 // Now let's finish enqueueing. While we shouldn't get an `Ignore` here, it's
205 unsafe { _embassy_time_schedule_wake(0, &waker) } 210 // better to be safe.
211 if op == timer_queue::TimerEnqueueOperation::Enqueue {
212 // Schedule the task in the past, so it gets dequeued ASAP.
213 unsafe { _embassy_time_schedule_wake(0, &waker) }
214 }
206 } 215 }
207 Poll::Pending => {} 216 Poll::Pending => {}
208 } 217 }
diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs
index c36708401..cd9a73822 100644
--- a/embassy-executor/src/raw/timer_queue.rs
+++ b/embassy-executor/src/raw/timer_queue.rs
@@ -30,9 +30,10 @@ impl TimerQueueItem {
30/// The operation to perform after `timer_enqueue` is called. 30/// The operation to perform after `timer_enqueue` is called.
31#[derive(Debug, Copy, Clone, PartialEq)] 31#[derive(Debug, Copy, Clone, PartialEq)]
32#[cfg_attr(feature = "defmt", derive(defmt::Format))] 32#[cfg_attr(feature = "defmt", derive(defmt::Format))]
33#[must_use]
33pub enum TimerEnqueueOperation { 34pub enum TimerEnqueueOperation {
34 /// Enqueue the task. 35 /// Enqueue the task (or update its expiration time).
35 Enqueue, 36 Enqueue,
36 /// Update the task's expiration time. 37 /// The task must not be enqueued in the timer queue.
37 Ignore, 38 Ignore,
38} 39}