1451: Work around xtensa deadlock, take 2 r=Dirbaio a=bugadani

This PR is another go at trying to do something with #1449. The commit was part of the previous attempt but mistakenly discarded as I still experienced lockups. However, after further testing, it looks like that lockup is caused by something else.

This is a manual, "cpu-local" critical section impl that should be good enough on dual-core CPUs, although the implementation still contains `SIGNAL_WORK_THREAD_MODE` which is absolutely not correct on dual-core. This approach was chosen because:
 - not taking the global lock technically allows the second core to run
 - wrapping the signal read and the sleep in a critical section prevents a race condition that would cause the CPU to sleep longer than ideal if an interrupt hits after reading, but before sleeping.

Co-authored-by: Dániel Buga <bugadani@gmail.com>
This commit is contained in:
bors[bot] 2023-05-14 22:20:15 +00:00 committed by GitHub
commit 6e93d193cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -63,21 +63,29 @@ mod thread {
loop { loop {
unsafe { unsafe {
self.inner.poll(); self.inner.poll();
// Manual critical section implementation that only masks interrupts handlers.
// We must not acquire the cross-core on dual-core systems because that would
// prevent the other core from doing useful work while this core is sleeping.
let token: critical_section::RawRestoreState;
core::arch::asm!("rsil {0}, 5", out(reg) token);
// we do not care about race conditions between the load and store operations, interrupts // we do not care about race conditions between the load and store operations, interrupts
// will only set this value to true. // will only set this value to true.
// if there is work to do, loop back to polling // if there is work to do, loop back to polling
// TODO can we relax this? if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) {
critical_section::with(|_| { SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) {
SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); core::arch::asm!(
} else { "wsr.ps {0}",
// waiti sets the PS.INTLEVEL when slipping into sleep "rsync", in(reg) token)
// because critical sections in Xtensa are implemented via increasing } else {
// PS.INTLEVEL the critical section ends here // waiti sets the PS.INTLEVEL when slipping into sleep
// take care not add code after `waiti` if it needs to be inside the CS // because critical sections in Xtensa are implemented via increasing
core::arch::asm!("waiti 0"); // critical section ends here // PS.INTLEVEL the critical section ends here
} // take care not add code after `waiti` if it needs to be inside the CS
}); core::arch::asm!("waiti 0"); // critical section ends here
}
} }
} }
} }