[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

@@ -39,6 +39,10 @@ class NotifierThread : public wpi::util::SafeThread {
public:
void Main() override;
void SetAlarm(HAL_NotifierHandle notifierHandle,
std::shared_ptr<Notifier>& notifier, uint64_t alarmTime,
uint64_t intervalTime, bool absolute, int32_t* status);
void ProcessAlarms();
UnlimitedHandleResource<HAL_NotifierHandle, Notifier,
@@ -97,6 +101,30 @@ void NotifierThread::Main() {
}
}
void NotifierThread::SetAlarm(HAL_NotifierHandle notifierHandle,
std::shared_ptr<Notifier>& notifier,
uint64_t alarmTime, uint64_t intervalTime,
bool absolute, int32_t* status) {
if (!absolute) {
alarmTime += HAL_GetFPGATime(status);
}
uint64_t prevWakeup = UINT64_MAX;
if (!m_alarmQueue.empty()) {
prevWakeup = m_alarmQueue.top().notifier->alarmTime;
m_alarmQueue.remove({notifierHandle, notifier});
}
notifier->alarmTime = alarmTime;
notifier->intervalTime = intervalTime;
notifier->overrunCount = 0;
m_alarmQueue.push({notifierHandle, notifier});
// wake up notifier thread if needed
if (alarmTime < prevWakeup) {
m_cond.notify_all();
}
}
void NotifierThread::ProcessAlarms() {
int32_t status = 0;
uint64_t curTime = HAL_GetFPGATime(&status);
@@ -182,25 +210,8 @@ void HAL_SetNotifierAlarm(HAL_NotifierHandle notifierHandle, uint64_t alarmTime,
if (!notifier) {
return;
}
if (!absolute) {
alarmTime += HAL_GetFPGATime(status);
}
uint64_t prevWakeup = UINT64_MAX;
if (!thr->m_alarmQueue.empty()) {
prevWakeup = thr->m_alarmQueue.top().notifier->alarmTime;
thr->m_alarmQueue.remove({notifierHandle, notifier});
}
notifier->alarmTime = alarmTime;
notifier->intervalTime = intervalTime;
notifier->overrunCount = 0;
thr->m_alarmQueue.push({notifierHandle, notifier});
// wake up notifier thread if needed
if (alarmTime < prevWakeup) {
thr->m_cond.notify_all();
}
thr->SetAlarm(notifierHandle, notifier, alarmTime, intervalTime, absolute,
status);
}
void HAL_CancelNotifierAlarm(HAL_NotifierHandle notifierHandle,
@@ -213,16 +224,23 @@ void HAL_CancelNotifierAlarm(HAL_NotifierHandle notifierHandle,
thr->m_alarmQueue.remove({notifierHandle, notifier});
notifier->alarmTime = UINT64_MAX;
notifier->handlerSignaled.clear();
}
void HAL_AcknowledgeNotifierAlarm(HAL_NotifierHandle notifierHandle,
HAL_Bool setAlarm, uint64_t alarmTime,
uint64_t intervalTime, HAL_Bool absolute,
int32_t* status) {
auto notifier =
notifierInstance->owner.GetThread()->m_handles.Get(notifierHandle);
auto thr = notifierInstance->owner.GetThread();
auto notifier = thr->m_handles.Get(notifierHandle);
if (!notifier) {
return;
}
notifier->handlerSignaled.clear();
if (setAlarm) {
thr->SetAlarm(notifierHandle, notifier, alarmTime, intervalTime, absolute,
status);
}
}
int32_t HAL_GetNotifierOverrun(HAL_NotifierHandle notifierHandle,