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-S3: Fix GPIO interrupt handler crashing when using GPIO48. (#898)
- Fixed short wait times in embassy causing hangs (#906)
### Removed

View File

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

View File

@ -32,15 +32,23 @@ impl EmbassyTimer {
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];
alarm.timestamp.set(u64::MAX);
if let Some((f, ctx)) = alarm.callback.get() {
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) {
critical_section::with(|cs| {
self.clear_interrupt(id);
@ -83,17 +91,20 @@ impl EmbassyTimer {
alarm: embassy_time::driver::AlarmHandle,
timestamp: u64,
) -> bool {
critical_section::with(|cs| {
critical_section::with(|_cs| {
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
// time.
alarm_state.timestamp.set(timestamp);
// time. In this case the interrupt handler will pend a wakeup when we exit the
// critical section.
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) {
@ -107,18 +118,9 @@ impl EmbassyTimer {
fn arm(&self, id: usize, timestamp: u64) {
match id {
0 => {
self.alarm0.set_target(timestamp);
self.alarm0.enable_interrupt(true);
}
1 => {
self.alarm1.set_target(timestamp);
self.alarm1.enable_interrupt(true);
}
2 => {
self.alarm2.set_target(timestamp);
self.alarm2.enable_interrupt(true);
}
0 => self.alarm0.set_target(timestamp),
1 => self.alarm1.set_target(timestamp),
2 => self.alarm2.set_target(timestamp),
_ => {}
}
}

View File

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