diff --git a/esp-preempt/src/scheduler.rs b/esp-preempt/src/scheduler.rs index 3c51b146d..658fed313 100644 --- a/esp-preempt/src/scheduler.rs +++ b/esp-preempt/src/scheduler.rs @@ -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); } diff --git a/esp-preempt/src/timer/mod.rs b/esp-preempt/src/timer/mod.rs index 7b4dac4d8..52eb5d87f 100644 --- a/esp-preempt/src/timer/mod.rs +++ b/esp-preempt/src/timer/mod.rs @@ -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) + } } } });