Skip to content

Commit e205f27

Browse files
authored
Rtos: avoid scanning timer/run qeues (#4301)
* Avoid scanning queues * Fix warning
1 parent 1b77d94 commit e205f27

File tree

5 files changed

+76
-31
lines changed

5 files changed

+76
-31
lines changed

esp-rtos/src/run_queue.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,6 @@ impl RunQueue {
172172
containing_queue.as_mut().remove(ready_task);
173173
}
174174
}
175-
self.ready_tasks[priority_n].remove(ready_task);
176175
self.ready_tasks[priority_n].push(ready_task);
177176

178177
cfg_if::cfg_if! {

esp-rtos/src/scheduler.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ impl CpuSchedulerState {
8080
pinned_to: None,
8181

8282
wakeup_at: 0,
83+
run_queued: false,
84+
timer_queued: false,
8385

8486
alloc_list_item: TaskListItem::None,
8587
ready_queue_item: TaskListItem::None,

esp-rtos/src/task/mod.rs

Lines changed: 66 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,24 @@ pub(crate) type TaskListItem = Option<TaskPtr>;
4242

4343
/// An abstraction that allows the task to contain multiple different queue pointers.
4444
pub(crate) trait TaskListElement: Default {
45+
/// Returns the pointer to the next element in the list.
4546
fn next(task: TaskPtr) -> Option<TaskPtr>;
47+
48+
/// Sets the pointer to the next element in the list.
4649
fn set_next(task: TaskPtr, next: Option<TaskPtr>);
50+
51+
/// Returns whether the task is in the list. If this function returns `None`, we don't know.
52+
fn is_in_queue(_task: TaskPtr) -> Option<bool> {
53+
// By default we don't store this information, so we return "Don't know".
54+
None
55+
}
56+
57+
/// Marks whether the task is in the list.
58+
fn mark_in_queue(_task: TaskPtr, _in_queue: bool) {}
4759
}
4860

4961
macro_rules! task_list_item {
50-
($struct:ident, $field:ident) => {
62+
($struct:ident, $field:ident $(, $in_queue_field:ident)?) => {
5163
#[derive(Default)]
5264
pub(crate) struct $struct;
5365
impl TaskListElement for $struct {
@@ -60,14 +72,27 @@ macro_rules! task_list_item {
6072
task.as_mut().$field = next;
6173
}
6274
}
75+
76+
$(
77+
fn is_in_queue(task: TaskPtr) -> Option<bool> {
78+
Some(unsafe { task.as_ref().$in_queue_field })
79+
}
80+
81+
fn mark_in_queue(mut task: TaskPtr, in_queue: bool) {
82+
unsafe {
83+
task.as_mut().$in_queue_field = in_queue;
84+
}
85+
}
86+
)?
6387
}
6488
};
6589
}
6690

91+
task_list_item!(TaskReadyQueueElement, ready_queue_item, run_queued);
92+
task_list_item!(TaskTimerQueueElement, timer_queue_item, timer_queued);
93+
// These aren't perf critical, no need to waste memory on caching list status:
6794
task_list_item!(TaskAllocListElement, alloc_list_item);
68-
task_list_item!(TaskReadyQueueElement, ready_queue_item);
6995
task_list_item!(TaskDeleteListElement, delete_list_item);
70-
task_list_item!(TaskTimerQueueElement, timer_queue_item);
7196

7297
/// Extension trait for common task operations. These should be inherent methods but we can't
7398
/// implement stuff for NonNull.
@@ -153,6 +178,11 @@ impl<E: TaskListElement> TaskList<E> {
153178
}
154179

155180
pub fn push(&mut self, task: TaskPtr) {
181+
if E::is_in_queue(task) == Some(true) {
182+
return;
183+
}
184+
E::mark_in_queue(task, true);
185+
156186
debug_assert!(E::next(task).is_none());
157187
E::set_next(task, self.head);
158188
self.head = Some(task);
@@ -164,12 +194,18 @@ impl<E: TaskListElement> TaskList<E> {
164194
if let Some(task) = popped {
165195
self.head = E::next(task);
166196
E::set_next(task, None);
197+
E::mark_in_queue(task, false);
167198
}
168199

169200
popped
170201
}
171202

172203
pub fn remove(&mut self, task: TaskPtr) {
204+
if E::is_in_queue(task) == Some(false) {
205+
return;
206+
}
207+
E::mark_in_queue(task, false);
208+
173209
// TODO: maybe this (and TaskQueue::remove) may prove too expensive.
174210
let mut list = core::mem::take(self);
175211
while let Some(popped) = list.pop() {
@@ -213,6 +249,11 @@ impl<E: TaskListElement> TaskQueue<E> {
213249
}
214250

215251
pub fn push(&mut self, task: TaskPtr) {
252+
if E::is_in_queue(task) == Some(true) {
253+
return;
254+
}
255+
E::mark_in_queue(task, true);
256+
216257
debug_assert!(E::next(task).is_none());
217258
if let Some(tail) = self.tail {
218259
E::set_next(tail, Some(task));
@@ -231,41 +272,39 @@ impl<E: TaskListElement> TaskQueue<E> {
231272
if self.head.is_none() {
232273
self.tail = None;
233274
}
275+
E::mark_in_queue(task, false);
234276
}
235277

236278
popped
237279
}
238280

239281
#[cfg(multi_core)]
240282
pub fn pop_if(&mut self, cond: impl Fn(&Task) -> bool) -> Option<TaskPtr> {
241-
let mut head = self.head.take();
242-
self.tail = None;
243-
244283
let mut popped = None;
245-
while let Some(task) = head {
246-
head = E::next(task);
247-
E::set_next(task, None);
248-
if cond(unsafe { task.as_ref() }) {
284+
285+
let mut list = core::mem::take(self);
286+
while let Some(task) = list.pop() {
287+
if popped.is_none() && cond(unsafe { task.as_ref() }) {
288+
E::mark_in_queue(task, false);
249289
popped = Some(task);
250-
break;
251290
} else {
252291
self.push(task);
253292
}
254293
}
255294

256-
while let Some(task) = head {
257-
head = E::next(task);
258-
E::set_next(task, None);
259-
self.push(task);
260-
}
261-
262295
popped
263296
}
264297

265298
pub fn remove(&mut self, task: TaskPtr) {
299+
if E::is_in_queue(task) == Some(false) {
300+
return;
301+
}
302+
266303
let mut list = core::mem::take(self);
267304
while let Some(popped) = list.pop() {
268-
if popped != task {
305+
if popped == task {
306+
E::mark_in_queue(task, false);
307+
} else {
269308
self.push(popped);
270309
}
271310
}
@@ -372,6 +411,11 @@ pub(crate) struct Task {
372411

373412
pub wakeup_at: u64,
374413

414+
/// Whether the task is currently queued in the run queue.
415+
pub run_queued: bool,
416+
/// Whether the task is currently queued in the timer queue.
417+
pub timer_queued: bool,
418+
375419
/// The current wait queue this task is in.
376420
pub(crate) current_queue: Option<NonNull<WaitQueue>>,
377421

@@ -463,6 +507,8 @@ impl Task {
463507
pinned_to,
464508

465509
wakeup_at: 0,
510+
timer_queued: false,
511+
run_queued: false,
466512

467513
alloc_list_item: TaskListItem::None,
468514
ready_queue_item: TaskListItem::None,
@@ -555,6 +601,8 @@ pub(super) fn allocate_main_task(
555601
scheduler.per_cpu[current_cpu].main_task.priority = Priority::ZERO;
556602
scheduler.per_cpu[current_cpu].main_task.state = TaskState::Ready;
557603
scheduler.per_cpu[current_cpu].main_task.stack = stack;
604+
scheduler.per_cpu[current_cpu].main_task.run_queued = false;
605+
scheduler.per_cpu[current_cpu].main_task.timer_queued = false;
558606
#[cfg(multi_core)]
559607
{
560608
scheduler.per_cpu[current_cpu].main_task.pinned_to = Some(cpu);

esp-rtos/src/timer/embassy.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
use core::task::Waker;
22

3+
use embassy_time_queue_utils::Queue;
34
use esp_sync::NonReentrantMutex;
45

56
use crate::SCHEDULER;
67

78
pub(super) struct TimerQueueInner {
8-
queue: embassy_time_queue_utils::Queue,
9+
queue: Queue,
910
pub next_wakeup: u64,
1011
}
1112

1213
impl TimerQueueInner {
1314
const fn new() -> Self {
1415
Self {
15-
queue: embassy_time_queue_utils::Queue::new(),
16+
queue: Queue::new(),
1617
next_wakeup: u64::MAX,
1718
}
1819
}
@@ -70,13 +71,13 @@ impl embassy_time_driver::Driver for TimerQueue {
7071

7172
#[inline]
7273
fn schedule_wake(&self, at: u64, waker: &Waker) {
74+
// Note that we don't put the thread to sleep here, as other embassy tasks may be
75+
// ready. The thread will go to sleep when it can.
7376
if self.inner.with(|inner| inner.schedule_wake(at, waker)) {
7477
// Next wakeup time became shorter, re-arm the timer.
7578
// FIXME: avoid two separate critical sections.
76-
// FIXME: this likely interferes with time slicing - we just keep pushing the time slice
77-
// out on the other core, if active.
78-
SCHEDULER.with(|s| {
79-
unwrap!(s.time_driver.as_mut()).arm_next_wakeup(crate::now());
79+
SCHEDULER.with(|scheduler| {
80+
unwrap!(scheduler.time_driver.as_mut()).arm_next_wakeup(crate::now());
8081
});
8182
}
8283
}

esp-sync/src/lib.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,6 @@ mod multi_core {
142142
}
143143
}
144144

145-
#[inline]
146-
fn is_owned_by(&self, thread: usize) -> bool {
147-
self.owner.load(Ordering::Relaxed) == thread
148-
}
149-
150145
#[inline]
151146
pub fn lock(&self, lock: &impl crate::RawLock) -> crate::RestoreState {
152147
// We acquire the lock inside an interrupt-free context to prevent a subtle
@@ -199,7 +194,7 @@ mod multi_core {
199194
#[inline]
200195
pub unsafe fn unlock(&self) {
201196
#[cfg(debug_assertions)]
202-
if !self.is_owned_by(thread_id()) {
197+
if self.owner.load(Ordering::Relaxed) != thread_id() {
203198
panic_attempt_unlock_not_owned();
204199
}
205200
self.owner.store(UNUSED_THREAD_ID_VALUE, Ordering::Release);

0 commit comments

Comments
 (0)