2018-06-24 02:29:21 -05:00
|
|
|
/*----------------------------------------------------------------------------*/
|
2020-03-15 19:56:08 -07:00
|
|
|
/* Copyright (c) 2018-2020 FIRST. All Rights Reserved. */
|
2018-06-24 02:29:21 -05:00
|
|
|
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
|
|
|
|
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
|
|
|
/* the project. */
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
2018-07-20 00:03:45 -07:00
|
|
|
#include "frc/Watchdog.h"
|
2018-06-24 02:29:21 -05:00
|
|
|
|
2018-12-01 00:05:33 -08:00
|
|
|
#include <wpi/Format.h>
|
|
|
|
|
#include <wpi/PriorityQueue.h>
|
2020-05-21 06:57:06 +03:00
|
|
|
#include <wpi/SmallString.h>
|
2018-06-24 02:29:21 -05:00
|
|
|
#include <wpi/raw_ostream.h>
|
|
|
|
|
|
2020-05-21 06:57:06 +03:00
|
|
|
#include "frc/DriverStation.h"
|
|
|
|
|
|
2018-06-24 02:29:21 -05:00
|
|
|
using namespace frc;
|
|
|
|
|
|
2018-12-07 19:39:02 -08:00
|
|
|
constexpr std::chrono::milliseconds Watchdog::kMinPrintPeriod;
|
|
|
|
|
|
2018-12-01 00:05:33 -08:00
|
|
|
class Watchdog::Thread : public wpi::SafeThread {
|
|
|
|
|
public:
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
wpi::PriorityQueue<Watchdog*, std::vector<Watchdog*>, DerefGreater<Watchdog*>>
|
|
|
|
|
m_watchdogs;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void Main() override;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void Watchdog::Thread::Main() {
|
2019-07-07 19:17:14 -07:00
|
|
|
std::unique_lock lock(m_mutex);
|
2018-12-01 00:05:33 -08:00
|
|
|
|
|
|
|
|
while (m_active) {
|
|
|
|
|
if (m_watchdogs.size() > 0) {
|
|
|
|
|
if (m_cond.wait_until(lock, m_watchdogs.top()->m_expirationTime) ==
|
|
|
|
|
std::cv_status::timeout) {
|
|
|
|
|
if (m_watchdogs.size() == 0 ||
|
|
|
|
|
m_watchdogs.top()->m_expirationTime > hal::fpga_clock::now()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the condition variable timed out, that means a Watchdog timeout
|
|
|
|
|
// has occurred, so call its timeout function.
|
|
|
|
|
auto watchdog = m_watchdogs.top();
|
|
|
|
|
m_watchdogs.pop();
|
|
|
|
|
|
2018-12-07 19:39:02 -08:00
|
|
|
auto now = hal::fpga_clock::now();
|
|
|
|
|
if (now - watchdog->m_lastTimeoutPrintTime > kMinPrintPeriod) {
|
|
|
|
|
watchdog->m_lastTimeoutPrintTime = now;
|
2018-12-29 16:22:54 -08:00
|
|
|
if (!watchdog->m_suppressTimeoutMessage) {
|
2020-05-21 06:57:06 +03:00
|
|
|
wpi::SmallString<128> buf;
|
|
|
|
|
wpi::raw_svector_ostream err(buf);
|
|
|
|
|
err << "Watchdog not fed within "
|
|
|
|
|
<< wpi::format("%.6f", watchdog->m_timeout.count() / 1.0e9)
|
|
|
|
|
<< "s\n";
|
|
|
|
|
frc::DriverStation::ReportWarning(err.str());
|
2018-12-29 16:22:54 -08:00
|
|
|
}
|
2018-12-07 19:39:02 -08:00
|
|
|
}
|
2019-01-08 21:37:59 -06: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;
|
|
|
|
|
|
2018-12-01 00:05:33 -08:00
|
|
|
lock.unlock();
|
|
|
|
|
watchdog->m_callback();
|
|
|
|
|
lock.lock();
|
|
|
|
|
}
|
|
|
|
|
// Otherwise, a Watchdog removed itself from the queue (it notifies the
|
|
|
|
|
// scheduler of this) or a spurious wakeup occurred, so just rewait with
|
|
|
|
|
// the soonest watchdog timeout.
|
|
|
|
|
} else {
|
|
|
|
|
m_cond.wait(lock, [&] { return m_watchdogs.size() > 0 || !m_active; });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-24 02:29:21 -05:00
|
|
|
Watchdog::Watchdog(double timeout, std::function<void()> callback)
|
2019-09-03 15:58:31 -07:00
|
|
|
: Watchdog(units::second_t{timeout}, callback) {}
|
|
|
|
|
|
|
|
|
|
Watchdog::Watchdog(units::second_t timeout, std::function<void()> callback)
|
|
|
|
|
: m_timeout(timeout), m_callback(callback), m_owner(&GetThreadOwner()) {}
|
2018-12-01 00:05:33 -08:00
|
|
|
|
|
|
|
|
Watchdog::~Watchdog() { Disable(); }
|
2018-06-24 02:29:21 -05:00
|
|
|
|
|
|
|
|
double Watchdog::GetTime() const {
|
2018-12-01 00:05:33 -08:00
|
|
|
return (hal::fpga_clock::now() - m_startTime).count() / 1.0e6;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Watchdog::SetTimeout(double timeout) {
|
2019-09-03 15:58:31 -07:00
|
|
|
SetTimeout(units::second_t{timeout});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Watchdog::SetTimeout(units::second_t timeout) {
|
|
|
|
|
using std::chrono::duration_cast;
|
|
|
|
|
using std::chrono::microseconds;
|
|
|
|
|
|
2018-12-01 00:05:33 -08:00
|
|
|
m_startTime = hal::fpga_clock::now();
|
2020-04-01 23:10:28 -04:00
|
|
|
m_tracer.ClearEpochs();
|
2018-12-01 00:05:33 -08:00
|
|
|
|
|
|
|
|
// Locks mutex
|
|
|
|
|
auto thr = m_owner->GetThread();
|
|
|
|
|
if (!thr) return;
|
|
|
|
|
|
2019-09-03 15:58:31 -07:00
|
|
|
m_timeout = timeout;
|
2018-12-01 00:05:33 -08:00
|
|
|
m_isExpired = false;
|
|
|
|
|
|
|
|
|
|
thr->m_watchdogs.remove(this);
|
2019-09-03 15:58:31 -07:00
|
|
|
m_expirationTime = m_startTime + duration_cast<microseconds>(m_timeout);
|
2018-12-01 00:05:33 -08:00
|
|
|
thr->m_watchdogs.emplace(this);
|
|
|
|
|
thr->m_cond.notify_all();
|
2018-06-24 02:29:21 -05:00
|
|
|
}
|
|
|
|
|
|
2018-12-01 00:05:33 -08:00
|
|
|
double Watchdog::GetTimeout() const {
|
|
|
|
|
// Locks mutex
|
|
|
|
|
auto thr = m_owner->GetThread();
|
|
|
|
|
|
2019-09-03 15:58:31 -07:00
|
|
|
return m_timeout.count() / 1.0e9;
|
2018-12-01 00:05:33 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Watchdog::IsExpired() const {
|
|
|
|
|
// Locks mutex
|
|
|
|
|
auto thr = m_owner->GetThread();
|
|
|
|
|
|
|
|
|
|
return m_isExpired;
|
|
|
|
|
}
|
2018-06-24 02:29:21 -05:00
|
|
|
|
|
|
|
|
void Watchdog::AddEpoch(wpi::StringRef epochName) {
|
2020-04-01 23:10:28 -04:00
|
|
|
m_tracer.AddEpoch(epochName);
|
2018-06-24 02:29:21 -05:00
|
|
|
}
|
|
|
|
|
|
2020-04-01 23:10:28 -04:00
|
|
|
void Watchdog::PrintEpochs() { m_tracer.PrintEpochs(); }
|
2018-06-24 02:29:21 -05:00
|
|
|
|
|
|
|
|
void Watchdog::Reset() { Enable(); }
|
|
|
|
|
|
|
|
|
|
void Watchdog::Enable() {
|
2019-09-03 15:58:31 -07:00
|
|
|
using std::chrono::duration_cast;
|
|
|
|
|
using std::chrono::microseconds;
|
|
|
|
|
|
2018-12-01 00:05:33 -08:00
|
|
|
m_startTime = hal::fpga_clock::now();
|
2020-04-01 23:10:28 -04:00
|
|
|
m_tracer.ClearEpochs();
|
2018-12-01 00:05:33 -08:00
|
|
|
|
|
|
|
|
// Locks mutex
|
|
|
|
|
auto thr = m_owner->GetThread();
|
|
|
|
|
if (!thr) return;
|
|
|
|
|
|
|
|
|
|
m_isExpired = false;
|
|
|
|
|
|
|
|
|
|
thr->m_watchdogs.remove(this);
|
2019-09-03 15:58:31 -07:00
|
|
|
m_expirationTime = m_startTime + duration_cast<microseconds>(m_timeout);
|
2018-12-01 00:05:33 -08:00
|
|
|
thr->m_watchdogs.emplace(this);
|
|
|
|
|
thr->m_cond.notify_all();
|
2018-06-24 02:29:21 -05:00
|
|
|
}
|
|
|
|
|
|
2018-12-01 00:05:33 -08:00
|
|
|
void Watchdog::Disable() {
|
|
|
|
|
// Locks mutex
|
|
|
|
|
auto thr = m_owner->GetThread();
|
|
|
|
|
if (!thr) return;
|
2018-06-24 02:29:21 -05:00
|
|
|
|
2018-12-01 00:05:33 -08:00
|
|
|
thr->m_watchdogs.remove(this);
|
|
|
|
|
thr->m_cond.notify_all();
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-29 16:22:54 -08:00
|
|
|
void Watchdog::SuppressTimeoutMessage(bool suppress) {
|
|
|
|
|
m_suppressTimeoutMessage = suppress;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-01 00:05:33 -08:00
|
|
|
bool Watchdog::operator>(const Watchdog& rhs) {
|
|
|
|
|
return m_expirationTime > rhs.m_expirationTime;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wpi::SafeThreadOwner<Watchdog::Thread>& Watchdog::GetThreadOwner() {
|
|
|
|
|
static wpi::SafeThreadOwner<Thread> inst = [] {
|
|
|
|
|
wpi::SafeThreadOwner<Watchdog::Thread> inst;
|
|
|
|
|
inst.Start();
|
|
|
|
|
return inst;
|
|
|
|
|
}();
|
|
|
|
|
return inst;
|
2018-06-24 02:29:21 -05:00
|
|
|
}
|