blob: 854f9e9d393b4dc5394613ff4e897d5700b9294e [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
Brian Silverman8fce7482020-01-05 13:18:21 -08004
5#include "frc/Watchdog.h"
6
Austin Schuh1e69f942020-11-14 15:06:14 -08007#include <atomic>
Austin Schuh812d0d12021-11-04 20:16:48 -07008#include <thread>
9#include <utility>
Austin Schuh1e69f942020-11-14 15:06:14 -080010
Austin Schuh812d0d12021-11-04 20:16:48 -070011#include <fmt/format.h>
Austin Schuh1e69f942020-11-14 15:06:14 -080012#include <hal/Notifier.h>
Austin Schuh812d0d12021-11-04 20:16:48 -070013#include <wpi/mutex.h>
Austin Schuh1e69f942020-11-14 15:06:14 -080014#include <wpi/priority_queue.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080015
Austin Schuh812d0d12021-11-04 20:16:48 -070016#include "frc/Errors.h"
17#include "frc/Timer.h"
Austin Schuh1e69f942020-11-14 15:06:14 -080018
Brian Silverman8fce7482020-01-05 13:18:21 -080019using namespace frc;
20
Austin Schuh1e69f942020-11-14 15:06:14 -080021class Watchdog::Impl {
Brian Silverman8fce7482020-01-05 13:18:21 -080022 public:
Austin Schuh1e69f942020-11-14 15:06:14 -080023 Impl();
24 ~Impl();
25
Brian Silverman8fce7482020-01-05 13:18:21 -080026 template <typename T>
27 struct DerefGreater {
28 constexpr bool operator()(const T& lhs, const T& rhs) const {
29 return *lhs > *rhs;
30 }
31 };
32
Austin Schuh1e69f942020-11-14 15:06:14 -080033 wpi::mutex m_mutex;
34 std::atomic<HAL_NotifierHandle> m_notifier;
35 wpi::priority_queue<Watchdog*, std::vector<Watchdog*>,
36 DerefGreater<Watchdog*>>
Brian Silverman8fce7482020-01-05 13:18:21 -080037 m_watchdogs;
38
Austin Schuh1e69f942020-11-14 15:06:14 -080039 void UpdateAlarm();
40
Brian Silverman8fce7482020-01-05 13:18:21 -080041 private:
Austin Schuh1e69f942020-11-14 15:06:14 -080042 void Main();
43
44 std::thread m_thread;
Brian Silverman8fce7482020-01-05 13:18:21 -080045};
46
Austin Schuh1e69f942020-11-14 15:06:14 -080047Watchdog::Impl::Impl() {
48 int32_t status = 0;
49 m_notifier = HAL_InitializeNotifier(&status);
Austin Schuh812d0d12021-11-04 20:16:48 -070050 FRC_CheckErrorStatus(status, "{}", "starting watchdog notifier");
Austin Schuh1e69f942020-11-14 15:06:14 -080051 HAL_SetNotifierName(m_notifier, "Watchdog", &status);
Brian Silverman8fce7482020-01-05 13:18:21 -080052
Austin Schuh1e69f942020-11-14 15:06:14 -080053 m_thread = std::thread([=] { Main(); });
54}
Brian Silverman8fce7482020-01-05 13:18:21 -080055
Austin Schuh1e69f942020-11-14 15:06:14 -080056Watchdog::Impl::~Impl() {
57 int32_t status = 0;
58 // atomically set handle to 0, then clean
59 HAL_NotifierHandle handle = m_notifier.exchange(0);
60 HAL_StopNotifier(handle, &status);
Austin Schuh812d0d12021-11-04 20:16:48 -070061 FRC_ReportError(status, "{}", "stopping watchdog notifier");
Brian Silverman8fce7482020-01-05 13:18:21 -080062
Austin Schuh1e69f942020-11-14 15:06:14 -080063 // Join the thread to ensure the handler has exited.
Austin Schuh812d0d12021-11-04 20:16:48 -070064 if (m_thread.joinable()) {
65 m_thread.join();
66 }
Brian Silverman8fce7482020-01-05 13:18:21 -080067
Austin Schuh1e69f942020-11-14 15:06:14 -080068 HAL_CleanNotifier(handle, &status);
69}
Brian Silverman8fce7482020-01-05 13:18:21 -080070
Austin Schuh1e69f942020-11-14 15:06:14 -080071void Watchdog::Impl::UpdateAlarm() {
72 int32_t status = 0;
73 // Return if we are being destructed, or were not created successfully
74 auto notifier = m_notifier.load();
Austin Schuh812d0d12021-11-04 20:16:48 -070075 if (notifier == 0) {
76 return;
77 }
78 if (m_watchdogs.empty()) {
Austin Schuh1e69f942020-11-14 15:06:14 -080079 HAL_CancelNotifierAlarm(notifier, &status);
Austin Schuh812d0d12021-11-04 20:16:48 -070080 } else {
Austin Schuh1e69f942020-11-14 15:06:14 -080081 HAL_UpdateNotifierAlarm(
82 notifier,
Austin Schuh812d0d12021-11-04 20:16:48 -070083 static_cast<uint64_t>(m_watchdogs.top()->m_expirationTime.value() *
Austin Schuh1e69f942020-11-14 15:06:14 -080084 1e6),
85 &status);
Austin Schuh812d0d12021-11-04 20:16:48 -070086 }
87 FRC_CheckErrorStatus(status, "{}", "updating watchdog notifier alarm");
Austin Schuh1e69f942020-11-14 15:06:14 -080088}
89
90void Watchdog::Impl::Main() {
91 for (;;) {
92 int32_t status = 0;
93 HAL_NotifierHandle notifier = m_notifier.load();
Austin Schuh812d0d12021-11-04 20:16:48 -070094 if (notifier == 0) {
95 break;
96 }
Austin Schuh1e69f942020-11-14 15:06:14 -080097 uint64_t curTime = HAL_WaitForNotifierAlarm(notifier, &status);
Austin Schuh812d0d12021-11-04 20:16:48 -070098 if (curTime == 0 || status != 0) {
99 break;
100 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800101
102 std::unique_lock lock(m_mutex);
103
Austin Schuh812d0d12021-11-04 20:16:48 -0700104 if (m_watchdogs.empty()) {
105 continue;
106 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800107
108 // If the condition variable timed out, that means a Watchdog timeout
109 // has occurred, so call its timeout function.
110 auto watchdog = m_watchdogs.pop();
111
112 units::second_t now{curTime * 1e-6};
113 if (now - watchdog->m_lastTimeoutPrintTime > kMinPrintPeriod) {
114 watchdog->m_lastTimeoutPrintTime = now;
115 if (!watchdog->m_suppressTimeoutMessage) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700116 FRC_ReportError(warn::Warning, "Watchdog not fed within {:.6f}s",
117 watchdog->m_timeout.value());
Brian Silverman8fce7482020-01-05 13:18:21 -0800118 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800119 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800120
121 // Set expiration flag before calling the callback so any manipulation
122 // of the flag in the callback (e.g., calling Disable()) isn't
123 // clobbered.
124 watchdog->m_isExpired = true;
125
126 lock.unlock();
127 watchdog->m_callback();
128 lock.lock();
129
130 UpdateAlarm();
Brian Silverman8fce7482020-01-05 13:18:21 -0800131 }
132}
133
Brian Silverman8fce7482020-01-05 13:18:21 -0800134Watchdog::Watchdog(units::second_t timeout, std::function<void()> callback)
Austin Schuh812d0d12021-11-04 20:16:48 -0700135 : m_timeout(timeout), m_callback(std::move(callback)), m_impl(GetImpl()) {}
Brian Silverman8fce7482020-01-05 13:18:21 -0800136
Austin Schuh812d0d12021-11-04 20:16:48 -0700137Watchdog::~Watchdog() {
138 try {
139 Disable();
140 } catch (const RuntimeError& e) {
141 e.Report();
142 }
143}
Brian Silverman8fce7482020-01-05 13:18:21 -0800144
Austin Schuh812d0d12021-11-04 20:16:48 -0700145Watchdog::Watchdog(Watchdog&& rhs) {
146 *this = std::move(rhs);
147}
Austin Schuh1e69f942020-11-14 15:06:14 -0800148
149Watchdog& Watchdog::operator=(Watchdog&& rhs) {
150 m_impl = rhs.m_impl;
151 std::scoped_lock lock(m_impl->m_mutex);
152 m_startTime = rhs.m_startTime;
153 m_timeout = rhs.m_timeout;
154 m_expirationTime = rhs.m_expirationTime;
155 m_callback = std::move(rhs.m_callback);
156 m_lastTimeoutPrintTime = rhs.m_lastTimeoutPrintTime;
157 m_suppressTimeoutMessage = rhs.m_suppressTimeoutMessage;
158 m_tracer = std::move(rhs.m_tracer);
159 m_isExpired = rhs.m_isExpired;
160 if (m_expirationTime != 0_s) {
161 m_impl->m_watchdogs.remove(&rhs);
162 m_impl->m_watchdogs.emplace(this);
163 }
164 return *this;
165}
166
Austin Schuh812d0d12021-11-04 20:16:48 -0700167units::second_t Watchdog::GetTime() const {
168 return Timer::GetFPGATimestamp() - m_startTime;
Brian Silverman8fce7482020-01-05 13:18:21 -0800169}
170
171void Watchdog::SetTimeout(units::second_t timeout) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700172 m_startTime = Timer::GetFPGATimestamp();
Austin Schuh1e69f942020-11-14 15:06:14 -0800173 m_tracer.ClearEpochs();
Brian Silverman8fce7482020-01-05 13:18:21 -0800174
Austin Schuh1e69f942020-11-14 15:06:14 -0800175 std::scoped_lock lock(m_impl->m_mutex);
Brian Silverman8fce7482020-01-05 13:18:21 -0800176 m_timeout = timeout;
177 m_isExpired = false;
178
Austin Schuh1e69f942020-11-14 15:06:14 -0800179 m_impl->m_watchdogs.remove(this);
180 m_expirationTime = m_startTime + m_timeout;
181 m_impl->m_watchdogs.emplace(this);
182 m_impl->UpdateAlarm();
Brian Silverman8fce7482020-01-05 13:18:21 -0800183}
184
Austin Schuh812d0d12021-11-04 20:16:48 -0700185units::second_t Watchdog::GetTimeout() const {
Austin Schuh1e69f942020-11-14 15:06:14 -0800186 std::scoped_lock lock(m_impl->m_mutex);
Austin Schuh812d0d12021-11-04 20:16:48 -0700187 return m_timeout;
Brian Silverman8fce7482020-01-05 13:18:21 -0800188}
189
190bool Watchdog::IsExpired() const {
Austin Schuh1e69f942020-11-14 15:06:14 -0800191 std::scoped_lock lock(m_impl->m_mutex);
Brian Silverman8fce7482020-01-05 13:18:21 -0800192 return m_isExpired;
193}
194
Austin Schuh812d0d12021-11-04 20:16:48 -0700195void Watchdog::AddEpoch(std::string_view epochName) {
Austin Schuh1e69f942020-11-14 15:06:14 -0800196 m_tracer.AddEpoch(epochName);
Brian Silverman8fce7482020-01-05 13:18:21 -0800197}
198
Austin Schuh812d0d12021-11-04 20:16:48 -0700199void Watchdog::PrintEpochs() {
200 m_tracer.PrintEpochs();
201}
Brian Silverman8fce7482020-01-05 13:18:21 -0800202
Austin Schuh812d0d12021-11-04 20:16:48 -0700203void Watchdog::Reset() {
204 Enable();
205}
Brian Silverman8fce7482020-01-05 13:18:21 -0800206
207void Watchdog::Enable() {
Austin Schuh812d0d12021-11-04 20:16:48 -0700208 m_startTime = Timer::GetFPGATimestamp();
Austin Schuh1e69f942020-11-14 15:06:14 -0800209 m_tracer.ClearEpochs();
Brian Silverman8fce7482020-01-05 13:18:21 -0800210
Austin Schuh1e69f942020-11-14 15:06:14 -0800211 std::scoped_lock lock(m_impl->m_mutex);
Brian Silverman8fce7482020-01-05 13:18:21 -0800212 m_isExpired = false;
213
Austin Schuh1e69f942020-11-14 15:06:14 -0800214 m_impl->m_watchdogs.remove(this);
215 m_expirationTime = m_startTime + m_timeout;
216 m_impl->m_watchdogs.emplace(this);
217 m_impl->UpdateAlarm();
Brian Silverman8fce7482020-01-05 13:18:21 -0800218}
219
220void Watchdog::Disable() {
Austin Schuh1e69f942020-11-14 15:06:14 -0800221 std::scoped_lock lock(m_impl->m_mutex);
Brian Silverman8fce7482020-01-05 13:18:21 -0800222
Austin Schuh1e69f942020-11-14 15:06:14 -0800223 if (m_expirationTime != 0_s) {
224 m_impl->m_watchdogs.remove(this);
225 m_expirationTime = 0_s;
226 m_impl->UpdateAlarm();
227 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800228}
229
230void Watchdog::SuppressTimeoutMessage(bool suppress) {
231 m_suppressTimeoutMessage = suppress;
232}
233
Austin Schuh1e69f942020-11-14 15:06:14 -0800234bool Watchdog::operator>(const Watchdog& rhs) const {
Brian Silverman8fce7482020-01-05 13:18:21 -0800235 return m_expirationTime > rhs.m_expirationTime;
236}
237
Austin Schuh1e69f942020-11-14 15:06:14 -0800238Watchdog::Impl* Watchdog::GetImpl() {
239 static Impl inst;
240 return &inst;
Brian Silverman8fce7482020-01-05 13:18:21 -0800241}