[hal,wpilib] Fix TimedRobot notifier race (#8445)

It was possible for the alarm to fire between the set alarm and ack,
resulting in a hang on next wait. It's not possible to ack before set
alarm due to a race in sim step timing, so the fix is to provide an
atomic ack and set alarm; the easiest way to implement this in the API
was to change ack to optionally also set the alarm again.
This commit is contained in:
Peter Johnson
2025-12-04 09:59:59 -07:00
committed by GitHub
parent d1b1703c86
commit 934f8d9c15
12 changed files with 147 additions and 78 deletions

View File

@@ -139,14 +139,11 @@ public class TimedRobot extends IterativeRobotBase {
// at the end of the loop.
var callback = m_callbacks.poll();
NotifierJNI.setNotifierAlarm(m_notifier, callback.expirationTime, 0, true);
// Acknowledge previous alarm after setting the next one to avoid a race
// against getting the next notifier timeout in HALSIM StepTiming.
if (first) {
first = false;
NotifierJNI.setNotifierAlarm(m_notifier, callback.expirationTime, 0, true);
} else {
NotifierJNI.acknowledgeNotifierAlarm(m_notifier);
NotifierJNI.acknowledgeNotifierAlarm(m_notifier, true, callback.expirationTime, 0, true);
}
try {

View File

@@ -95,7 +95,7 @@ public class Notifier implements AutoCloseable {
}
// Acknowledge the alarm
NotifierJNI.acknowledgeNotifierAlarm(notifier);
NotifierJNI.acknowledgeNotifierAlarm(notifier, false, 0, 0, false);
}
});
m_thread.setName("Notifier");

View File

@@ -120,7 +120,7 @@ public class Watchdog implements Closeable, Comparable<Watchdog> {
m_watchdogs.remove(this);
m_expirationTime = m_startTime + m_timeout;
m_watchdogs.add(this);
updateAlarm();
updateAlarm(false);
} finally {
m_queueMutex.unlock();
}
@@ -194,7 +194,7 @@ public class Watchdog implements Closeable, Comparable<Watchdog> {
m_watchdogs.remove(this);
m_expirationTime = m_startTime + m_timeout;
m_watchdogs.add(this);
updateAlarm();
updateAlarm(false);
} finally {
m_queueMutex.unlock();
}
@@ -205,7 +205,7 @@ public class Watchdog implements Closeable, Comparable<Watchdog> {
m_queueMutex.lock();
try {
m_watchdogs.remove(this);
updateAlarm();
updateAlarm(false);
} finally {
m_queueMutex.unlock();
}
@@ -223,9 +223,12 @@ public class Watchdog implements Closeable, Comparable<Watchdog> {
}
@SuppressWarnings("resource")
private static void updateAlarm() {
private static void updateAlarm(boolean acknowledge) {
if (m_watchdogs.isEmpty()) {
NotifierJNI.cancelNotifierAlarm(m_notifier);
} else if (acknowledge) {
NotifierJNI.acknowledgeNotifierAlarm(
m_notifier, true, (long) (m_watchdogs.peek().m_expirationTime * 1e6), 0, true);
} else {
NotifierJNI.setNotifierAlarm(
m_notifier, (long) (m_watchdogs.peek().m_expirationTime * 1e6), 0, true);
@@ -277,9 +280,7 @@ public class Watchdog implements Closeable, Comparable<Watchdog> {
watchdog.m_callback.run();
m_queueMutex.lock();
updateAlarm();
NotifierJNI.acknowledgeNotifierAlarm(m_notifier);
updateAlarm(true);
} finally {
m_queueMutex.unlock();
}