Skip to content

Commit 67e8a83

Browse files
authored
Deoptimize task deletion, ensure RunQueue::pop is inlined (#4308)
* Deoptimize task deletion, ensure RunQueue::pop is inlined * Check for stack overflow of deleted tasks
1 parent 4ee4d73 commit 67e8a83

File tree

2 files changed

+34
-35
lines changed

2 files changed

+34
-35
lines changed

esp-rtos/src/scheduler.rs

Lines changed: 30 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -168,20 +168,18 @@ impl SchedulerState {
168168
task_ptr
169169
}
170170

171+
#[cold]
172+
#[inline(never)]
171173
fn delete_marked_tasks(&mut self, cpu: Cpu) {
172174
let current_cpu = cpu as usize;
173175
let mut to_delete = core::mem::take(&mut self.to_delete);
174176
'outer: while let Some(task_ptr) = to_delete.pop() {
175177
assert!(task_ptr.state() == TaskState::Deleted);
176178
for cpu in 0..Cpu::COUNT {
177-
if Some(task_ptr) == self.per_cpu[cpu].current_task {
178-
if cpu == current_cpu {
179-
self.per_cpu[cpu].current_task = None;
180-
} else {
181-
// We can't delete a task that is currently running on another CPU.
182-
self.to_delete.push(task_ptr);
183-
continue 'outer;
184-
}
179+
if cpu != current_cpu && Some(task_ptr) == self.per_cpu[cpu].current_task {
180+
// We can't delete a task that is currently running on another CPU.
181+
self.to_delete.push(task_ptr);
182+
continue 'outer;
185183
}
186184
}
187185

@@ -197,25 +195,22 @@ impl SchedulerState {
197195
let cpu = Cpu::current();
198196
let current_cpu = cpu as usize;
199197

200-
let mut priority = if let Some(current_task) = self.per_cpu[current_cpu].current_task {
201-
let current_task = unsafe { current_task.as_ref() };
202-
current_task.ensure_no_stack_overflow();
203-
Some(current_task.priority)
204-
} else {
205-
None
206-
};
207-
208-
self.delete_marked_tasks(cpu);
198+
if !self.to_delete.is_empty() {
199+
self.delete_marked_tasks(cpu);
200+
}
209201

210202
let current_task = self.per_cpu[current_cpu].current_task;
211-
if let Some(current_task) = current_task
212-
&& current_task.state() == TaskState::Ready
213-
{
214-
// Current task is still ready, mark it as such.
215-
debug!("re-queueing current task: {:?}", current_task);
216-
self.run_queue.mark_task_ready(&self.per_cpu, current_task);
217-
}
203+
if let Some(current_task) = current_task {
204+
unsafe { current_task.as_ref().ensure_no_stack_overflow() };
218205

206+
if current_task.state() == TaskState::Ready {
207+
// Current task is still ready, mark it as such.
208+
debug!("re-queueing current task: {:?}", current_task);
209+
self.run_queue.mark_task_ready(&self.per_cpu, current_task);
210+
}
211+
};
212+
213+
let mut arm_next_timeslice_tick = false;
219214
let next_task = self.run_queue.pop();
220215
if next_task != current_task {
221216
trace!("Switching task {:?} -> {:?}", current_task, next_task);
@@ -243,18 +238,19 @@ impl SchedulerState {
243238
core::ptr::null_mut()
244239
};
245240

246-
let new_core_priority =
247-
next_task.map_or(Priority::ZERO, |t| t.priority(&mut self.run_queue));
248241
let next_context = if let Some(next) = next_task {
249-
priority = Some(new_core_priority);
250242
#[cfg(feature = "rtos-trace")]
251243
rtos_trace::trace::task_exec_begin(next.rtos_trace_id());
252244

253245
unsafe { next.as_ref().set_up_stack_watchpoint() };
254246

247+
// If there are more tasks at this priority level, we need to schedule a timeslice
248+
// tick.
249+
let new_core_priority = next.priority(&mut self.run_queue);
250+
arm_next_timeslice_tick = !self.run_queue.is_level_empty(new_core_priority);
251+
255252
unsafe { &raw mut (*next.as_ptr()).cpu_context }
256253
} else {
257-
priority = None;
258254
// If there is no next task, set up and return to the idle hook.
259255
// Reuse the stack frame of the main task. Note that this requires the main task to
260256
// be pinned to the current CPU. If we're switching out the main task, however, we
@@ -310,13 +306,6 @@ impl SchedulerState {
310306
self.per_cpu[current_cpu].current_task = next_task;
311307
}
312308

313-
// The current task is not in the run queue. If the run queue on the current priority
314-
// level is empty, the current task is the only one running at its priority
315-
// level. In this case, we don't need time slicing.
316-
let arm_next_timeslice_tick = priority
317-
.map(|p| !self.run_queue.is_level_empty(p))
318-
.unwrap_or(false);
319-
320309
let time_driver = unwrap!(self.time_driver.as_mut());
321310
let now = crate::now();
322311
time_driver.set_time_slice(cpu, now, arm_next_timeslice_tick);
@@ -351,6 +340,10 @@ impl SchedulerState {
351340
self.to_delete.push(task_to_delete);
352341
task_to_delete.set_state(TaskState::Deleted);
353342

343+
if is_current {
344+
self.per_cpu[current_cpu].current_task = None;
345+
}
346+
354347
is_current
355348
}
356349

@@ -372,6 +365,8 @@ impl SchedulerState {
372365
}
373366

374367
fn delete_task(&mut self, mut to_delete: TaskPtr) {
368+
unsafe { to_delete.as_ref().ensure_no_stack_overflow() };
369+
375370
self.remove_from_all_queues(to_delete);
376371

377372
unsafe {

esp-rtos/src/task/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,10 @@ impl<E: TaskListElement> TaskList<E> {
224224
Some(task)
225225
})
226226
}
227+
228+
pub(crate) fn is_empty(&self) -> bool {
229+
self.head.is_none()
230+
}
227231
}
228232

229233
/// A singly linked queue of tasks.

0 commit comments

Comments
 (0)