2020-12-26 14:12:05 -08:00
|
|
|
// Copyright (c) FIRST and other WPILib contributors.
|
|
|
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
|
|
|
// the WPILib BSD license file in the root directory of this project.
|
2018-06-24 02:29:21 -05:00
|
|
|
|
2018-07-20 00:03:45 -07:00
|
|
|
#include "frc/Watchdog.h"
|
2018-06-24 02:29:21 -05:00
|
|
|
|
2020-07-21 22:58:16 -07:00
|
|
|
#include <atomic>
|
2021-05-24 23:36:26 -07:00
|
|
|
#include <thread>
|
2020-12-28 10:12:52 -08:00
|
|
|
#include <utility>
|
2020-07-21 22:58:16 -07:00
|
|
|
|
2021-05-24 23:36:26 -07:00
|
|
|
#include <fmt/format.h>
|
2020-07-21 22:58:16 -07:00
|
|
|
#include <hal/Notifier.h>
|
2021-05-24 23:36:26 -07:00
|
|
|
#include <wpi/mutex.h>
|
2020-10-04 12:49:23 -07:00
|
|
|
#include <wpi/priority_queue.h>
|
2018-06-24 02:29:21 -05:00
|
|
|
|
2021-04-18 20:35:29 -07:00
|
|
|
#include "frc/Errors.h"
|
2021-05-28 22:06:59 -07:00
|
|
|
#include "frc/Timer.h"
|
2020-05-21 06:57:06 +03:00
|
|
|
|
2018-06-24 02:29:21 -05:00
|
|
|
using namespace frc;
|
|
|
|
|
|
2020-07-21 22:58:16 -07:00
|
|
|
class Watchdog::Impl {
|
2018-12-01 00:05:33 -08:00
|
|
|
public:
|
2020-07-21 22:58:16 -07:00
|
|
|
Impl();
|
|
|
|
|
~Impl();
|
|
|
|
|
|
2018-12-01 00:05:33 -08:00
|
|
|
template <typename T>
|
2019-02-14 23:44:30 -05:00
|
|
|
struct DerefGreater {
|
2018-12-01 00:05:33 -08:00
|
|
|
constexpr bool operator()(const T& lhs, const T& rhs) const {
|
|
|
|
|
return *lhs > *rhs;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-07-21 22:58:16 -07:00
|
|
|
wpi::mutex m_mutex;
|
|
|
|
|
std::atomic<HAL_NotifierHandle> m_notifier;
|
2020-10-04 12:49:23 -07:00
|
|
|
wpi::priority_queue<Watchdog*, std::vector<Watchdog*>,
|
|
|
|
|
DerefGreater<Watchdog*>>
|
2018-12-01 00:05:33 -08:00
|
|
|
m_watchdogs;
|
|
|
|
|
|
2020-07-21 22:58:16 -07:00
|
|
|
void UpdateAlarm();
|
|
|
|
|
|
2018-12-01 00:05:33 -08:00
|
|
|
private:
|
2020-07-21 22:58:16 -07:00
|
|
|
void Main();
|
|
|
|
|
|
|
|
|
|
std::thread m_thread;
|
2018-12-01 00:05:33 -08:00
|
|
|
};
|
|
|
|
|
|
2020-07-21 22:58:16 -07:00
|
|
|
Watchdog::Impl::Impl() {
|
|
|
|
|
int32_t status = 0;
|
|
|
|
|
m_notifier = HAL_InitializeNotifier(&status);
|
2022-10-19 10:49:27 -07:00
|
|
|
FRC_CheckErrorStatus(status, "starting watchdog notifier");
|
2020-07-21 22:58:16 -07:00
|
|
|
HAL_SetNotifierName(m_notifier, "Watchdog", &status);
|
|
|
|
|
|
2022-10-15 16:33:14 -07:00
|
|
|
m_thread = std::thread([=, this] { Main(); });
|
2020-07-21 22:58:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Watchdog::Impl::~Impl() {
|
|
|
|
|
int32_t status = 0;
|
|
|
|
|
// atomically set handle to 0, then clean
|
|
|
|
|
HAL_NotifierHandle handle = m_notifier.exchange(0);
|
|
|
|
|
HAL_StopNotifier(handle, &status);
|
2022-10-19 10:49:27 -07:00
|
|
|
FRC_ReportError(status, "stopping watchdog notifier");
|
2020-07-21 22:58:16 -07:00
|
|
|
|
|
|
|
|
// Join the thread to ensure the handler has exited.
|
2020-12-28 12:58:06 -08:00
|
|
|
if (m_thread.joinable()) {
|
|
|
|
|
m_thread.join();
|
|
|
|
|
}
|
2020-07-21 22:58:16 -07:00
|
|
|
|
|
|
|
|
HAL_CleanNotifier(handle, &status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Watchdog::Impl::UpdateAlarm() {
|
|
|
|
|
int32_t status = 0;
|
|
|
|
|
// Return if we are being destructed, or were not created successfully
|
|
|
|
|
auto notifier = m_notifier.load();
|
2020-12-28 12:58:06 -08:00
|
|
|
if (notifier == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (m_watchdogs.empty()) {
|
2020-07-21 22:58:16 -07:00
|
|
|
HAL_CancelNotifierAlarm(notifier, &status);
|
2020-12-28 12:58:06 -08:00
|
|
|
} else {
|
2020-07-21 22:58:16 -07:00
|
|
|
HAL_UpdateNotifierAlarm(
|
|
|
|
|
notifier,
|
2021-10-25 08:58:12 -07:00
|
|
|
static_cast<uint64_t>(m_watchdogs.top()->m_expirationTime.value() *
|
2020-07-21 22:58:16 -07:00
|
|
|
1e6),
|
|
|
|
|
&status);
|
2020-12-28 12:58:06 -08:00
|
|
|
}
|
2022-10-19 10:49:27 -07:00
|
|
|
FRC_CheckErrorStatus(status, "updating watchdog notifier alarm");
|
2020-07-21 22:58:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Watchdog::Impl::Main() {
|
|
|
|
|
for (;;) {
|
|
|
|
|
int32_t status = 0;
|
|
|
|
|
HAL_NotifierHandle notifier = m_notifier.load();
|
2020-12-28 12:58:06 -08:00
|
|
|
if (notifier == 0) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-07-21 22:58:16 -07:00
|
|
|
uint64_t curTime = HAL_WaitForNotifierAlarm(notifier, &status);
|
2020-12-28 12:58:06 -08:00
|
|
|
if (curTime == 0 || status != 0) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-07-21 22:58:16 -07:00
|
|
|
|
|
|
|
|
std::unique_lock lock(m_mutex);
|
|
|
|
|
|
2020-12-28 12:58:06 -08:00
|
|
|
if (m_watchdogs.empty()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2020-07-21 22:58:16 -07:00
|
|
|
|
|
|
|
|
// If the condition variable timed out, that means a Watchdog timeout
|
|
|
|
|
// has occurred, so call its timeout function.
|
2020-10-04 12:49:23 -07:00
|
|
|
auto watchdog = m_watchdogs.pop();
|
2020-07-21 22:58:16 -07:00
|
|
|
|
|
|
|
|
units::second_t now{curTime * 1e-6};
|
|
|
|
|
if (now - watchdog->m_lastTimeoutPrintTime > kMinPrintPeriod) {
|
|
|
|
|
watchdog->m_lastTimeoutPrintTime = now;
|
|
|
|
|
if (!watchdog->m_suppressTimeoutMessage) {
|
2021-05-24 23:36:26 -07:00
|
|
|
FRC_ReportError(warn::Warning, "Watchdog not fed within {:.6f}s",
|
2021-10-25 08:58:12 -07:00
|
|
|
watchdog->m_timeout.value());
|
2018-12-01 00:05:33 -08:00
|
|
|
}
|
|
|
|
|
}
|
2020-07-21 22:58:16 -07:00
|
|
|
|
|
|
|
|
// Set expiration flag before calling the callback so any manipulation
|
|
|
|
|
// of the flag in the callback (e.g., calling Disable()) isn't
|
|
|
|
|
// clobbered.
|
|
|
|
|
watchdog->m_isExpired = true;
|
|
|
|
|
|
|
|
|
|
lock.unlock();
|
|
|
|
|
watchdog->m_callback();
|
|
|
|
|
lock.lock();
|
|
|
|
|
|
|
|
|
|
UpdateAlarm();
|
2018-12-01 00:05:33 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-03 15:58:31 -07:00
|
|
|
Watchdog::Watchdog(units::second_t timeout, std::function<void()> callback)
|
2020-12-28 10:12:52 -08:00
|
|
|
: m_timeout(timeout), m_callback(std::move(callback)), m_impl(GetImpl()) {}
|
2018-12-01 00:05:33 -08:00
|
|
|
|
2020-12-28 12:58:06 -08:00
|
|
|
Watchdog::~Watchdog() {
|
2021-04-18 20:35:29 -07:00
|
|
|
try {
|
|
|
|
|
Disable();
|
|
|
|
|
} catch (const RuntimeError& e) {
|
|
|
|
|
e.Report();
|
|
|
|
|
}
|
2020-12-28 12:58:06 -08:00
|
|
|
}
|
2018-06-24 02:29:21 -05:00
|
|
|
|
2020-12-28 12:58:06 -08:00
|
|
|
Watchdog::Watchdog(Watchdog&& rhs) {
|
|
|
|
|
*this = std::move(rhs);
|
|
|
|
|
}
|
2020-07-21 22:58:16 -07:00
|
|
|
|
|
|
|
|
Watchdog& Watchdog::operator=(Watchdog&& rhs) {
|
|
|
|
|
m_impl = rhs.m_impl;
|
|
|
|
|
std::scoped_lock lock(m_impl->m_mutex);
|
|
|
|
|
m_startTime = rhs.m_startTime;
|
|
|
|
|
m_timeout = rhs.m_timeout;
|
|
|
|
|
m_expirationTime = rhs.m_expirationTime;
|
|
|
|
|
m_callback = std::move(rhs.m_callback);
|
|
|
|
|
m_lastTimeoutPrintTime = rhs.m_lastTimeoutPrintTime;
|
|
|
|
|
m_suppressTimeoutMessage = rhs.m_suppressTimeoutMessage;
|
|
|
|
|
m_tracer = std::move(rhs.m_tracer);
|
|
|
|
|
m_isExpired = rhs.m_isExpired;
|
|
|
|
|
if (m_expirationTime != 0_s) {
|
|
|
|
|
m_impl->m_watchdogs.remove(&rhs);
|
|
|
|
|
m_impl->m_watchdogs.emplace(this);
|
|
|
|
|
}
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:06:59 -07:00
|
|
|
units::second_t Watchdog::GetTime() const {
|
|
|
|
|
return Timer::GetFPGATimestamp() - m_startTime;
|
2019-09-03 15:58:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Watchdog::SetTimeout(units::second_t timeout) {
|
2021-05-28 22:06:59 -07:00
|
|
|
m_startTime = Timer::GetFPGATimestamp();
|
2020-04-01 23:10:28 -04:00
|
|
|
m_tracer.ClearEpochs();
|
2018-12-01 00:05:33 -08:00
|
|
|
|
2020-07-21 22:58:16 -07:00
|
|
|
std::scoped_lock lock(m_impl->m_mutex);
|
2019-09-03 15:58:31 -07:00
|
|
|
m_timeout = timeout;
|
2018-12-01 00:05:33 -08:00
|
|
|
m_isExpired = false;
|
|
|
|
|
|
2020-07-21 22:58:16 -07:00
|
|
|
m_impl->m_watchdogs.remove(this);
|
|
|
|
|
m_expirationTime = m_startTime + m_timeout;
|
|
|
|
|
m_impl->m_watchdogs.emplace(this);
|
|
|
|
|
m_impl->UpdateAlarm();
|
2018-06-24 02:29:21 -05:00
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:06:59 -07:00
|
|
|
units::second_t Watchdog::GetTimeout() const {
|
2020-07-21 22:58:16 -07:00
|
|
|
std::scoped_lock lock(m_impl->m_mutex);
|
2021-05-28 22:06:59 -07:00
|
|
|
return m_timeout;
|
2018-12-01 00:05:33 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Watchdog::IsExpired() const {
|
2020-07-21 22:58:16 -07:00
|
|
|
std::scoped_lock lock(m_impl->m_mutex);
|
2018-12-01 00:05:33 -08:00
|
|
|
return m_isExpired;
|
|
|
|
|
}
|
2018-06-24 02:29:21 -05:00
|
|
|
|
2021-06-06 16:13:58 -07:00
|
|
|
void Watchdog::AddEpoch(std::string_view epochName) {
|
2020-04-01 23:10:28 -04:00
|
|
|
m_tracer.AddEpoch(epochName);
|
2018-06-24 02:29:21 -05:00
|
|
|
}
|
|
|
|
|
|
2020-12-28 12:58:06 -08:00
|
|
|
void Watchdog::PrintEpochs() {
|
|
|
|
|
m_tracer.PrintEpochs();
|
|
|
|
|
}
|
2018-06-24 02:29:21 -05:00
|
|
|
|
2020-12-28 12:58:06 -08:00
|
|
|
void Watchdog::Reset() {
|
|
|
|
|
Enable();
|
|
|
|
|
}
|
2018-06-24 02:29:21 -05:00
|
|
|
|
|
|
|
|
void Watchdog::Enable() {
|
2021-05-28 22:06:59 -07:00
|
|
|
m_startTime = Timer::GetFPGATimestamp();
|
2020-04-01 23:10:28 -04:00
|
|
|
m_tracer.ClearEpochs();
|
2018-12-01 00:05:33 -08:00
|
|
|
|
2020-07-21 22:58:16 -07:00
|
|
|
std::scoped_lock lock(m_impl->m_mutex);
|
2018-12-01 00:05:33 -08:00
|
|
|
m_isExpired = false;
|
|
|
|
|
|
2020-07-21 22:58:16 -07:00
|
|
|
m_impl->m_watchdogs.remove(this);
|
|
|
|
|
m_expirationTime = m_startTime + m_timeout;
|
|
|
|
|
m_impl->m_watchdogs.emplace(this);
|
|
|
|
|
m_impl->UpdateAlarm();
|
2018-06-24 02:29:21 -05:00
|
|
|
}
|
|
|
|
|
|
2018-12-01 00:05:33 -08:00
|
|
|
void Watchdog::Disable() {
|
2020-07-21 22:58:16 -07:00
|
|
|
std::scoped_lock lock(m_impl->m_mutex);
|
2018-06-24 02:29:21 -05:00
|
|
|
|
2020-07-21 22:58:16 -07:00
|
|
|
if (m_expirationTime != 0_s) {
|
|
|
|
|
m_impl->m_watchdogs.remove(this);
|
|
|
|
|
m_expirationTime = 0_s;
|
|
|
|
|
m_impl->UpdateAlarm();
|
|
|
|
|
}
|
2018-12-01 00:05:33 -08:00
|
|
|
}
|
|
|
|
|
|
2018-12-29 16:22:54 -08:00
|
|
|
void Watchdog::SuppressTimeoutMessage(bool suppress) {
|
|
|
|
|
m_suppressTimeoutMessage = suppress;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-03 08:36:51 -07:00
|
|
|
bool Watchdog::operator>(const Watchdog& rhs) const {
|
2018-12-01 00:05:33 -08:00
|
|
|
return m_expirationTime > rhs.m_expirationTime;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-21 22:58:16 -07:00
|
|
|
Watchdog::Impl* Watchdog::GetImpl() {
|
|
|
|
|
static Impl inst;
|
|
|
|
|
return &inst;
|
2018-06-24 02:29:21 -05:00
|
|
|
}
|