blob: d8377527b7852cb635ec473d9097c6c318740258 [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
20Notifier::Notifier(std::function<void()> handler) {
Austin Schuh812d0d12021-11-04 20:16:48 -070021 if (!handler) {
James Kuszmaulcf324122023-01-14 14:07:17 -080022 throw FRC_MakeError(err::NullParameter, "handler");
Austin Schuh812d0d12021-11-04 20:16:48 -070023 }
Brian Silverman8fce7482020-01-05 13:18:21 -080024 m_handler = handler;
25 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
41 std::function<void()> handler;
42 {
43 std::scoped_lock lock(m_processMutex);
44 handler = m_handler;
45 if (m_periodic) {
46 m_expirationTime += m_period;
47 UpdateAlarm();
48 } else {
49 // need to update the alarm to cause it to wait again
50 UpdateAlarm(UINT64_MAX);
51 }
52 }
53
54 // call callback
Austin Schuh812d0d12021-11-04 20:16:48 -070055 if (handler) {
56 handler();
57 }
Brian Silverman8fce7482020-01-05 13:18:21 -080058 }
59 });
60}
61
Austin Schuh1e69f942020-11-14 15:06:14 -080062Notifier::Notifier(int priority, std::function<void()> handler) {
Austin Schuh812d0d12021-11-04 20:16:48 -070063 if (!handler) {
James Kuszmaulcf324122023-01-14 14:07:17 -080064 throw FRC_MakeError(err::NullParameter, "handler");
Austin Schuh812d0d12021-11-04 20:16:48 -070065 }
Austin Schuh1e69f942020-11-14 15:06:14 -080066 m_handler = handler;
67 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
84 std::function<void()> handler;
85 {
86 std::scoped_lock lock(m_processMutex);
87 handler = m_handler;
88 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
Austin Schuh812d0d12021-11-04 20:16:48 -070098 if (handler) {
James Kuszmaulcf324122023-01-14 14:07:17 -080099 try {
100 handler();
101 } 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
126 // Join the thread to ensure the handler 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()),
137 m_handler(std::move(rhs.m_handler)),
138 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;
148 m_handler = std::move(rhs.m_handler);
149 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
164void Notifier::SetHandler(std::function<void()> handler) {
165 std::scoped_lock lock(m_processMutex);
166 m_handler = handler;
167}
168
Brian Silverman8fce7482020-01-05 13:18:21 -0800169void Notifier::StartSingle(units::second_t delay) {
170 std::scoped_lock lock(m_processMutex);
171 m_periodic = false;
Austin Schuh812d0d12021-11-04 20:16:48 -0700172 m_period = delay;
Brian Silverman8fce7482020-01-05 13:18:21 -0800173 m_expirationTime = Timer::GetFPGATimestamp() + m_period;
174 UpdateAlarm();
175}
176
Brian Silverman8fce7482020-01-05 13:18:21 -0800177void Notifier::StartPeriodic(units::second_t period) {
178 std::scoped_lock lock(m_processMutex);
179 m_periodic = true;
Austin Schuh812d0d12021-11-04 20:16:48 -0700180 m_period = period;
Brian Silverman8fce7482020-01-05 13:18:21 -0800181 m_expirationTime = Timer::GetFPGATimestamp() + m_period;
182 UpdateAlarm();
183}
184
185void Notifier::Stop() {
Austin Schuh1e69f942020-11-14 15:06:14 -0800186 std::scoped_lock lock(m_processMutex);
187 m_periodic = false;
Brian Silverman8fce7482020-01-05 13:18:21 -0800188 int32_t status = 0;
189 HAL_CancelNotifierAlarm(m_notifier, &status);
James Kuszmaulcf324122023-01-14 14:07:17 -0800190 FRC_CheckErrorStatus(status, "CancelNotifierAlarm");
Brian Silverman8fce7482020-01-05 13:18:21 -0800191}
192
193void Notifier::UpdateAlarm(uint64_t triggerTime) {
194 int32_t status = 0;
195 // Return if we are being destructed, or were not created successfully
196 auto notifier = m_notifier.load();
Austin Schuh812d0d12021-11-04 20:16:48 -0700197 if (notifier == 0) {
198 return;
199 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800200 HAL_UpdateNotifierAlarm(notifier, triggerTime, &status);
James Kuszmaulcf324122023-01-14 14:07:17 -0800201 FRC_CheckErrorStatus(status, "UpdateNotifierAlarm");
Brian Silverman8fce7482020-01-05 13:18:21 -0800202}
203
204void Notifier::UpdateAlarm() {
205 UpdateAlarm(static_cast<uint64_t>(m_expirationTime * 1e6));
206}
Austin Schuh812d0d12021-11-04 20:16:48 -0700207
208bool Notifier::SetHALThreadPriority(bool realTime, int32_t priority) {
209 int32_t status = 0;
210 return HAL_SetNotifierThreadPriority(realTime, priority, &status);
211}