blob: e5be4fae7685972af7fa3e72ff4de3f40974b576 [file] [log] [blame]
Brian Silverman41cdd3e2019-01-19 19:48:58 -08001/*----------------------------------------------------------------------------*/
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -08002/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
Brian Silverman41cdd3e2019-01-19 19:48:58 -08003/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "frc/Watchdog.h"
9
10#include <wpi/Format.h>
11#include <wpi/PriorityQueue.h>
12#include <wpi/raw_ostream.h>
13
14using namespace frc;
15
16constexpr std::chrono::milliseconds Watchdog::kMinPrintPeriod;
17
18class Watchdog::Thread : public wpi::SafeThread {
19 public:
20 template <typename T>
Brian Silverman60246092019-03-02 13:29:58 -080021 struct DerefGreater {
Brian Silverman41cdd3e2019-01-19 19:48:58 -080022 constexpr bool operator()(const T& lhs, const T& rhs) const {
23 return *lhs > *rhs;
24 }
25 };
26
27 wpi::PriorityQueue<Watchdog*, std::vector<Watchdog*>, DerefGreater<Watchdog*>>
28 m_watchdogs;
29
30 private:
31 void Main() override;
32};
33
34void Watchdog::Thread::Main() {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080035 std::unique_lock lock(m_mutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -080036
37 while (m_active) {
38 if (m_watchdogs.size() > 0) {
39 if (m_cond.wait_until(lock, m_watchdogs.top()->m_expirationTime) ==
40 std::cv_status::timeout) {
41 if (m_watchdogs.size() == 0 ||
42 m_watchdogs.top()->m_expirationTime > hal::fpga_clock::now()) {
43 continue;
44 }
45
46 // If the condition variable timed out, that means a Watchdog timeout
47 // has occurred, so call its timeout function.
48 auto watchdog = m_watchdogs.top();
49 m_watchdogs.pop();
50
51 auto now = hal::fpga_clock::now();
52 if (now - watchdog->m_lastTimeoutPrintTime > kMinPrintPeriod) {
53 watchdog->m_lastTimeoutPrintTime = now;
54 if (!watchdog->m_suppressTimeoutMessage) {
55 wpi::outs() << "Watchdog not fed within "
56 << wpi::format("%.6f",
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080057 watchdog->m_timeout.count() / 1.0e9)
Brian Silverman41cdd3e2019-01-19 19:48:58 -080058 << "s\n";
59 }
60 }
61
62 // Set expiration flag before calling the callback so any manipulation
63 // of the flag in the callback (e.g., calling Disable()) isn't
64 // clobbered.
65 watchdog->m_isExpired = true;
66
67 lock.unlock();
68 watchdog->m_callback();
69 lock.lock();
70 }
71 // Otherwise, a Watchdog removed itself from the queue (it notifies the
72 // scheduler of this) or a spurious wakeup occurred, so just rewait with
73 // the soonest watchdog timeout.
74 } else {
75 m_cond.wait(lock, [&] { return m_watchdogs.size() > 0 || !m_active; });
76 }
77 }
78}
79
80Watchdog::Watchdog(double timeout, std::function<void()> callback)
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080081 : Watchdog(units::second_t{timeout}, callback) {}
82
83Watchdog::Watchdog(units::second_t timeout, std::function<void()> callback)
84 : m_timeout(timeout), m_callback(callback), m_owner(&GetThreadOwner()) {}
Brian Silverman41cdd3e2019-01-19 19:48:58 -080085
86Watchdog::~Watchdog() { Disable(); }
87
88double Watchdog::GetTime() const {
89 return (hal::fpga_clock::now() - m_startTime).count() / 1.0e6;
90}
91
92void Watchdog::SetTimeout(double timeout) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080093 SetTimeout(units::second_t{timeout});
94}
95
96void Watchdog::SetTimeout(units::second_t timeout) {
97 using std::chrono::duration_cast;
98 using std::chrono::microseconds;
99
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800100 m_startTime = hal::fpga_clock::now();
101 m_epochs.clear();
102
103 // Locks mutex
104 auto thr = m_owner->GetThread();
105 if (!thr) return;
106
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800107 m_timeout = timeout;
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800108 m_isExpired = false;
109
110 thr->m_watchdogs.remove(this);
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800111 m_expirationTime = m_startTime + duration_cast<microseconds>(m_timeout);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800112 thr->m_watchdogs.emplace(this);
113 thr->m_cond.notify_all();
114}
115
116double Watchdog::GetTimeout() const {
117 // Locks mutex
118 auto thr = m_owner->GetThread();
119
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800120 return m_timeout.count() / 1.0e9;
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800121}
122
123bool Watchdog::IsExpired() const {
124 // Locks mutex
125 auto thr = m_owner->GetThread();
126
127 return m_isExpired;
128}
129
130void Watchdog::AddEpoch(wpi::StringRef epochName) {
131 auto currentTime = hal::fpga_clock::now();
132 m_epochs[epochName] = currentTime - m_startTime;
133 m_startTime = currentTime;
134}
135
136void Watchdog::PrintEpochs() {
137 auto now = hal::fpga_clock::now();
138 if (now - m_lastEpochsPrintTime > kMinPrintPeriod) {
139 m_lastEpochsPrintTime = now;
140 for (const auto& epoch : m_epochs) {
141 wpi::outs() << '\t' << epoch.getKey() << ": "
142 << wpi::format("%.6f", epoch.getValue().count() / 1.0e6)
143 << "s\n";
144 }
145 }
146}
147
148void Watchdog::Reset() { Enable(); }
149
150void Watchdog::Enable() {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800151 using std::chrono::duration_cast;
152 using std::chrono::microseconds;
153
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800154 m_startTime = hal::fpga_clock::now();
155 m_epochs.clear();
156
157 // Locks mutex
158 auto thr = m_owner->GetThread();
159 if (!thr) return;
160
161 m_isExpired = false;
162
163 thr->m_watchdogs.remove(this);
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800164 m_expirationTime = m_startTime + duration_cast<microseconds>(m_timeout);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800165 thr->m_watchdogs.emplace(this);
166 thr->m_cond.notify_all();
167}
168
169void Watchdog::Disable() {
170 // Locks mutex
171 auto thr = m_owner->GetThread();
172 if (!thr) return;
173
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800174 thr->m_watchdogs.remove(this);
175 thr->m_cond.notify_all();
176}
177
178void Watchdog::SuppressTimeoutMessage(bool suppress) {
179 m_suppressTimeoutMessage = suppress;
180}
181
182bool Watchdog::operator>(const Watchdog& rhs) {
183 return m_expirationTime > rhs.m_expirationTime;
184}
185
186wpi::SafeThreadOwner<Watchdog::Thread>& Watchdog::GetThreadOwner() {
187 static wpi::SafeThreadOwner<Thread> inst = [] {
188 wpi::SafeThreadOwner<Watchdog::Thread> inst;
189 inst.Start();
190 return inst;
191 }();
192 return inst;
193}