Fix us waits in embassy hanging (#906)

* Remove unused field from AlarmState

* Clean up non-idiomatic code

* Simplify timg0 time driver

* Better explain instant firing, hide fn, re-add check

* Add changelog entry
This commit is contained in:
Dániel Buga 2023-11-07 15:34:14 +01:00 committed by GitHub
parent 111d00617f
commit 780a7f5e4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 44 additions and 53 deletions

View File

@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- ESP32-C2/C3 examples: fix build error (#899) - ESP32-C2/C3 examples: fix build error (#899)
- ESP32-S3: Fix GPIO interrupt handler crashing when using GPIO48. (#898) - ESP32-S3: Fix GPIO interrupt handler crashing when using GPIO48. (#898)
- Fixed short wait times in embassy causing hangs (#906)
### Removed ### Removed

View File

@ -101,7 +101,6 @@ pub fn init(clocks: &Clocks, td: time_driver::TimerType) {
} }
pub struct AlarmState { pub struct AlarmState {
pub timestamp: Cell<u64>,
pub callback: Cell<Option<(fn(*mut ()), *mut ())>>, pub callback: Cell<Option<(fn(*mut ()), *mut ())>>,
pub allocated: Cell<bool>, pub allocated: Cell<bool>,
} }
@ -111,7 +110,6 @@ unsafe impl Send for AlarmState {}
impl AlarmState { impl AlarmState {
pub const fn new() -> Self { pub const fn new() -> Self {
Self { Self {
timestamp: Cell::new(0),
callback: Cell::new(None), callback: Cell::new(None),
allocated: Cell::new(false), allocated: Cell::new(false),
} }
@ -124,18 +122,17 @@ impl Driver for EmbassyTimer {
} }
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
return critical_section::with(|cs| { critical_section::with(|cs| {
let alarms = self.alarms.borrow(cs); for (i, alarm) in self.alarms.borrow(cs).iter().enumerate() {
for i in 0..time_driver::ALARM_COUNT { if !alarm.allocated.get() {
let c = alarms.get_unchecked(i);
if !c.allocated.get() {
// set alarm so it is not overwritten // set alarm so it is not overwritten
c.allocated.set(true); alarm.allocated.set(true);
return Option::Some(AlarmHandle::new(i as u8)); self.on_alarm_allocated(i);
return Some(AlarmHandle::new(i as u8));
} }
} }
return Option::None; None
}); })
} }
fn set_alarm_callback( fn set_alarm_callback(

View File

@ -32,15 +32,23 @@ impl EmbassyTimer {
SystemTimer::now() SystemTimer::now()
} }
pub(crate) fn trigger_alarm(&self, n: usize, cs: CriticalSection) { fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
let alarm = &self.alarms.borrow(cs)[n]; let alarm = &self.alarms.borrow(cs)[n];
alarm.timestamp.set(u64::MAX);
if let Some((f, ctx)) = alarm.callback.get() { if let Some((f, ctx)) = alarm.callback.get() {
f(ctx); f(ctx);
} }
} }
pub(super) fn on_alarm_allocated(&self, n: usize) {
match n {
0 => self.alarm0.enable_interrupt(true),
1 => self.alarm1.enable_interrupt(true),
2 => self.alarm2.enable_interrupt(true),
_ => {}
}
}
fn on_interrupt(&self, id: usize) { fn on_interrupt(&self, id: usize) {
critical_section::with(|cs| { critical_section::with(|cs| {
self.clear_interrupt(id); self.clear_interrupt(id);
@ -83,17 +91,20 @@ impl EmbassyTimer {
alarm: embassy_time::driver::AlarmHandle, alarm: embassy_time::driver::AlarmHandle,
timestamp: u64, timestamp: u64,
) -> bool { ) -> bool {
critical_section::with(|cs| { critical_section::with(|_cs| {
let n = alarm.id() as usize; let n = alarm.id() as usize;
let alarm_state = &self.alarms.borrow(cs)[n];
// The hardware fires the alarm even if timestamp is lower than the current // The hardware fires the alarm even if timestamp is lower than the current
// time. // time. In this case the interrupt handler will pend a wakeup when we exit the
alarm_state.timestamp.set(timestamp); // critical section.
self.arm(n, timestamp); self.arm(n, timestamp);
});
true // In theory, the above comment is true. However, in practice, we seem to be
}) // missing interrupt for very short timeouts, so let's make sure and catch
// timestamps that already passed. Returning `false` means embassy will
// run one more poll loop.
Self::now() < timestamp
} }
fn clear_interrupt(&self, id: usize) { fn clear_interrupt(&self, id: usize) {
@ -107,18 +118,9 @@ impl EmbassyTimer {
fn arm(&self, id: usize, timestamp: u64) { fn arm(&self, id: usize, timestamp: u64) {
match id { match id {
0 => { 0 => self.alarm0.set_target(timestamp),
self.alarm0.set_target(timestamp); 1 => self.alarm1.set_target(timestamp),
self.alarm0.enable_interrupt(true); 2 => self.alarm2.set_target(timestamp),
}
1 => {
self.alarm1.set_target(timestamp);
self.alarm1.enable_interrupt(true);
}
2 => {
self.alarm2.set_target(timestamp);
self.alarm2.enable_interrupt(true);
}
_ => {} _ => {}
} }
} }

View File

@ -33,15 +33,16 @@ impl EmbassyTimer {
unsafe { Timer0::<TIMG0>::steal() }.now() unsafe { Timer0::<TIMG0>::steal() }.now()
} }
pub(crate) fn trigger_alarm(&self, n: usize, cs: CriticalSection) { fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
let alarm = &self.alarms.borrow(cs)[n]; let alarm = &self.alarms.borrow(cs)[n];
alarm.timestamp.set(u64::MAX);
if let Some((f, ctx)) = alarm.callback.get() { if let Some((f, ctx)) = alarm.callback.get() {
f(ctx); f(ctx);
} }
} }
pub(super) fn on_alarm_allocated(&self, _n: usize) {}
fn on_interrupt<Timer: Instance>(&self, id: u8, mut timer: Timer) { fn on_interrupt<Timer: Instance>(&self, id: u8, mut timer: Timer) {
critical_section::with(|cs| { critical_section::with(|cs| {
timer.clear_interrupt(); timer.clear_interrupt();
@ -89,29 +90,19 @@ impl EmbassyTimer {
timestamp: u64, timestamp: u64,
) -> bool { ) -> bool {
critical_section::with(|cs| { critical_section::with(|cs| {
let n = alarm.id() as usize;
let alarm_state = &self.alarms.borrow(cs)[n];
#[cfg(any(esp32, esp32s2, esp32s3))]
if n == 1 {
let tg = unsafe { Timer1::<TIMG0>::steal() };
return Self::set_alarm_impl(tg, alarm_state, timestamp);
}
let tg = unsafe { Timer0::<TIMG0>::steal() };
Self::set_alarm_impl(tg, alarm_state, timestamp)
})
}
fn set_alarm_impl<Timer: Instance>(
mut tg: Timer,
alarm_state: &AlarmState,
timestamp: u64,
) -> bool {
// The hardware fires the alarm even if timestamp is lower than the current // The hardware fires the alarm even if timestamp is lower than the current
// time. // time. In this case the interrupt handler will pend a wakeup when we exit the
alarm_state.timestamp.set(timestamp); // critical section.
#[cfg(any(esp32, esp32s2, esp32s3))]
if alarm.id() == 1 {
let mut tg = unsafe { Timer1::<TIMG0>::steal() };
Self::arm(&mut tg, timestamp); Self::arm(&mut tg, timestamp);
return;
}
let mut tg = unsafe { Timer0::<TIMG0>::steal() };
Self::arm(&mut tg, timestamp);
});
true true
} }