blob: 147beb400354215f61bd09f62d7b1f5979c49342 [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/Notifier.h"
6
7#include <utility>
8
Austin Schuh812d0d12021-11-04 20:16:48 -07009#include <fmt/format.h>
James Kuszmaulcf324122023-01-14 14:07:17 -080010#include <hal/DriverStation.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080011#include <hal/FRCUsageReporting.h>
12#include <hal/Notifier.h>
Austin Schuh1e69f942020-11-14 15:06:14 -080013#include <hal/Threads.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080014
Austin Schuh812d0d12021-11-04 20:16:48 -070015#include "frc/Errors.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080016#include "frc/Timer.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080017
18using namespace frc;
19
James Kuszmaulb13e13f2023-11-22 20:44:04 -080020Notifier::Notifier(std::function<void()> callback) {
21 if (!callback) {
22 throw FRC_MakeError(err::NullParameter, "callback");
Austin Schuh812d0d12021-11-04 20:16:48 -070023 }
James Kuszmaulb13e13f2023-11-22 20:44:04 -080024 m_callback = callback;
Brian Silverman8fce7482020-01-05 13:18:21 -080025 int32_t status = 0;
26 m_notifier = HAL_InitializeNotifier(&status);
James Kuszmaulcf324122023-01-14 14:07:17 -080027 FRC_CheckErrorStatus(status, "InitializeNotifier");
Brian Silverman8fce7482020-01-05 13:18:21 -080028
James Kuszmaulcf324122023-01-14 14:07:17 -080029 m_thread = std::thread([=, this] {
Brian Silverman8fce7482020-01-05 13:18:21 -080030 for (;;) {
31 int32_t status = 0;
32 HAL_NotifierHandle notifier = m_notifier.load();
Austin Schuh812d0d12021-11-04 20:16:48 -070033 if (notifier == 0) {
34 break;
35 }
Brian Silverman8fce7482020-01-05 13:18:21 -080036 uint64_t curTime = HAL_WaitForNotifierAlarm(notifier, &status);
Austin Schuh812d0d12021-11-04 20:16:48 -070037 if (curTime == 0 || status != 0) {
38 break;
39 }
Brian Silverman8fce7482020-01-05 13:18:21 -080040
James Kuszmaulb13e13f2023-11-22 20:44:04 -080041 std::function<void()> callback;
Brian Silverman8fce7482020-01-05 13:18:21 -080042 {
43 std::scoped_lock lock(m_processMutex);
James Kuszmaulb13e13f2023-11-22 20:44:04 -080044 callback = m_callback;
Brian Silverman8fce7482020-01-05 13:18:21 -080045 if (m_periodic) {
46 m_expirationTime += m_period;
47 UpdateAlarm();
48 } else {
James Kuszmaulb13e13f2023-11-22 20:44:04 -080049 // Need to update the alarm to cause it to wait again
Brian Silverman8fce7482020-01-05 13:18:21 -080050 UpdateAlarm(UINT64_MAX);
51 }
52 }
53
James Kuszmaulb13e13f2023-11-22 20:44:04 -080054 // Call callback
55 if (callback) {
56 callback();
Austin Schuh812d0d12021-11-04 20:16:48 -070057 }
Brian Silverman8fce7482020-01-05 13:18:21 -080058 }
59 });
60}
61
James Kuszmaulb13e13f2023-11-22 20:44:04 -080062Notifier::Notifier(int priority, std::function<void()> callback) {
63 if (!callback) {
64 throw FRC_MakeError(err::NullParameter, "callback");
Austin Schuh812d0d12021-11-04 20:16:48 -070065 }
James Kuszmaulb13e13f2023-11-22 20:44:04 -080066 m_callback = callback;
Austin Schuh1e69f942020-11-14 15:06:14 -080067 int32_t status = 0;
68 m_notifier = HAL_InitializeNotifier(&status);
James Kuszmaulcf324122023-01-14 14:07:17 -080069 FRC_CheckErrorStatus(status, "InitializeNotifier");
Austin Schuh1e69f942020-11-14 15:06:14 -080070
James Kuszmaulcf324122023-01-14 14:07:17 -080071 m_thread = std::thread([=, this] {
Austin Schuh1e69f942020-11-14 15:06:14 -080072 int32_t status = 0;
73 HAL_SetCurrentThreadPriority(true, priority, &status);
74 for (;;) {
75 HAL_NotifierHandle notifier = m_notifier.load();
Austin Schuh812d0d12021-11-04 20:16:48 -070076 if (notifier == 0) {
77 break;
78 }
Austin Schuh1e69f942020-11-14 15:06:14 -080079 uint64_t curTime = HAL_WaitForNotifierAlarm(notifier, &status);
Austin Schuh812d0d12021-11-04 20:16:48 -070080 if (curTime == 0 || status != 0) {
81 break;
82 }
Austin Schuh1e69f942020-11-14 15:06:14 -080083
James Kuszmaulb13e13f2023-11-22 20:44:04 -080084 std::function<void()> callback;
Austin Schuh1e69f942020-11-14 15:06:14 -080085 {
86 std::scoped_lock lock(m_processMutex);
James Kuszmaulb13e13f2023-11-22 20:44:04 -080087 callback = m_callback;
Austin Schuh1e69f942020-11-14 15:06:14 -080088 if (m_periodic) {
89 m_expirationTime += m_period;
90 UpdateAlarm();
91 } else {
92 // need to update the alarm to cause it to wait again
93 UpdateAlarm(UINT64_MAX);
94 }
95 }
96
97 // call callback
James Kuszmaulb13e13f2023-11-22 20:44:04 -080098 if (callback) {
James Kuszmaulcf324122023-01-14 14:07:17 -080099 try {
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800100 callback();
James Kuszmaulcf324122023-01-14 14:07:17 -0800101 } catch (const frc::RuntimeError& e) {
102 e.Report();
103 FRC_ReportError(
104 err::Error,
105 "Error in Notifier thread."
106 " The above stacktrace can help determine where the error "
107 "occurred.\n"
108 " See https://wpilib.org/stacktrace for more information.\n");
109 throw;
110 } catch (const std::exception& e) {
111 HAL_SendError(1, err::Error, 0, e.what(), "", "", 1);
112 throw;
113 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700114 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800115 }
116 });
117}
118
Brian Silverman8fce7482020-01-05 13:18:21 -0800119Notifier::~Notifier() {
120 int32_t status = 0;
121 // atomically set handle to 0, then clean
122 HAL_NotifierHandle handle = m_notifier.exchange(0);
123 HAL_StopNotifier(handle, &status);
James Kuszmaulcf324122023-01-14 14:07:17 -0800124 FRC_ReportError(status, "StopNotifier");
Brian Silverman8fce7482020-01-05 13:18:21 -0800125
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800126 // Join the thread to ensure the callback has exited.
Austin Schuh812d0d12021-11-04 20:16:48 -0700127 if (m_thread.joinable()) {
128 m_thread.join();
129 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800130
131 HAL_CleanNotifier(handle, &status);
132}
133
134Notifier::Notifier(Notifier&& rhs)
Austin Schuh812d0d12021-11-04 20:16:48 -0700135 : m_thread(std::move(rhs.m_thread)),
Brian Silverman8fce7482020-01-05 13:18:21 -0800136 m_notifier(rhs.m_notifier.load()),
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800137 m_callback(std::move(rhs.m_callback)),
Brian Silverman8fce7482020-01-05 13:18:21 -0800138 m_expirationTime(std::move(rhs.m_expirationTime)),
139 m_period(std::move(rhs.m_period)),
140 m_periodic(std::move(rhs.m_periodic)) {
141 rhs.m_notifier = HAL_kInvalidHandle;
142}
143
144Notifier& Notifier::operator=(Notifier&& rhs) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800145 m_thread = std::move(rhs.m_thread);
146 m_notifier = rhs.m_notifier.load();
147 rhs.m_notifier = HAL_kInvalidHandle;
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800148 m_callback = std::move(rhs.m_callback);
Brian Silverman8fce7482020-01-05 13:18:21 -0800149 m_expirationTime = std::move(rhs.m_expirationTime);
150 m_period = std::move(rhs.m_period);
151 m_periodic = std::move(rhs.m_periodic);
152
153 return *this;
154}
155
Austin Schuh812d0d12021-11-04 20:16:48 -0700156void Notifier::SetName(std::string_view name) {
157 fmt::memory_buffer buf;
158 fmt::format_to(fmt::appender{buf}, "{}", name);
159 buf.push_back('\0'); // null terminate
Brian Silverman8fce7482020-01-05 13:18:21 -0800160 int32_t status = 0;
Austin Schuh812d0d12021-11-04 20:16:48 -0700161 HAL_SetNotifierName(m_notifier, buf.data(), &status);
Brian Silverman8fce7482020-01-05 13:18:21 -0800162}
163
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800164void Notifier::SetHandler(std::function<void()> callback) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800165 std::scoped_lock lock(m_processMutex);
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800166 m_callback = callback;
167}
168
169void Notifier::SetCallback(std::function<void()> callback) {
170 std::scoped_lock lock(m_processMutex);
171 m_callback = callback;
Brian Silverman8fce7482020-01-05 13:18:21 -0800172}
173
Brian Silverman8fce7482020-01-05 13:18:21 -0800174void Notifier::StartSingle(units::second_t delay) {
175 std::scoped_lock lock(m_processMutex);
176 m_periodic = false;
Austin Schuh812d0d12021-11-04 20:16:48 -0700177 m_period = delay;
Brian Silverman8fce7482020-01-05 13:18:21 -0800178 m_expirationTime = Timer::GetFPGATimestamp() + m_period;
179 UpdateAlarm();
180}
181
Brian Silverman8fce7482020-01-05 13:18:21 -0800182void Notifier::StartPeriodic(units::second_t period) {
183 std::scoped_lock lock(m_processMutex);
184 m_periodic = true;
Austin Schuh812d0d12021-11-04 20:16:48 -0700185 m_period = period;
Brian Silverman8fce7482020-01-05 13:18:21 -0800186 m_expirationTime = Timer::GetFPGATimestamp() + m_period;
187 UpdateAlarm();
188}
189
190void Notifier::Stop() {
Austin Schuh1e69f942020-11-14 15:06:14 -0800191 std::scoped_lock lock(m_processMutex);
192 m_periodic = false;
Brian Silverman8fce7482020-01-05 13:18:21 -0800193 int32_t status = 0;
194 HAL_CancelNotifierAlarm(m_notifier, &status);
James Kuszmaulcf324122023-01-14 14:07:17 -0800195 FRC_CheckErrorStatus(status, "CancelNotifierAlarm");
Brian Silverman8fce7482020-01-05 13:18:21 -0800196}
197
198void Notifier::UpdateAlarm(uint64_t triggerTime) {
199 int32_t status = 0;
200 // Return if we are being destructed, or were not created successfully
201 auto notifier = m_notifier.load();
Austin Schuh812d0d12021-11-04 20:16:48 -0700202 if (notifier == 0) {
203 return;
204 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800205 HAL_UpdateNotifierAlarm(notifier, triggerTime, &status);
James Kuszmaulcf324122023-01-14 14:07:17 -0800206 FRC_CheckErrorStatus(status, "UpdateNotifierAlarm");
Brian Silverman8fce7482020-01-05 13:18:21 -0800207}
208
209void Notifier::UpdateAlarm() {
210 UpdateAlarm(static_cast<uint64_t>(m_expirationTime * 1e6));
211}
Austin Schuh812d0d12021-11-04 20:16:48 -0700212
213bool Notifier::SetHALThreadPriority(bool realTime, int32_t priority) {
214 int32_t status = 0;
215 return HAL_SetNotifierThreadPriority(realTime, priority, &status);
216}