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