mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-27 04:10:28 +00:00
Process timer queue in timer interrupt handler (#4147)
This commit is contained in:
parent
37f048f20b
commit
e57530c9b5
@ -35,9 +35,6 @@ impl SchedulerEvent {
|
||||
// If this is NOT set, the event is a cooperative yield of some sort (time slicing, self-yield).
|
||||
const TASK_BLOCKED: usize = 1 << 0;
|
||||
|
||||
// If this is set, the timer queue should be processed.
|
||||
const TIMER_EVENT: usize = 1 << 1;
|
||||
|
||||
fn contains(self, bit: usize) -> bool {
|
||||
self.0 & bit != 0
|
||||
}
|
||||
@ -49,17 +46,9 @@ impl SchedulerEvent {
|
||||
self.set(Self::TASK_BLOCKED)
|
||||
}
|
||||
|
||||
pub(crate) fn set_timer_event(&mut self) {
|
||||
self.set(Self::TIMER_EVENT)
|
||||
}
|
||||
|
||||
pub(crate) fn is_cooperative_yield(self) -> bool {
|
||||
!self.contains(Self::TASK_BLOCKED)
|
||||
}
|
||||
|
||||
pub(crate) fn is_timer_event(self) -> bool {
|
||||
self.contains(Self::TIMER_EVENT)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SchedulerState {
|
||||
@ -154,22 +143,6 @@ impl SchedulerState {
|
||||
self.delete_marked_tasks();
|
||||
|
||||
let event = core::mem::take(&mut self.event);
|
||||
let time_driver = unwrap!(self.time_driver.as_mut());
|
||||
|
||||
if event.is_timer_event() {
|
||||
time_driver.handle_alarm(|ready_task| {
|
||||
debug_assert_eq!(
|
||||
ready_task.state(),
|
||||
task::TaskState::Sleeping,
|
||||
"task: {:?}",
|
||||
ready_task
|
||||
);
|
||||
|
||||
debug!("Task {:?} is ready", ready_task);
|
||||
|
||||
self.run_queue.mark_task_ready(ready_task);
|
||||
});
|
||||
}
|
||||
|
||||
if event.is_cooperative_yield()
|
||||
&& let Some(current_task) = self.current_task
|
||||
@ -232,6 +205,7 @@ impl SchedulerState {
|
||||
let arm_next_timeslice_tick = priority
|
||||
.map(|p| !self.run_queue.is_level_empty(p))
|
||||
.unwrap_or(false);
|
||||
let time_driver = unwrap!(self.time_driver.as_mut());
|
||||
time_driver.arm_next_wakeup(arm_next_timeslice_tick);
|
||||
}
|
||||
|
||||
|
@ -169,28 +169,43 @@ impl TimeDriver {
|
||||
#[esp_hal::ram]
|
||||
extern "C" fn timer_tick_handler(#[cfg(xtensa)] _context: &mut esp_hal::trapframe::TrapFrame) {
|
||||
SCHEDULER.with(|scheduler| {
|
||||
unwrap!(scheduler.time_driver.as_mut())
|
||||
.timer
|
||||
.clear_interrupt();
|
||||
let time_driver = unwrap!(scheduler.time_driver.as_mut());
|
||||
|
||||
scheduler.event.set_timer_event();
|
||||
time_driver.timer.clear_interrupt();
|
||||
|
||||
// `Scheduler::switch_task` must be called on a single interrupt priority level only.
|
||||
// To ensure this, we call yield_task to pend the software interrupt.
|
||||
//
|
||||
// RISC-V: esp-hal's interrupt handler can process multiple interrupts before handing
|
||||
// control back to the interrupted context. This can result in two task switches
|
||||
// before the first one's context save could run. To prevent this, here we only
|
||||
// trigger the software interrupt which will then run the scheduler.
|
||||
//
|
||||
// ESP32: Because on ESP32 the software interrupt is triggered at priority 3 but
|
||||
// the timer interrupt is triggered at priority 1, we need to trigger the
|
||||
// software interrupt manually.
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(riscv, esp32))] {
|
||||
crate::task::yield_task();
|
||||
} else {
|
||||
scheduler.switch_task(_context)
|
||||
let mut switch_tasks = false;
|
||||
time_driver.handle_alarm(|ready_task| {
|
||||
debug_assert_eq!(
|
||||
ready_task.state(),
|
||||
crate::task::TaskState::Sleeping,
|
||||
"task: {:?}",
|
||||
ready_task
|
||||
);
|
||||
|
||||
debug!("Task {:?} is ready", ready_task);
|
||||
|
||||
scheduler.run_queue.mark_task_ready(ready_task);
|
||||
switch_tasks = true;
|
||||
});
|
||||
|
||||
if switch_tasks {
|
||||
// `Scheduler::switch_task` must be called on a single interrupt priority level only.
|
||||
// To ensure this, we call yield_task to pend the software interrupt.
|
||||
//
|
||||
// RISC-V: esp-hal's interrupt handler can process multiple interrupts before handing
|
||||
// control back to the interrupted context. This can result in two task switches
|
||||
// before the first one's context save could run. To prevent this, here we only
|
||||
// trigger the software interrupt which will then run the scheduler.
|
||||
//
|
||||
// ESP32: Because on ESP32 the software interrupt is triggered at priority 3 but
|
||||
// the timer interrupt is triggered at priority 1, we need to trigger the
|
||||
// software interrupt manually.
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(riscv, esp32))] {
|
||||
crate::task::yield_task();
|
||||
} else {
|
||||
scheduler.switch_task(_context)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user